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 { 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 btnreset = photoview.querySelector( " sl-icon-button[label='Start Over']", ) as SlIconButton; let initialized = false; async function clearCamera() { video.pause(); video.srcObject = null; video.classList.add("invisible"); video.parentNode?.querySelectorAll("canvas").forEach((e) => e.remove()); btnshutter.disabled = true; btnshutter.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: { facingMode: { ideal: "environment", }, width: { ideal: 1280 }, height: { ideal: 720 }, }, 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", undefined, undefined, null, ); window.location.href = "/transactions"; } 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"); canvas.setAttribute("id", "camera-photo"); 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); 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(); }); 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; });