const APP_VERSION = "50c23f2"; // ── User dropdown menu ──────────────────────────────────────────────────────── function UserMenu({ username, onLogout }) { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); React.useEffect(() => { function handleClick(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); } document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, []); return (
{open && (
)}
); } // ── Hash routing ────────────────────────────────────────────────────────────── function useRoute() { const [hash, setHash] = React.useState(window.location.hash || "#/receipts"); React.useEffect(() => { const h = () => setHash(window.location.hash || "#/receipts"); window.addEventListener("hashchange", h); return () => window.removeEventListener("hashchange", h); }, []); if (hash.startsWith("#/receipts/") && hash.endsWith("/correct")) { const id = hash.slice(11, -8); return { page: "correct", id }; } if (hash.startsWith("#/receipts/")) return { page: "receipt", id: hash.slice(11) }; if (hash.startsWith("#/new-receipt")) return { page: "new-receipt" }; if (hash.startsWith("#/items")) return { page: "items" }; if (hash.startsWith("#/basket")) return { page: "basket" }; if (hash.startsWith("#/stats")) return { page: "stats" }; if (hash.startsWith("#/item/")) return { page: "item-details", barcode: hash.slice(7) }; if (hash.startsWith("#/item")) return { page: "item-details", barcode: "" }; return { page: "receipts" }; } // ── Auth state ──────────────────────────────────────────────────────────────── function useAuth() { // null = not authed, undefined = loading, object = { user_id, username } const [auth, setAuth] = React.useState(undefined); React.useEffect(() => { fetch("/auth/me") .then((r) => (r.ok ? r.json() : null)) .then(setAuth) .catch(() => setAuth(null)); }, []); async function logout() { await fetch("/auth/logout", { method: "POST" }); setAuth(null); } return { auth, setAuth, logout }; } // ── App shell ───────────────────────────────────────────────────────────────── function App() { const route = useRoute(); const { auth, setAuth, logout } = useAuth(); const onReceipts = route.page === "receipts" || route.page === "receipt"; const onItems = route.page === "items"; const onBasket = route.page === "basket"; const onStats = route.page === "stats"; // Loading — check session before rendering anything if (auth === undefined) { return (
Caricamento…
); } // Not authenticated — show login form if (auth === null) { return ; } if (route.page === "correct") { return (
); } if (route.page === "new-receipt") { return (
); } return (
{/* Top bar */}
{/* Mobile: brand + logout */}
Spesa
{/* Desktop: brand + nav */}
Spesa Scontrini Prodotti Spesa Statistiche
{route.page === "receipts" && } {route.page === "receipt" && } {route.page === "items" && } {route.page === "basket" && } {route.page === "stats" && } {route.page === "item-details" && ( )}
{/* Footer */} {/* Mobile bottom tab bar */}
); } ReactDOM.createRoot(document.getElementById("root")).render();