import * as React from 'react'; import classNames from 'classnames'; interface QRScannerProps { onScan: (result: string) => void; onError?: (error: Error) => void; onClose?: () => void; className?: string; } export function QRScanner({ onScan, onError, onClose, className }: QRScannerProps) { const videoRef = React.useRef(null); const canvasRef = React.useRef(null); const [isScanning, setIsScanning] = React.useState(false); const [hasCamera, setHasCamera] = React.useState(true); const [cameraError, setCameraError] = React.useState(null); const streamRef = React.useRef(null); const startCamera = React.useCallback(async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', width: { ideal: 1280 }, height: { ideal: 720 }, }, }); streamRef.current = stream; if (videoRef.current) { videoRef.current.srcObject = stream; await videoRef.current.play(); setIsScanning(true); } } catch (err) { const error = err as Error; setHasCamera(false); setCameraError(error.message); onError?.(error); } }, [onError]); const stopCamera = React.useCallback(() => { if (streamRef.current) { streamRef.current.getTracks().forEach((track) => track.stop()); streamRef.current = null; } setIsScanning(false); }, []); React.useEffect(() => { startCamera(); return () => stopCamera(); }, [startCamera, stopCamera]); // Simple QR detection simulation (in production, use a library like jsQR) React.useEffect(() => { if (!isScanning) return; const scanInterval = setInterval(() => { if (videoRef.current && canvasRef.current) { const canvas = canvasRef.current; const video = videoRef.current; const ctx = canvas.getContext('2d'); if (ctx && video.readyState === video.HAVE_ENOUGH_DATA) { canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // In production, use jsQR library here: // const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // const code = jsQR(imageData.data, imageData.width, imageData.height); // if (code) { // stopCamera(); // onScan(code.data); // } } } }, 100); return () => clearInterval(scanInterval); }, [isScanning, onScan, stopCamera]); // Demo function to simulate a scan const simulateScan = () => { stopCamera(); onScan('plant:abc123-tomato-heirloom'); }; if (!hasCamera) { return (

Camera access denied

{cameraError}

); } return (
{/* Video feed */}