← All tasks

R1 — Reports redesign (Loyverse-style At-a-glance) PROD · GATE 2

Live on prod 2026-05-20. Replaces the R5.3 monthly /cafe/reports screen with the Loyverse-modeled At-a-glance Sales Summary specced in the v0.2 Notion doc on 2026-05-18. Filter bar (date prev/next + popover w/ 8 presets + custom Start/End range, multi-store, employee) + 5 KPI cards w/ prior-period delta (Gross sales / Refunds / Discounts / Net sales / Cups sold — replaces Gross Profit per spec) + Sales by Hours area chart. Existing sections (Ranking of POS, Payment Methods, Top Products, Tax Summary) preserved and respect the new filter. Tier-agnostic — both Starter and Pro render the same shell.

11/11 prod · 51/51 regression = 62/62 total green. 2 commits to nix-cafe: fadb913 (initial ship) + 61d9970 (router.push fix — used absolute pathname so SPA nav updates the URL on prod). No backend migration. New dep: recharts. Validated on get-coffee (Pro) + lumiere-coffee (Starter).

Prod test — 11/11

51/51 regression green — no regressions from this push.
test-phase1-prod.mjs11/11
test-phase2-sso-outdoor-prod.mjs6/6
test-phase2-cafe-multishop-prod.mjs6/6
test-m1-prod.mjs10/10
test-r7-prod.mjs14/14
test-r8-prod.mjs4/4

Screenshots

1. Default load (Pro · today)Filter bar + 5 KPIs + Sales by Hours card
2. After prev arrowPeriod shifted Today→Yesterday (May 19)
3. Date popover open8 presets stacked + Start/End custom inputs + Done
4. Last 7 days preset appliedURL = ?preset=last_7_days; KPIs + chart reflow
5. Custom range 2026-05-01 → 2026-05-15From/To dates committed, period label shows range
6. Existing sections preserved (full-page)Ranking of POS + Payment Methods + Top Products + Tax Summary
7. Starter tenant (lumiere-coffee)Same shell renders, single-shop hides multi-shop filter

Mid-Gate-2 fix (commit 61d9970)

First post-deploy run had 3 failures on client-side nav (date prev / preset / custom): the filter bar used router.push(`?${searchParams}`) — a relative URL. On prod Cloudflare Workers the SPA hop didn't actually update the URL on the first attempt. Switched to usePathname() + absolute router.push(`${pathname}?${qs}`) (the Next.js App Router documented pattern for filter bars). Re-ran prod test: 11/11. Same code passed locally because Next dev resolves relative URLs differently than the Workers runtime.

Took two rounds of test iteration to clear the remaining cold-worker hydration flakes — added a hydration probe (open + close the popover before the prev-arrow click) and used page.evaluate w/ a native value setter for type=date inputs. All three nav assertions now use waitForURL for reliability.

Operator follow-ups (not blocking R1)

Confirm with Narong:

Cups Sold — implemented as SUM(qty − refunded_qty) across ALL categories on paid + partial_refund order lines. Loyverse's metric is drink-only; if Narong wants the same, we need a product category model (separate slice).

Employee filter — single-select (cashier from cafe.orders.cashier_pin_id). Loyverse's "All employees" UI is ambiguous about multi-select; staying single for v1.

Time-of-day filter ("All day") — not implemented. Spec is ambiguous whether it's a slicer or just a default label.

Future polish (not requested):

• Multi-shop Ranking of POS — extends aggregateRevenueByPosConfig to accept array. Today: ranking only filters when 0/1 shop selected.

• PDF + Excel exports — buttons still cosmetic stubs.

Next slice — R2 Order Analysis: the second piece of the H5 spec (Odoo-pivot-style Total→Month→Store→Session tree). Separate slice.