// ── Stats Page ────────────────────────────────────────────────────────────────
function MiniBar({ value, max, color = "#2d7a3a" }) {
const pct = max > 0 ? (value / max) * 100 : 0;
return (
{entries.map(([key, { label, unit, color }]) => {
const val = macros[key];
const isEnergy = key === "energy_kcal";
return (
{label}
{!isEnergy && (
)}
{val} {unit}
);
})}
Media per 100g su tutti i prodotti abbinati
);
}
function StatsPage() {
const [data, setData] = React.useState(null);
const [error, setError] = React.useState(null);
const [range, setRange] = React.useState("6m");
React.useEffect(() => {
setData(null);
fetch(`/api/stats?date_range=${range}`)
.then((r) => r.json())
.then(setData)
.catch((e) => setError(e.message));
}, [range]);
if (error)
return (
{/* Header + date range selector */}
Statistiche
{/* Overview cards */}
Scontrino
{[
["Min", overview.min_basket_eur],
["Med", overview.avg_basket_eur],
["Max", overview.max_basket_eur],
].map(([label, val]) => (
{label}
€{val.toFixed(2)}
))}
{/* Monthly spending */}
{Object.keys(monthly).length > 0 && (
{Object.entries(monthly).map(([month, total]) => (
{month}
€{total.toFixed(2)}
))}
)}
{/* Spending by chain — pie chart */}
{by_chain && by_chain.length > 0 && (
)}
{/* Shopping by day */}
{by_day.map(({ day, visits, total_eur }) => (
{day}
{visits} {visits !== 1 ? "visite" : "visita"}
€{total_eur.toFixed(2)}
))}
{/* Payment methods */}
{payment_methods.length > 0 && (
{payment_methods.map(({ method, count }) => (
{method}
{count}×
))}
)}
{/* Nutrition stats — from OFF data */}
{nutrition && nutrition.product_count > 0 && (
<>
Nutrizione — {nutrition.product_count} prodotti abbinati
{nutrition.by_category?.length > 0 && (
)}
{nutrition.nutriscore?.length > 0 && (
)}
{nutrition.nova?.length > 0 && (
)}
{nutrition.avg_macros &&
Object.keys(nutrition.avg_macros).length > 0 && (
)}
>
)}
);
}