Commit graph wutzcalc/client/src/admin/Admin.tsx
Author SHA1 Message Date
iris
a589f88cd8 admin: fix drink-order reorder race under rapid clicks
BarDrinkEditor derived selected drink order straight from the bar prop,
which only refreshes once a fire-and-forget PATCH's reload() lands. A
second reorder/add/remove click before that round-trip completed
re-derived from the same stale array the first click started from, so
whichever PATCH the server applied last silently won and discarded the
rest.

Track our own in-flight edits in local pendingIds state so back-to-back
clicks chain off each other instead of the lagging prop; it resets to
null (defer to the prop) whenever a fresh bar.drink_ids comes back,
whether that's our own round-trip landing or an edit from elsewhere.
2026-07-31 01:12:20 +02:00
iris
1d99881e88 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.
2026-07-30 11:40:34 +02:00
iris
59eb54d9fb server: generic 500s, fix duplicate/invalid-id error handling
All admin API error responses (and the global error handler) now emit
RFC 7807 application/problem+json bodies (type/title/status/detail)
instead of the ad hoc { error: string } shape, per review feedback on
this PR. Scoped to admin.ts + the global handler in index.ts, since
that's what this PR already touches; public.ts's routes still use the
old shape pending a follow-up.
2026-07-30 00:15:09 +02:00
iris
7fc4c1912c admin: share one drinks list between Drinks and Bars instead of two independent copies
Drinks and Bars each fetched + held their own copy of the drinks list. Adding
(or editing/archiving) a drink only updated Drinks's own copy — Bars's
BarDrinkEditor kept rendering the stale pre-change snapshot until a full page
reload re-mounted everything, so a newly added drink didn't show up in the
'add to bar' list without a manual refresh.

Lifted the drinks list + its reload fn into Dashboard, passed down as props to
both Drinks and Bars.
2026-07-29 21:20:36 +02:00
iris
fb02cc0f94 admin: reuse @wutzcalc/shared's Drink/Bar types instead of re-declaring them
Admin.tsx defined its own local Drink/BarRow-adjacent interfaces
rather than importing from @wutzcalc/shared, even though the tablet
code in the same package already does (App.tsx, Sale.tsx,
BarPicker.tsx). The two had already drifted: the local Drink.archived
was typed number while the shared package's was boolean, despite
describing the exact same wire field. No runtime bug (JS doesn't
enforce it, and the code only does truthiness checks), but a
maintainability smell.

BarRow now extends the shared Bar type instead of duplicating its
fields; Drink is imported directly. Totals/PerDrink/ByDay stay local
since they're admin-only stats shapes, not part of the wire contract
the tablet also consumes.
2026-07-29 20:36:36 +02:00
iris
058c77524a tablet error screen can recover from a stale bar_id; admin drink reorder gets a non-drag fallback
- App.tsx: the error screen (shown when api.config(barId) fails, e.g.
  an admin deleted/renamed the bar while this tablet was offline)
  only offered 'Neu laden', which re-reads the same stale localStorage
  bar_id and fails again — a permanent stuck loop with no in-app fix.
  Added a 'Bar wechseln' button reusing the existing changeBar()
  logic, which now also clears the error state.
- Admin.tsx BarDrinkEditor: the drag-and-drop reorder is a
  mouse-oriented API that doesn't fire on touch-only input and has no
  keyboard equivalent. Added ▲/▼ buttons alongside the drag handle so
  reordering works regardless of input method.
2026-07-29 20:33:57 +02:00
iris
59f6041a16 admin: surface silent failures, guard against double-submit on add
- Drinks.add()/patch() now check res.ok and alert() the error text,
  matching the pattern every other mutator in this file already
  used (Drinks.del(), everything in Bars) — previously a rejected
  name/price change just re-fetched the (unchanged) list with no
  indication anything went wrong.
- Stats.reload(), Drinks.reload(), Bars.reload() now .catch() a
  failed fetch/non-ok response instead of leaving an unhandled
  promise rejection — a network hiccup used to leave the section
  stuck on 'Lade…' forever with no visible error.
- Drinks 'Hinzufügen' and Bars 'Bar hinzufügen' are now disabled
  while their POST is in flight, mirroring the submitting guard
  Sale.tsx already uses — a fast double-tap could otherwise fire two
  requests before the first reload() resolved.
2026-07-29 20:32:55 +02:00
müde
cca3077b7a review fixes: light-mode contrast, tz robustness, deploy copy
- admin: maroon buttons use .danger class (white text); replace hardcoded
  #333/#ff8a8a/drop-target colors with theme vars so light mode is legible
- time.ts: force hourCycle h23 (no 24:00 artifact), drop dead 24-guard
- Makefile/README: rsync with excludes instead of `cp -a .` so .git and the
  dev DB don't ship to /opt; add rsync to deps
2026-06-14 22:55:29 +02:00
müde
df513fda68 admin: unify response error extraction into errText helper
removes a double-body-read in the delete handlers and makes the four
alert sites consistent.
2026-06-14 22:30:49 +02:00
müde
40cdc98f82 admin: fix timestamps, per-day stats, reset, delete drinks/bars
- store created_at as UTC ISO; display/group in Europe/Berlin
- stats grouped by business day (sales night past midnight, 5am cutoff)
- add "Statistik zurücksetzen"
- allow deleting drinks/tresen (refused if referenced by sales)
- CSV exports local wall-clock time
2026-06-14 21:55:51 +02:00
müde
a0babc2cfa admin: drag-and-drop drink reordering per bar 2026-05-19 18:22:49 +02:00
müde
4904ff0032 admin: reorder drinks per bar 2026-05-19 18:19:26 +02:00
müde
02c7e9b5fd admin: add and rename bars 2026-05-19 18:18:10 +02:00
müde
e0898bea22 scaffold festival drink tracker (pnpm workspace, Fastify + SQLite, Preact tablet UI, admin) 2026-05-19 18:12:01 +02:00