Adds the roster and manual promotion controls deferred from the booking
lifecycle milestones. Businesses can now inspect who holds a seat and who
is queueing on a class session, and promote waitlisted customers by hand
— including a deliberate over-capacity override when the session is full.
Backend:
- GET /v1/catalog/class-sessions/{id}/roster — session metadata plus
confirmed and waitlist entry lists with queue positions
- POST /v1/catalog/bookings/{id}/promote — 409 on a full session unless
allowOverCapacity is set; 400 for non-waitlisted, 404 for missing or
foreign bookings; reuses the shared confirm path so waitlist ledger
rows resolve and reminder jobs re-arm
- ListBookingsBySession repository method (pgx + memory); ClassSession
carries waitlistCount; UpcomingBooking carries classSessionId and
waitlistPosition
- OpenAPI paths + schemas, regenerated client, catalog service tests
Frontend:
- classes tab session rows expand into a two-column roster panel with
per-entry promote buttons and an explicit confirm-anyway step on 409
- booking detail modal shows waitlist position and offers promote for
waitlisted bookings; list badges show #N queue positions
- CS/EN i18n keys and seeded demo roster data; demo mutations surface
the demo notice
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Templates were delete-and-recreate only. Adds PUT
/v1/catalog/class-templates/{id} across repository, service (sharing
the create-time validation), route, spec, and client; the classes tab
gets a pencil action that loads the row into the form with a
save/cancel affordance.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- POST /v1/catalog/bookings/{id}/cancel lets businesses cancel
tenant-owned bookings; two-step confirm in the detail modal
- Shared PromoteWaitlist helper promotes earliest waitlisted bookings
when a class seat frees, on both dashboard and customer cancel paths;
promoted customers get a confirmation email, waitlist ledger marked
resolved
- waitlisted status surfaced in bookings UI: stats card, filter, warning
badge, demo seed, CS+EN strings
- BookingRecord gains created_at for fair ordering; GetBookingByID and
ResolveWaitlistEntry added to the repository surface
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Working-hours repo accepts nullable staff scope (pgx + memory); staff
rules live in availability_rules and drive public slot generation
- Protected routes for staff working hours, class templates, and class
sessions with tenant-ownership checks and input validation
- Sessions derive endsAt/capacity from templates; list resolves location
names and live booked counts; template delete cascades sessions,
session delete preserves bookings via SET NULL (mirrored in memory)
- Zones page: staff-scope selector on working hours, new Classes tab
with template/session CRUD, CS+EN strings, mobile tab wrapping
- Normalize availability_rules times to HH:MM in the service layer
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Services and staff existed as schema + booking FK targets but had no
management surface. Adds full CRUD: repository (pgx + in-memory with
matching FK semantics), catalog service validation with tenant isolation,
MaxStaff entitlement enforcement, protected API routes, OpenAPI schemas,
and dashboard screens for both entities.
Dashboard summary now resolves staff names on bookings and computes
setup completion from real configured state. Booking modal selects from
the tenant's actual services; blocked days can be scoped per staff member.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>