function ItemDetailsPage({ initialBarcode = "" }) { const [input, setInput] = React.useState(initialBarcode); const [barcode, setBarcode] = React.useState(initialBarcode); const [product, setProduct] = React.useState(null); const [similar, setSimilar] = React.useState([]); const [loading, setLoading] = React.useState(false); const [loadingSimilar, setLoadingSimilar] = React.useState(false); const [error, setError] = React.useState(null); const [scanning, setScanning] = React.useState(false); const [debugProduct, setDebugProduct] = React.useState(null); const videoRef = React.useRef(null); const readerRef = React.useRef(null); React.useEffect(() => { if (debugProduct) { document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = ""; }; } }, [debugProduct]); function openDebug() { setDebugProduct(product._raw || product); } // Fetch product whenever barcode changes React.useEffect(() => { if (!barcode) return; window.location.hash = `#/item/${barcode}`; setProduct(null); setSimilar([]); setError(null); setLoading(true); fetch(`/api/search-off?barcode=${encodeURIComponent(barcode)}`) .then((r) => r.json()) .then((d) => { const hit = d.results?.[0]; if (hit) { setProduct({ ...hit, off_found: true, _raw: d.raw || null }); } else { setError("Prodotto non trovato su OpenFoodFacts."); } }) .catch(() => setError("Errore di rete.")) .finally(() => setLoading(false)); }, [barcode]); // Fetch similar products after product loads React.useEffect(() => { const bc = product?.barcode; if (!bc) return; setLoadingSimilar(true); fetch(`/api/similar/${encodeURIComponent(bc)}`) .then((r) => r.json()) .then((d) => setSimilar(d.results || [])) .catch(() => {}) .finally(() => setLoadingSimilar(false)); }, [product?.barcode]); // Start/stop camera with ZXing React.useEffect(() => { if (!scanning) return; if (!videoRef.current) return; const reader = new ZXing.BrowserMultiFormatReader(); readerRef.current = reader; reader .decodeFromVideoDevice(undefined, videoRef.current, (result) => { if (!result) return; const text = result.getText(); reader.reset(); readerRef.current = null; setScanning(false); setInput(text); setBarcode(text); }) .catch(() => setScanning(false)); return () => { reader.reset(); readerRef.current = null; }; }, [scanning]); function stopScan() { readerRef.current?.reset(); readerRef.current = null; setScanning(false); } function handleSubmit(e) { e.preventDefault(); const cleaned = input.trim(); if (cleaned) setBarcode(cleaned); } function loadProduct(p) { setInput(p.barcode); setBarcode(p.barcode); window.scrollTo({ top: 0, behavior: "smooth" }); } return (