'use client'; import { type ReactNode, useEffect, useRef, useState } from 'react'; import jsQR from 'jsqr'; import { Camera, ScanLine } from 'lucide-react'; /** Starts on mount: the clock IS the scanner, so there is nothing to open first. */ export function BadgeCamera({onDecode,paused,overlay,hint='Hold the badge QR inside the frame',compact=false}:{onDecode:(code:string)=>void;paused:boolean;overlay?:ReactNode;hint?:string;compact?:boolean}) { const video=useRef(null); const callback=useRef(onDecode);callback.current=onDecode; const pause=useRef(paused);pause.current=paused; const [enabled,setEnabled]=useState(true); const [live,setLive]=useState(false); const [error,setError]=useState(''); useEffect(()=>{ if(!enabled)return; let stopped=false;let stream:MediaStream|null=null;let timer:ReturnType; const canvas=document.createElement('canvas'); const context=canvas.getContext('2d',{willReadFrequently:true}); const tick=()=>{ if(stopped)return; const frame=video.current; if(!pause.current && context && frame && frame.readyState>=2 && frame.videoWidth) { const scale=Math.min(1,720/frame.videoWidth); canvas.width=Math.round(frame.videoWidth*scale);canvas.height=Math.round(frame.videoHeight*scale); context.drawImage(frame,0,0,canvas.width,canvas.height); const pixels=context.getImageData(0,0,canvas.width,canvas.height); const code=jsQR(pixels.data,pixels.width,pixels.height,{inversionAttempts:'attemptBoth'}); if(code?.data)callback.current(code.data); } timer=setTimeout(tick,180); }; async function start() { try { if(!navigator.mediaDevices?.getUserMedia)throw new Error('Camera needs HTTPS or localhost. Type the employee code below instead.'); stream=await navigator.mediaDevices.getUserMedia({video:{facingMode:{ideal:'environment'}},audio:false}); if(stopped){stream.getTracks().forEach(track=>track.stop());return;} if(video.current){video.current.srcObject=stream;await video.current.play();} setLive(true);tick(); }catch(e){ stream?.getTracks().forEach(track=>track.stop()); if(!stopped){setError(e instanceof Error && e.message.startsWith('Camera needs')?e.message:'Camera unavailable or permission denied. Allow camera access and tap Start camera, or type the employee code below.');setEnabled(false);} } } void start(); return ()=>{stopped=true;setLive(false);clearTimeout(timer);stream?.getTracks().forEach(track=>track.stop());}; },[enabled]); return
{enabled &&
{error &&

{error}

}
; }