feat(metrics): per-staff performance view in dashboard

GET /v1/metrics/staff returns bookings, completed, cancelled,
utilization, and revenue per staff member over a trailing window
(default 30d, max 365d). Appointment bookings only — class sessions
have no staff assignment. All active staff appear, including
zero-booking rows.

Staff page gains a localized Performance panel; demo mode seeds
trailing-window bookings and matching metric rows.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 16:34:45 +02:00
1 parent 4674203502
commit 76e45d4178
11 files changed
+496 -3

No files matched your search

+11 -2
View File
@@ -179,6 +179,15 @@
- shared `ExportCsvButton` (fetch + blob + object-URL download; the generated client is JSON-typed so it can't carry file responses) on the bookings and customers page headers, CS/EN label
- verified live: real booking row with comma-in-name and quotes-in-notes escaped correctly, diacritics preserved, range bounds honored (inclusive `to`), three seeded customers exported; browser download produced the real file on the bookings page, customers button present, console clean
- remaining: live staging validation on Neon + pgx repository path
- `[done/partial]` Milestone 26: Staff performance view
- `GET /v1/metrics/staff?days=` — authed, tenant-scoped; trailing window defaults to 30 days, invalid/out-of-range values normalize to 30, max 365
- appointment bookings only — class sessions carry no `staff_id`, so class attendance is honestly excluded rather than silently misattributed; noted in the OpenAPI description
- per-staff metrics: bookings, completed (confirmed with `ends_at < now`), cancelled, utilization percent (confirmed share of the window), revenue cents from linked service prices over non-cancelled bookings
- every active staff member appears in the response including zero-booking rows — the service merges repository aggregates with the full staff list
- dashboard staff page gains a Performance panel above the member list: per-staff stat grid (bookings/completed/cancelled/utilization/revenue), CZK revenue formatting via `Intl.NumberFormat` per locale, CS/EN labels, responsive 2→5 column grid; demo mode fabricates rows matching the fabricated staff list for page coherence
- demo repository seeds four trailing-window bookings (3 confirmed + 1 cancelled) via `NewRepository`'s demo branch only — unit tests constructing `NewMemoryRepository` directly are unaffected
- verified live: endpoint returns 4/3/1, 75% utilization, 3 600 Kč; `days=7` and `days=999` normalize correctly; browser-checked both locales and mobile wrap, console clean; focused aggregation test passes
- remaining: live staging validation on Neon + pgx repository path
## Overview
Bookra is a Czech-first booking SaaS for small local service businesses. It targets salons, massage therapists, small clinics, repair shops, and small studios/clubs that need a simple public booking flow, internal schedule management, reminders, and basic business reporting without becoming a full ERP.
@@ -432,8 +441,8 @@ Shortlist of worthwhile directions once v1 stabilizes — kept deliberately smal
- **Recurring class schedules**: templates currently schedule sessions one at a time; a weekly recurrence rule with per-session overrides would cut setup time for studios.
- **Booking notes intake**: structured intake fields per service (e.g. "first visit?", "injuries?") instead of the single free-text notes field — still schema-light.
- **Persistent notification center**: M24 put real activity events in the dropdown, but read state is still in-memory and there's no delivery history; a `notifications` table (or read-cursor on `activity_events`) would persist both.
- **Staff performance view**: bookings/completion/utilization per staff member — the metrics primitives already exist; this is a filtered presentation layer.
- **CSV export**: bookings and customers export for accounting/backup — landed in M25; a report-style export (per-staff utilization, revenue summaries) can follow once metrics stabilize.
- **Capacity-based utilization**: M26 measures utilization as the confirmed share of bookings; a truer measure compares booked minutes against each member's working-hours rules — needs availability-rule integration in the metrics query.
- **Report-style exports**: M25 shipped raw row exports; a summarized report (per-staff metrics, revenue by service, period comparisons) can reuse `GetStaffMetrics`-style aggregation once the metric set stabilizes.
## Product Principles
- calm, premium, structured UX