1 Commits

Author SHA1 Message Date
metatroncubeswdev
c5ec8f368c feat: Phase 6 — ops/dispatch dashboard
- Prisma: Booking.totalPriceCents (parsed from the order webhook's
  total_price string), populated in booking.server.ts so "revenue by
  method" is real data, not a placeholder.
- app/services/dashboard.server.ts: pure aggregation over injected booking
  data (CLAUDE.md — no DB calls in the math) — bucketByLocalDate (each
  booking grouped under its own location's local calendar day, not a
  shared UTC day), revenueByMethod (confirmed/fulfilled only),
  utilizationByDateLocation (booked vs. summed SlotTemplate capacity for
  that weekday, capped at 100%), upcomingFulfillments, and a CSV
  writer/formatter with proper quote-escaping.
- /app/dashboard: filterable (date range, location, method, status) view
  with revenue-by-method cards, a capacity-utilization list (color-coded
  by load), an upcoming-fulfillments table with one-click
  confirmed->fulfilled/no_show status transitions, and a full by-day
  booking list.
- /app/dashboard/export: a resource route (loader only, no component)
  streaming the same filtered bookings as a downloadable CSV — kept
  separate from the dashboard route specifically so it can import
  dashboard.server.ts freely without the client-bundling constraint the
  main route has to respect (see below).

Fixed the same class of server/client bundling bug from the Phase 5 commit
before it could ship, this time by construction: dashboard.server.ts's
aggregation functions are called only inside app.dashboard.tsx's `loader`,
never referenced by the default-exported component (which only reads
useLoaderData() output) — verified this holds by actually running
`npm run build`, not just tsc/vitest, which both stay silent about this
class of error. Also hit (and fixed) the same "loader Dates arrive as
strings on the client" issue from Phase 1: swapped DateTime.fromJSDate for
DateTime.fromISO in the two places the component formats a booking's
slotStart.

Verified: lint, typecheck, 102 unit tests (+16 new for dashboard.server.ts,
+2 new for totalPriceCents parsing), 18 integration tests, both builds, and
a live script exercising the full aggregation pipeline (revenue exclusion
of cancelled bookings, utilization math, CSV output) against the Postgres
container.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:33:21 -04:00