// ── Receipt detail ──────────────────────────────────────────────────────────── function ReceiptDetail({ id }) { const [data, setData] = React.useState(null); const [error, setError] = React.useState(null); const [refreshKey, setRefreshKey] = React.useState(0); const [expandedItem, setExpandedItem] = React.useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false); const [deleteMatchItem, setDeleteMatchItem] = React.useState(null); const [scrollToItem, setScrollToItem] = React.useState(null); const [savingItem, setSavingItem] = React.useState(null); React.useEffect(() => { setData(null); fetch(`/api/receipts/${encodeURIComponent(id)}`) .then((r) => (r.ok ? r.json() : Promise.reject(r.status))) .then((d) => { setData(d); }) .catch((e) => setError(String(e))); }, [id, refreshKey]); React.useEffect(() => { if (!data || !scrollToItem) return; const el = document.getElementById(`item-${CSS.escape(scrollToItem)}`); if (el) el.scrollIntoView({ behavior: "smooth", block: "center" }); setScrollToItem(null); }, [data, scrollToItem]); async function confirmDeleteReceipt() { const res = await fetch(`/api/receipts/${encodeURIComponent(id)}`, { method: "DELETE", }); if (res.ok) { location.hash = "#/receipts"; } } async function confirmDeleteMatch() { await fetch( `/api/match/${encodeURIComponent(data.chain)}/${encodeURIComponent(deleteMatchItem)}`, { method: "DELETE" }, ); setDeleteMatchItem(null); setRefreshKey((k) => k + 1); } async function handleMatchSaved(itemName) { try { const d = await fetch(`/api/receipts/${encodeURIComponent(id)}`).then( (r) => r.json(), ); setData((prev) => ({ ...prev, items: d.items })); setScrollToItem(itemName); } catch (e) { setRefreshKey((k) => k + 1); } await new Promise((r) => setTimeout(r, 300)); setSavingItem(null); } function deleteMatch(itemName) { setDeleteMatchItem(itemName); } const colors = useChainColors(); if (error) return
Errore: {error}
; if (!data) return (Caricamento scontrino…
); const { r, chain, items, mongo_ok, brave_ok, source } = data; const store = r.store_address || r.store_name || "Sconosciuto"; const { score_avg: scoreAvg, scored_count: scoredCount } = r; const scoreMin = scoreAvg != null ? Math.min( ...items .map((i) => i.match?.mediterranean_score?.score) .filter((s) => s != null), ) : null; return (