From e8769e4d5dfa5771a267bd51748ae76f2dc99f64 Mon Sep 17 00:00:00 2001 From: justin Date: Sat, 30 May 2026 15:56:00 -0500 Subject: [PATCH] Photo ID upload: add QR code for phone + scanner device support Three upload methods: - Upload File: standard file picker - Camera / Scanner: uses capture attribute for camera on mobile or TWAIN/WIA scanner devices on desktop - QR Code: generates QR with current page URL so user can scan with phone and take a photo of their ID on mobile QR generated via api.qrserver.com (no library dependency). Remove button restores all upload options. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../intake/steps/DOTIntakeStep.astro | 94 +++++++++++++++++-- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/site/src/components/intake/steps/DOTIntakeStep.astro b/site/src/components/intake/steps/DOTIntakeStep.astro index dd1f71d..8668646 100644 --- a/site/src/components/intake/steps/DOTIntakeStep.astro +++ b/site/src/components/intake/steps/DOTIntakeStep.astro @@ -214,10 +214,34 @@ - +
+
+ + +
+ +
+
+
+ or use your phone +
+
+ +
+ +
@@ -402,24 +426,74 @@ const idPreview = document.getElementById("dot-id-preview"); const idImg = document.getElementById("dot-id-img"); const idRemove = document.getElementById("dot-id-remove"); - idBtn?.addEventListener("click", () => idInput?.click()); - idInput?.addEventListener("change", () => { - const file = idInput.files?.[0]; + const idQrBtn = document.getElementById("dot-id-qr-btn"); + const idQrContainer = document.getElementById("dot-id-qr-container"); + const idUploadOpts = document.getElementById("dot-id-upload-options"); + + // Upload button — opens file picker + idBtn?.addEventListener("click", function() { if (idInput) idInput.click(); }); + + // Scanner/camera button — uses capture attribute to trigger camera or scanner + var idScanBtn = document.getElementById("dot-id-scan-btn"); + var idScanInput = document.getElementById("dot-photo-id-scan"); + idScanBtn?.addEventListener("click", function() { if (idScanInput) idScanInput.click(); }); + idScanInput?.addEventListener("change", function() { + var file = idScanInput.files?.[0]; if (!file) return; - (window).__dotPhotoId = file; + // Copy to main input handler + window.__dotPhotoId = file; + if (file.type.startsWith("image/") && idImg) { + var reader = new FileReader(); + reader.onload = function(e) { idImg.src = e.target?.result; }; + reader.readAsDataURL(file); + } + if (idPreview) idPreview.hidden = false; + if (idUploadOpts) idUploadOpts.style.display = "none"; + }); + + // QR code button — show QR with current page URL for phone upload + idQrBtn?.addEventListener("click", function() { + if (!idQrContainer) return; + var showing = !idQrContainer.hidden; + idQrContainer.hidden = showing; + if (!showing) { + // Generate QR code using canvas + var canvas = document.getElementById("dot-id-qr-canvas"); + if (canvas && !canvas.dataset.rendered) { + var url = window.location.href; + // Use a simple QR code API (Google Charts - no library needed) + var img = new Image(); + img.crossOrigin = "anonymous"; + img.onload = function() { + canvas.width = 200; + canvas.height = 200; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, 200, 200); + canvas.dataset.rendered = "1"; + }; + img.src = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" + encodeURIComponent(url); + } + } + }); + + idInput?.addEventListener("change", function() { + var file = idInput.files?.[0]; + if (!file) return; + window.__dotPhotoId = file; if (file.type.startsWith("image/")) { const reader = new FileReader(); reader.onload = (e) => { idImg.src = e.target?.result; }; reader.readAsDataURL(file); } if (idPreview) idPreview.hidden = false; - if (idBtn) idBtn.style.display = "none"; + if (idUploadOpts) idUploadOpts.style.display = "none"; }); idRemove?.addEventListener("click", () => { (window).__dotPhotoId = null; if (idInput) idInput.value = ""; + if (idScanInput) idScanInput.value = ""; if (idPreview) idPreview.hidden = true; - if (idBtn) idBtn.style.display = "flex"; + if (idUploadOpts) idUploadOpts.style.display = ""; }); } // end guard