mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 15:51:15 +00:00
feat(classes): session roster + manual waitlist promotion
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>
This commit is contained in:
1 parent
a82a48e1c8
commit
1cd5b1ba91
18 files changed
+1022
-59
No files matched your search
@@ -820,6 +820,34 @@ paths:
|
||||
description: Booking already cancelled
|
||||
"404":
|
||||
description: Booking not found
|
||||
/v1/catalog/bookings/{bookingID}/promote:
|
||||
post:
|
||||
tags: [Catalog]
|
||||
operationId: promoteDashboardBooking
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: bookingID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PromoteBookingRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Waitlisted booking confirmed; customer notified
|
||||
"400":
|
||||
description: Booking is not on a waitlist
|
||||
"404":
|
||||
description: Booking not found
|
||||
"409":
|
||||
description: Session at capacity; retry with allowOverCapacity to override
|
||||
/v1/catalog/blocked-days:
|
||||
get:
|
||||
tags: [Catalog]
|
||||
@@ -1196,6 +1224,28 @@ paths:
|
||||
description: Class session deleted
|
||||
"404":
|
||||
description: Class session not found
|
||||
/v1/catalog/class-sessions/{sessionID}/roster:
|
||||
get:
|
||||
tags: [Catalog]
|
||||
operationId: getSessionRoster
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: sessionID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Confirmed attendees and FIFO waitlist for the session
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SessionRoster"
|
||||
"404":
|
||||
description: Class session not found
|
||||
# ---------------- Billing ----------------
|
||||
/v1/billing/subscription:
|
||||
get:
|
||||
@@ -1714,6 +1764,12 @@ components:
|
||||
type: string
|
||||
notes:
|
||||
type: string
|
||||
classSessionId:
|
||||
type: string
|
||||
format: uuid
|
||||
waitlistPosition:
|
||||
type: integer
|
||||
description: 1-based queue spot for waitlisted class bookings; absent otherwise
|
||||
DashboardBookingRequest:
|
||||
type: object
|
||||
required: [customerName, customerEmail, startsAt]
|
||||
@@ -2126,7 +2182,7 @@ components:
|
||||
type: integer
|
||||
ClassSession:
|
||||
type: object
|
||||
required: [id, tenantId, templateId, templateTitle, startsAt, endsAt, capacity, bookedCount]
|
||||
required: [id, tenantId, templateId, templateTitle, startsAt, endsAt, capacity, bookedCount, waitlistCount]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
@@ -2154,6 +2210,63 @@ components:
|
||||
type: integer
|
||||
bookedCount:
|
||||
type: integer
|
||||
waitlistCount:
|
||||
type: integer
|
||||
SessionRosterEntry:
|
||||
type: object
|
||||
required: [bookingId, reference, customerName, customerEmail, status, createdAt]
|
||||
properties:
|
||||
bookingId:
|
||||
type: string
|
||||
format: uuid
|
||||
reference:
|
||||
type: string
|
||||
customerName:
|
||||
type: string
|
||||
customerEmail:
|
||||
type: string
|
||||
customerPhone:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
waitlistPosition:
|
||||
type: integer
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
SessionRoster:
|
||||
type: object
|
||||
required: [sessionId, templateTitle, startsAt, endsAt, capacity, confirmed, waitlist]
|
||||
properties:
|
||||
sessionId:
|
||||
type: string
|
||||
format: uuid
|
||||
templateTitle:
|
||||
type: string
|
||||
locationName:
|
||||
type: string
|
||||
startsAt:
|
||||
type: string
|
||||
format: date-time
|
||||
endsAt:
|
||||
type: string
|
||||
format: date-time
|
||||
capacity:
|
||||
type: integer
|
||||
confirmed:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/SessionRosterEntry"
|
||||
waitlist:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/SessionRosterEntry"
|
||||
PromoteBookingRequest:
|
||||
type: object
|
||||
properties:
|
||||
allowOverCapacity:
|
||||
type: boolean
|
||||
description: Confirm even when the session is full (deliberate overbooking)
|
||||
CreateClassSessionRequest:
|
||||
type: object
|
||||
required: [templateId, startsAt]
|
||||
|
||||
Reference in new issue
Block a user