client: standalone /stats page with charts, off the admin dashboard

Closes #40.

New /stats route (client/stats.html + src/stats/), served by the same
catch-all pattern as /admin. Reuses the admin login (STATS_PUBLIC env var
on the server side decides whether it needs one at all).

Three uPlot charts (daily revenue, daily transaction count, top-5-drink
sold-qty trend) plus the same three tables Admin.tsx used to render inline
— those move here wholesale, Admin.tsx now just links to /stats instead of
fetching /admin/api/stats itself. The 'Statistik zurücksetzen' reset button
moves here too, gated on an actual admin session (checked separately from
whether /api/stats itself succeeded, since STATS_PUBLIC can make that true
for an anonymous viewer).

Chart lib is uPlot (~45kb) per mara's steer not to hand-roll this. Both
client and server build/typecheck clean; manually smoke-tested the auth
gate (401 unauthed, 200 after login) and the /stats route against a fresh
DB.
This commit is contained in:
iris 2026-07-30 10:25:54 +02:00 committed by mara
commit 1d99881e88
10 changed files with 332 additions and 75 deletions

View file

@ -287,6 +287,13 @@ button.danger { background: #5a2a2a; border-color: #7a3a3a; color: #fff; }
.admin .row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.admin .muted { opacity: 0.6; }
.admin .login { max-width: 320px; margin: 80px auto; display: flex; flex-direction: column; gap: 12px; }
.admin .chart {
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 8px;
padding: 12px 12px 4px;
margin-bottom: 16px;
}
.dnd-list { list-style: none; padding: 0; margin: 0 0 8px; }
.dnd-item {