From 837caecc3abf3268e17b2620eef133dc533c1ddd Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sat, 8 Mar 2025 11:11:42 -0600 Subject: [PATCH] js: Initial UI implementation I've implemented the UI using TypeScript and Shoelace. I originally started with Pico CSS, but I didn't really like its visuals. Since capturing photos using the camera requires JavaScript, and that's basically the entire point of this application, Shoelace's JavaScript dependency (for WebComponents), is acceptable. The photo capture uses the Media Capture Web API, which exposes the camera directly as a video stream. We capture a frame from this stream and save it in a canvas, which we then pass to Cropper.js to let the user select only the relevant portion of the picture containing the receipt itself. --- js/.gitignore | 2 + js/alert.ts | 28 ++ js/build.js | 57 +++ js/common.css | 58 +++ js/common.ts | 20 + js/icons/apple-touch-icon.png | Bin 0 -> 2555 bytes js/icons/favicon.ico | Bin 0 -> 5238 bytes js/icons/icon-192-maskable.png | Bin 0 -> 3075 bytes js/icons/icon-192.png | Bin 0 -> 5715 bytes js/icons/icon-512-maskable.png | Bin 0 -> 9447 bytes js/icons/icon-512.png | Bin 0 -> 17116 bytes js/package-lock.json | 779 +++++++++++++++++++++++++++++++++ js/package.json | 14 + js/transaction.css | 17 + js/transaction.ts | 199 +++++++++ 15 files changed, 1174 insertions(+) create mode 100644 js/.gitignore create mode 100644 js/alert.ts create mode 100644 js/build.js create mode 100644 js/common.css create mode 100644 js/common.ts create mode 100644 js/icons/apple-touch-icon.png create mode 100644 js/icons/favicon.ico create mode 100644 js/icons/icon-192-maskable.png create mode 100644 js/icons/icon-192.png create mode 100644 js/icons/icon-512-maskable.png create mode 100644 js/icons/icon-512.png create mode 100644 js/package-lock.json create mode 100644 js/package.json create mode 100644 js/transaction.css create mode 100644 js/transaction.ts diff --git a/js/.gitignore b/js/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/js/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/js/alert.ts b/js/alert.ts new file mode 100644 index 0000000..bb1855b --- /dev/null +++ b/js/alert.ts @@ -0,0 +1,28 @@ +import SlAlert from "@shoelace-style/shoelace/dist/components/alert/alert.js"; +import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.js"; + +type AlertVariant = "primary" | "success" | "neutral" | "warning" | "danger"; + +export function notify( + message: string, + variant: AlertVariant = "primary", + iconName = "info-circle", + duration: number | null = 3000, +) { + const alert = new SlAlert(); + const icon = new SlIcon(); + icon.slot = "icon"; + icon.name = iconName; + alert.variant = variant; + alert.open = true; + alert.style.position = "relative"; + alert.closable = true; + if (duration) { + alert.duration = duration; + } + alert.appendChild(icon); + alert.appendChild(document.createTextNode(message)); + document.body.append(alert); + alert.toast(); +} + diff --git a/js/build.js b/js/build.js new file mode 100644 index 0000000..aa4742f --- /dev/null +++ b/js/build.js @@ -0,0 +1,57 @@ +import * as esbuild from "esbuild"; +import * as path from "path"; +import * as fs from "fs"; +import * as url from "url"; +const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); + +const context = await esbuild.context({ + entryPoints: [ + "*.ts", + "icons/**/*", + ], + outdir: "./dist", + bundle: true, + platform: "node", + target: "esnext", + format: "esm", + loader: { + ".png": "copy", + ".ico": "copy", + }, + plugins: [ + { + name: "copy-shoelace-assets", + setup(build) { + let hasCopied = false; + build.onStart(() => { + if (!hasCopied) { + // We only copy one time, these files shouldn't be changing often. + const shoelaceAssets = path.resolve( + __dirname, + "node_modules/@shoelace-style/shoelace/dist/assets", + ); + const outputDir = path.resolve( + __dirname, + "dist/shoelace/assets", + ); + + fs.rmSync(outputDir, { force: true, recursive: true }); + fs.cpSync(shoelaceAssets, outputDir, { + recursive: true, + }); + hasCopied = true; + } + }); + }, + }, + ], +}); + +await context.rebuild(); + +if (process.argv.includes("--watch")) { + await context.watch(); + console.log("watching for file changes..."); +} else { + await context.dispose(); +} diff --git a/js/common.css b/js/common.css new file mode 100644 index 0000000..8765e5d --- /dev/null +++ b/js/common.css @@ -0,0 +1,58 @@ +:host, body { + font-family: var(--sl-font-sans); +} + +body:has(div#page-loading) main { + opacity: 0; +} + +main { + transition: 1s opacity; +} + +#page-loading { + animation: 2s linear infinite pulse; + align-items: center; + bottom: 0; + display: flex; + font-size: 24pt; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +@keyframes pulse { + 0% { opacity: 0; } + 50% { opacity: 1; } + 100% { opacity: 0; } +} + +@media screen and (min-width: 900px) { + body { + margin: auto; + max-width: 1000px; + } +} + +form footer { + text-align: right; +} + +form footer sl-button { + margin: 0.5em 0.25em; +} + +table { + width: 100%; + border-collapse: collapse; +} + +tr { + border-bottom: 1px solid var(--sl-color-neutral-200); +} + +table td, table th { + padding: 1rem; +} diff --git a/js/common.ts b/js/common.ts new file mode 100644 index 0000000..7f01faf --- /dev/null +++ b/js/common.ts @@ -0,0 +1,20 @@ +import "@shoelace-style/shoelace/dist/themes/light.css"; +import "@shoelace-style/shoelace/dist/themes/dark.css"; + +import "./common.css"; + +const mql = window.matchMedia("(prefers-color-scheme: dark"); +const setDarkMode = () => { + if (mql.matches) { + document.documentElement.classList.add("sl-theme-dark"); + } else { + document.documentElement.classList.remove("sl-theme-dark"); + } +}; +mql.addEventListener("change", setDarkMode); +setDarkMode(); + +document.addEventListener("DOMContentLoaded", () => { + const elm = document.getElementById("page-loading"); + if (elm) elm.remove(); +}); diff --git a/js/icons/apple-touch-icon.png b/js/icons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea8af9cf394bc433f8e80de490477dd7258260cc GIT binary patch literal 2555 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 lo!HZy%0BM;;MC6{K1y(vc>} zQj{W6B)bt&ZEb8a&xE2F~5hp^{k?e6{Yyh#M>_=jxdakAbJD(v|$BCTe=$`!h!@E1KD3$3$X z7c#HYI&fjF8CvYo)pT}XdwR+WCWnNeTWMp!Jw61;2?cmnG>AmPKo=fh4xoS`9HNsk z0L}luY#y?s%M%JZdy6j=WoI*fhO=0#?DBG@0~%C}?`VBWRCKiBGP9#2e!4B~iOY+? zWf!c1LPdRifqB~d=a=8j_LVpWd>%ft@%7!tp)AL09~0B0~oaKRyo zE-^A#<=wkk>e7%}?Kei56RTPA8?L!8a>B{D%%vfRi&vy`eRW20DK}*ETczKa?g8aX z2%R7n-J>JZJaBomsxtmn$fk{hL#k$yL4<()jS?-413g9MijAF}hckec`S5{{%Qg3+tEBBrmA;=#eI@ebs zk#WZVj%L6tzQUs|XKgKjQ06c9nwP=o!|iTqXRU*9ioI z?Gb+2oZT@_a>kNA+ug@XbxGSUcB3Iv{4RLEnoZ|5G-gST5-&AzbN zWh)PV?r4jR%8gZ-?#Qy#y0*x%oD5_LA3l7zGgBux{qyZy;dgIO^}bF%NjfGgt8W{Y zI^LRf|GwTKqqu+#X}_^o`O5Ci8i{m?ou+6}+wcGYspi&#)*`}_W4gMTE{(UGr@E=f z$3wk}?Ml415}fwbaFZ%L%oEQu8gz`(eTS;Ip0tz*EW6LW&&SHzN{jh-bZk!X*fjK) zw$}wN_kgrXlgopZo(A&r@~WQi4>L*83Q+u>r!5ELbkf(Qba05ygj9rUsnbjSuCA`e zBjcG_G-_`1GF<^uccHwFQ-(~dZf)J*L7|ks`X(kMbUw8$Cc0nNSgp`Le!n8K@#vl! zNDB+8Z^O9-3ZXLlATzYM_Q2cY!(KSxaGmX^}8h@*|t{?9$mpsLWnNU}bYvRlWtQqC>kHa1{;ze}wp~f9BbPqh}he4`vSd9oOTigyY zu2Or`rs%}N($!xqhgYJkE96)hy2nK!da_x|>(`y^kH;OWde)qtA2mh>;SOiTK*#9UQ`$;15pHP>B?kp_R##V*QAyzn2!`sc zjMeTFeTSka5HJV>kiT8zdxGaeqyEflH%^dbAaT2i+UzP~tbz{xn;1RBDWuR}wR!Q- zMZP41A;}9wpaq9~1Q3N%C|7?BKrqCBJ9v@}8;~S|zeq=^Ao~##xA(fGhN8m%%|G~L z?liN6>dA`#WO1 z>STTD!<|7P91a&16QjD-g`q1!QBhHa1`z`DWm0?i`0@m0bRTV#(Ev8UgR;W;H131# z&&KY4rE1vz>|5C|vH+q`#(_sEWFz3brKOyppzim<^|iUZTdN&7qLr0vWl_?_{n;U# z-x9L2{5bZzCbZ1b&G%?a6{ozr@Mwi+gc%_yL0O=X2 zhC(T=3t^x5?J00`b4y4}yv!MG2xb>7^p_DPn`8N3D4sldAvrlyZ{cYF~-`ygVxtlRau)J&^_GY3EE#OrF9n9#m7ay?R13rv!v- ztXX#zoM3ZrtV~86kW71ti>qTcRHmdHxWX!NId5wEW^630zM(;p=H%kuWNp&IVUiWV z4$CNBx~dY*S0kMno2Kl3r#jv1o8P~AC|4!T>I+{&P|=c-zgn>4rFBwk1LpJ<9V+o@)j)m2C|mw8bkiCCuWe^x4u83 z4A(sNxUMl@j=HAd{+Z8_7tn?N{{!rQ49)AJ=!M!ytS5K@001UuEeuQbUBmtf$(b|c literal 0 HcmV?d00001 diff --git a/js/icons/icon-192.png b/js/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..512f1ee7b48e998c9ff61e5f66ac78500f5508b0 GIT binary patch literal 5715 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 vt*vd1F`wC;a$~x8Gmm+$B6#{lR7X( literal 0 HcmV?d00001 diff --git a/js/icons/icon-512-maskable.png b/js/icons/icon-512-maskable.png new file mode 100644 index 0000000000000000000000000000000000000000..c5955a6b704ddb500b8a8b298e472228d5fb7a19 GIT binary patch literal 9447 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 literal 0 HcmV?d00001 diff --git a/js/package-lock.json b/js/package-lock.json new file mode 100644 index 0000000..701ce25 --- /dev/null +++ b/js/package-lock.json @@ -0,0 +1,779 @@ +{ + "name": "receipts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "receipts", + "dependencies": { + "@shoelace-style/shoelace": "^2.20.0", + "cropperjs": "^2.0.0" + }, + "devDependencies": { + "esbuild": "^0.25.0" + } + }, + "node_modules/@cropper/element": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz", + "integrity": "sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==", + "license": "MIT", + "dependencies": { + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-canvas": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz", + "integrity": "sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-crosshair": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.0.0.tgz", + "integrity": "sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-grid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.0.0.tgz", + "integrity": "sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-handle": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.0.0.tgz", + "integrity": "sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-image": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.0.0.tgz", + "integrity": "sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/element-canvas": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.0.0.tgz", + "integrity": "sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/element-canvas": "^2.0.0", + "@cropper/element-image": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-shade": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.0.0.tgz", + "integrity": "sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/element-canvas": "^2.0.0", + "@cropper/element-selection": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/element-viewer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.0.0.tgz", + "integrity": "sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/element-canvas": "^2.0.0", + "@cropper/element-image": "^2.0.0", + "@cropper/element-selection": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/@cropper/elements": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.0.0.tgz", + "integrity": "sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==", + "license": "MIT", + "dependencies": { + "@cropper/element": "^2.0.0", + "@cropper/element-canvas": "^2.0.0", + "@cropper/element-crosshair": "^2.0.0", + "@cropper/element-grid": "^2.0.0", + "@cropper/element-handle": "^2.0.0", + "@cropper/element-image": "^2.0.0", + "@cropper/element-selection": "^2.0.0", + "@cropper/element-shade": "^2.0.0", + "@cropper/element-viewer": "^2.0.0" + } + }, + "node_modules/@cropper/utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.0.0.tgz", + "integrity": "sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==", + "license": "MIT" + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", + "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", + "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/react": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.7.tgz", + "integrity": "sha512-cencnwwLXQKiKxjfFzSgZRngcWJzUDZi/04E0fSaF86wZgchMdvTyu+lE36DrUfvuus3bH8+xLPrhM1cTjwpzw==", + "license": "BSD-3-Clause", + "peerDependencies": { + "@types/react": "17 || 18 || 19" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, + "node_modules/@shoelace-style/animations": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@shoelace-style/animations/-/animations-1.2.0.tgz", + "integrity": "sha512-avvo1xxkLbv2dgtabdewBbqcJfV0e0zCwFqkPMnHFGbJbBHorRFfMAHh1NG9ymmXn0jW95ibUVH03E1NYXD6Gw==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/claviska" + } + }, + "node_modules/@shoelace-style/localize": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.2.1.tgz", + "integrity": "sha512-r4C9C/5kSfMBIr0D9imvpRdCNXtUNgyYThc4YlS6K5Hchv1UyxNQ9mxwj+BTRH2i1Neits260sR3OjKMnplsFA==", + "license": "MIT" + }, + "node_modules/@shoelace-style/shoelace": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.20.0.tgz", + "integrity": "sha512-Qq/kPtWC//HVyHX6EZ/i5y9zTMORjqV4lrxU2sbHLh+qdc9DlroYVSSqa2eqkmSzeLO+gHPZrjYmxDTja85iAA==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.1.0", + "@floating-ui/dom": "^1.6.12", + "@lit/react": "^1.0.6", + "@shoelace-style/animations": "^1.2.0", + "@shoelace-style/localize": "^3.2.1", + "composed-offset-position": "^0.0.6", + "lit": "^3.2.1", + "qr-creator": "^1.0.0" + }, + "engines": { + "node": ">=14.17.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/claviska" + } + }, + "node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/composed-offset-position": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.6.tgz", + "integrity": "sha512-Q7dLompI6lUwd7LWyIcP66r4WcS9u7AL2h8HaeipiRfCRPLMWqRx8fYsjb4OHi6UQFifO7XtNC2IlEJ1ozIFxw==", + "license": "MIT", + "peerDependencies": { + "@floating-ui/utils": "^0.2.5" + } + }, + "node_modules/cropperjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz", + "integrity": "sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==", + "license": "MIT", + "dependencies": { + "@cropper/elements": "^2.0.0", + "@cropper/utils": "^2.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT", + "peer": true + }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/lit": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz", + "integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.1.0", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-element": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz", + "integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-html": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz", + "integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/qr-creator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/qr-creator/-/qr-creator-1.0.0.tgz", + "integrity": "sha512-C0cqfbS1P5hfqN4NhsYsUXePlk9BO+a45bAQ3xLYjBL3bOIFzoVEjs79Fado9u9BPBD3buHi3+vY+C8tHh4qMQ==", + "license": "MIT" + } + } +} diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..ef8c3ed --- /dev/null +++ b/js/package.json @@ -0,0 +1,14 @@ +{ + "name": "receipts", + "type": "module", + "dependencies": { + "@shoelace-style/shoelace": "^2.20.0", + "cropperjs": "^2.0.0" + }, + "devDependencies": { + "esbuild": "^0.25.0" + }, + "scripts": { + "build": "node build.js" + } +} diff --git a/js/transaction.css b/js/transaction.css new file mode 100644 index 0000000..84f20b9 --- /dev/null +++ b/js/transaction.css @@ -0,0 +1,17 @@ +.invisible { + display: none !important; +} + +#photo-view { + display: flex; +} + +#photo-view .workspace { + flex-grow: 1; + text-align: center; +} + +#photo-view sl-icon-button { + font-size: 3em; + margin: 0 0.5em; +} diff --git a/js/transaction.ts b/js/transaction.ts new file mode 100644 index 0000000..ef09ed7 --- /dev/null +++ b/js/transaction.ts @@ -0,0 +1,199 @@ +import "./transaction.css"; +import "@shoelace-style/shoelace/dist/components/button/button.js"; +import "@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js"; +import "@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js"; +import "@shoelace-style/shoelace/dist/components/details/details.js"; +import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"; +import "@shoelace-style/shoelace/dist/components/input/input.js"; +import "@shoelace-style/shoelace/dist/components/textarea/textarea.js"; +import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js"; + +import SlButton from "@shoelace-style/shoelace/dist/components/button/button.js"; +import SlDetails from "@shoelace-style/shoelace/dist/components/details/details.js"; +import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"; +import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js"; +import Cropper from "cropperjs"; + +import { notify } from "./alert"; + +setBasePath("/static/shoelace/"); + +const form = document.forms[0]; +const photobox = document.getElementById("photo-box")!; +const photoview = document.getElementById("photo-view")!; +const video = photoview.querySelector("video")!; +const btnshutter = photoview.querySelector( + " sl-icon-button[label='Take Photo']", +) as SlIconButton; +const btncrop = photoview.querySelector( + " sl-icon-button[label='Crop']", +) as SlIconButton; +const btnreset = photoview.querySelector( + " sl-icon-button[label='Start Over']", +) as SlIconButton; + +let cropper: Cropper | null = null; +let initialized = false; + +async function clearCamera() { + if (cropper) { + cropper.getCropperCanvas()?.remove(); + cropper = null; + } + video.pause(); + video.srcObject = null; + video.classList.add("invisible"); + video.parentNode?.querySelectorAll("canvas").forEach((e) => e.remove()); + btnshutter.disabled = true; + btnshutter.classList.add("invisible"); + btncrop.disabled = true; + btncrop.classList.add("invisible"); + btnreset.disabled = true; + btnreset.classList.add("invisible"); + photoview.classList.remove("invisible"); +} + +async function startCamera() { + let stream: MediaStream; + try { + stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: false, + }); + } catch (ex) { + console.error(ex); + notify(`${ex}`, "danger", "exclamation-octagon", null); + return; + } + photobox.querySelectorAll(".fallback").forEach((e) => e.remove()); + btnshutter.classList.remove("invisible"); + video.classList.remove("invisible"); + video.srcObject = stream; + video.play(); +} + +async function submitForm(data: FormData) { + const btn = form.querySelector("sl-button[type='submit']") as SlButton; + btn.loading = true; + let r: Response | null = null; + try { + r = await fetch("", { + method: "POST", + body: data, + }); + } catch (e) { + notify( + `Failed to submit form: ${e}`, + "danger", + "exclamation-octagon", + null, + ); + } + btn.loading = false; + if (r) { + if (r.ok) { + notify("Successfully updated transaction"); + } else { + const html = await r.text(); + if (html) { + const doc = new DOMParser().parseFromString(html, "text/html"); + notify( + doc.body.textContent ?? "", + "danger", + "exclamation-octagon", + null, + ); + } else { + notify(r.statusText, "danger", "exclamation-octagon", null); + } + } + } +} + +function takePhoto() { + btnshutter.disabled = true; + btnshutter.classList.add("invisible"); + video.pause(); + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + if (!context) { + notify( + "Failed to get canvas 2D rendering context", + "danger", + "exclamation-octagon", + null, + ); + return; + } + const width = video.videoWidth; + const height = video.videoHeight; + canvas.width = width; + canvas.height = height; + context.drawImage(video, 0, 0, width, height); + video.srcObject = null; + video.classList.add("invisible"); + video.parentNode!.appendChild(canvas); + cropper = new Cropper(canvas); + cropper.getCropperCanvas()!.style.height = `${height}px`; + btncrop.disabled = false; + btncrop.classList.remove("invisible"); + btnreset.disabled = false; + btnreset.classList.remove("invisible"); +} + +photobox.addEventListener("sl-show", () => { + if (!initialized) { + initialized = true; + clearCamera(); + startCamera(); + } +}); + +video.addEventListener("canplay", () => { + btnshutter.disabled = false; +}); + +btnshutter.addEventListener("click", async () => { + takePhoto(); +}); + +btncrop.addEventListener("click", async () => { + if (cropper) { + const canvas = await cropper.getCropperSelection()?.$toCanvas(); + if (canvas) { + canvas.setAttribute("id", "camera-photo"); + video.parentNode!.appendChild(canvas); + cropper.getCropperCanvas()?.remove(); + btncrop.disabled = true; + btncrop.classList.add("invisible"); + cropper = null; + } + } +}); + +btnreset.addEventListener("click", () => { + clearCamera(); + startCamera(); +}); + +form.addEventListener("submit", async (evt) => { + evt.preventDefault(); + let data = new FormData(form); + const canvas = document.getElementById("camera-photo") as HTMLCanvasElement; + if (canvas) { + canvas.toBlob((blob: Blob | null) => { + if (blob) { + data.append("photo", blob, "photo.jpg"); + } + submitForm(data); + }, "image/jpeg"); + } else { + submitForm(data); + } +}); + +form.addEventListener("reset", () => { + document.querySelectorAll("sl-details").forEach((e: SlDetails) => e.hide()); + clearCamera(); + initialized = false; +});