"use client"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Dialog, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPopup, DialogTitle, } from "@/components/ui/dialog"; // Camera barcode/QR scanner in a dialog. Decoding uses @zxing/browser // (lazy-loaded so it stays out of the initial bundle), which reads 1D barcodes // (EAN/UPC/Code128) and 2D codes (QR, GS1 DataMatrix, PDF417) — the mix found // on medication packaging and patient wallet codes. Emits the decoded string // once, then the caller closes the dialog. export function BarcodeScanner({ open, onOpenChange, onDetected, title, description, }: { open: boolean; onOpenChange: (open: boolean) => void; onDetected: (value: string) => void; title?: string; description?: string; }) { const { t } = useTranslation(); const videoRef = useRef(null); // Keep the latest onDetected without restarting the camera on every render. const onDetectedRef = useRef(onDetected); onDetectedRef.current = onDetected; const [error, setError] = useState(null); useEffect(() => { if (!open) return; let stopped = false; let controls: { stop: () => void } | null = null; setError(null); (async () => { const video = videoRef.current; if (!video) return; try { const { BrowserMultiFormatReader } = await import("@zxing/browser"); const reader = new BrowserMultiFormatReader(); controls = await reader.decodeFromConstraints( { video: { facingMode: "environment" } }, video, (result) => { if (result && !stopped) { stopped = true; controls?.stop(); onDetectedRef.current(result.getText()); } }, ); // The dialog may have closed while getUserMedia was resolving. if (stopped) controls.stop(); } catch (err) { setError( err instanceof DOMException && err.name === "NotAllowedError" ? t("scan.permissionDenied") : t("scan.unavailable"), ); } })(); return () => { stopped = true; controls?.stop(); }; }, [open, t]); return ( {title ?? t("scan.title")} {description ?? t("scan.description")} {error ? (

{error}

) : (
{/* biome-ignore lint/a11y/useMediaCaption: live camera preview */}
); }