Commit graph wutzcalc/server/src/time.ts
Author SHA1 Message Date
iris
6c2a938b91 stats: add hourly views — continuous timeline + hour-of-day pattern
Closes #45.

Two new server-side aggregates in computeStats(), same JS-bucketing
approach as the existing by_day: by_hour (localHourBucket — literal clock
time, no business-day rollover, feeds a left-to-right timeline) and
by_hour_of_day (localHourOfDay — every day's hour 0-23 summed together,
zero-filled to a full 24-entry axis so a quiet hour reads as zero, not a
missing data point).

Client: two more revenue charts on /stats — 'Umsatz nach Stunde' (timeline)
and 'Umsatz nach Tageszeit' (pattern). Revenue only, not also a tx-count
variant, to keep the page from growing a chart per metric per granularity.

Build clean both sides. Manually verified by_hour_of_day returns all 24
zero-filled buckets against a fresh DB.
2026-07-30 20:14:45 +02:00
iris
9b82cd54c4 backend polish: stats LEFT JOIN, CSV formula injection, trustProxy, validated env vars, dead code
- Stats: bars table is now the LEFT side of the join to transactions,
  so a bar with zero sales still gets a zero row instead of vanishing
  from the totals table until its first sale (indistinguishable from
  a deleted bar). by_day stays JS-computed on purpose — a SQL rewrite
  would trade DST-aware timezone handling for a fixed-hour-offset
  'localtime' expression that's wrong on DST transition nights, to
  fix a cost the original review noted is 'fine today'. Not worth
  that trade for a money-adjacent report; left a comment explaining
  why.
- CSV export: cells starting with =/+/-/@ are now prefixed with '
  before quoting, closing a formula-injection path (an admin-entered
  drink/bar name like =HYPERLINK(...) would otherwise execute when
  the export is opened in Excel/LibreOffice).
- server/index.ts: PORT is now parsed and range-checked instead of a
  bare Number(...) (an unparseable value silently became NaN, and
  Fastify listens on a random free port for that); ADMIN_PASSWORD
  missing now warns at boot instead of only surfacing as a 500 at
  the first login attempt; new WUTZ_TRUST_PROXY env flag (off by
  default) so req.ip can actually reflect the real client behind a
  reverse proxy, documented in the README alongside the other env
  vars.
- time.ts: WUTZ_DAY_CUTOFF_HOUR gets the same parse+range-check
  treatment, for the same reason (a typo used to silently disable
  the business-day rollback with no error).
- shared/src/index.ts: Drink.archived is now typed 0 | 1, matching
  what SQLite actually returns (was boolean, which only worked by
  accident since 0 is falsy); removed TransactionRecord/
  TransactionItemRecord, declared but never returned by any route —
  leftovers from a planned endpoint that was never built.

Verified: pnpm --filter server|client typecheck/build all clean;
also ran the built server with a bad PORT and no ADMIN_PASSWORD to
confirm both warnings fire and the port falls back correctly.
2026-07-29 20:56:32 +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
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