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.
This commit is contained in:
parent
cb316d16d1
commit
6c2a938b91
3 changed files with 118 additions and 3 deletions
|
|
@ -79,6 +79,27 @@ export function formatLocal(d: Date, tz: string = TZ): string {
|
|||
return `${p.year}-${pad(p.month)}-${pad(p.day)} ${pad(p.hour)}:${pad(p.minute)}:${pad(p.second)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local wall-clock hour bucket as `YYYY-MM-DD HH:00`, for the continuous
|
||||
* hourly timeline in /stats (#45) — literal clock time, no business-day
|
||||
* rollover (unlike businessDay() below): the point of this bucket is "what
|
||||
* hour did this actually happen", not which sales night it counts toward.
|
||||
*/
|
||||
export function localHourBucket(d: Date, tz: string = TZ): string {
|
||||
const p = localParts(d, tz);
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
return `${p.year}-${pad(p.month)}-${pad(p.day)} ${pad(p.hour)}:00`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local hour-of-day (0-23), for the "which hour is busiest" pattern
|
||||
* summed across every day in /stats (#45) — deliberately not business-day
|
||||
* aware either, same reasoning as localHourBucket().
|
||||
*/
|
||||
export function localHourOfDay(d: Date, tz: string = TZ): number {
|
||||
return localParts(d, tz).hour;
|
||||
}
|
||||
|
||||
/**
|
||||
* The business day a timestamp belongs to, as `YYYY-MM-DD`.
|
||||
* Hours before BUSINESS_DAY_CUTOFF_HOUR are attributed to the previous day.
|
||||
|
|
|
|||
Loading…
Reference in a new issue