Waitlist System
Overview
A slot-level waitlist system with timed offers, cascade logic, and automated notifications for managing oversubscribed tee times.
Status: Implemented
The waitlist feature is fully implemented in the backend with database schema, services, and API endpoints.
Features
Core Waitlist
- Per-slot queues: Each tee time slot maintains its own waitlist
- Priority ordering: First-come-first-served with optional priority tiers
- Queue size limits: Configurable max waitlist size per course
- Auto-cleanup: Expired entries automatically removed
Slot Offers
- Timed offers: When a slot becomes available, offer is sent to first in queue
- Offer duration: Configurable time to accept (default: 15 minutes)
- Cascade logic: If declined or expired, automatically offer to next player
- Notification limits: Max offers before slot goes public
Waitlist Settings (Per-Course)
- Queue size limits
- Offer duration
- Auto-cascade toggle
- Auto-expiry rules
- Notification channel preferences
Data Model
WaitlistEntry
├── id, tenantId
├── slotId (references TeeTimeSlot)
├── playerId
├── priority
├── status (WAITING, OFFERED, ACCEPTED, EXPIRED, CANCELLED)
├── position
├── requestedAt
├── expiresAt
└── notes
WaitlistOffer
├── id, waitlistEntryId
├── offeredAt
├── expiresAt
├── status (PENDING, ACCEPTED, DECLINED, EXPIRED)
├── respondedAt
└── cascadedToId (next entry if declined)
WaitlistSettings (per-course)
├── courseId
├── maxQueueSize
├── offerDurationMinutes
├── maxOffersBeforePublic
├── autoCascadeEnabled
├── autoExpiryDays
└── notificationChannels (EMAIL, SMS, PUSH)
API Endpoints
Player Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/slots/:slotId/waitlist | Join waitlist |
| DELETE | /v1/slots/:slotId/waitlist | Leave waitlist |
| GET | /v1/players/me/waitlist | My waitlist entries |
| POST | /v1/waitlist/offers/:id/accept | Accept offer |
| POST | /v1/waitlist/offers/:id/decline | Decline offer |
Admin Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /admin/slots/:slotId/waitlist | View slot waitlist |
| POST | /admin/waitlist/:entryId/bump | Bump priority |
| DELETE | /admin/waitlist/:entryId | Remove from waitlist |
| GET | /admin/courses/:id/waitlist/settings | Get settings |
| PATCH | /admin/courses/:id/waitlist/settings | Update settings |
Services
WaitlistService
joinWaitlist(slotId, playerId, priority?)- Add to queueleaveWaitlist(slotId, playerId)- Remove from queuegetWaitlistPosition(slotId, playerId)- Check positionlistWaitlist(slotId)- Full queue for adminbumpPriority(entryId)- Move up in queue
SlotOfferService
createOffer(entryId, durationMinutes)- Create timed offeracceptOffer(offerId, playerId)- Accept and bookdeclineOffer(offerId, playerId)- Decline and cascadeexpireOffers()- Scheduled job to expire and cascadecascadeToNext(offerId)- Offer to next in queue
Notification Flow
1. Slot becomes available (cancellation, no-show, block removal)
↓
2. System checks waitlist for that slot
↓
3. If entries exist:
- Create WaitlistOffer for first entry
- Send notification (EMAIL/SMS/PUSH)
- Start expiry timer
↓
4. Player response:
- ACCEPT → Convert to booking, remove from waitlist
- DECLINE → Cascade to next entry
- TIMEOUT → Auto-cascade if enabled
↓
5. If max offers reached or queue empty:
- Slot becomes publicly available
Integration Points
- Tee Sheet: Listens for slot availability changes
- Booking Service: Converts accepted offers to bookings
- Notifications: Multi-channel offer delivery
- Scheduler: Periodic expiry processing
Configuration
// Default settings
{
maxQueueSize: 10,
offerDurationMinutes: 15,
maxOffersBeforePublic: 3,
autoCascadeEnabled: true,
autoExpiryDays: 7,
notificationChannels: ['EMAIL', 'PUSH']
}
Admin UI
Status: Implemented (December 2025)
The admin UI is accessible via TeeSheets → Select Course → Waitlist Tab and includes:
| Component | Description |
|---|---|
WaitlistSettingsTab | Configure queue size, offer duration, cascade rules |
WaitlistAnalyticsTab | View waitlist metrics and trends |
WaitlistQueuePanel | Real-time queue management per date |
Location: apps/teetime/teetime-admin/src/app/waitlist/
Player UI
Status: Implemented (December 2025)
- Mobile:
WaitlistAcceptViewatlibs/ui/tee-time-ui-mobile/.../WaitlistAcceptView.tsx - Web:
WaitlistAcceptPageatlibs/tee-time-ui/.../WaitlistAcceptPage.tsx - Deep Link:
/waitlist/accept?id=OFFER_IDwired in both apps - Link Router: Full handler at
apps/link-router/src/routes/waitlist.ts - Public API:
GET/POST /api/waitlist/offers/:id(no auth required)
Future Enhancements
- Admin UI for waitlist management (via TeeSheetDashboard)
- Player app waitlist view (WaitlistAcceptView + WaitlistAcceptPage)
- My waitlist entries view (player can see their position in queues)
- Group waitlist (multiple slots)
- Preferred time range matching