Skip to main content

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

MethodEndpointDescription
POST/v1/slots/:slotId/waitlistJoin waitlist
DELETE/v1/slots/:slotId/waitlistLeave waitlist
GET/v1/players/me/waitlistMy waitlist entries
POST/v1/waitlist/offers/:id/acceptAccept offer
POST/v1/waitlist/offers/:id/declineDecline offer

Admin Endpoints

MethodEndpointDescription
GET/admin/slots/:slotId/waitlistView slot waitlist
POST/admin/waitlist/:entryId/bumpBump priority
DELETE/admin/waitlist/:entryIdRemove from waitlist
GET/admin/courses/:id/waitlist/settingsGet settings
PATCH/admin/courses/:id/waitlist/settingsUpdate settings

Services

WaitlistService

  • joinWaitlist(slotId, playerId, priority?) - Add to queue
  • leaveWaitlist(slotId, playerId) - Remove from queue
  • getWaitlistPosition(slotId, playerId) - Check position
  • listWaitlist(slotId) - Full queue for admin
  • bumpPriority(entryId) - Move up in queue

SlotOfferService

  • createOffer(entryId, durationMinutes) - Create timed offer
  • acceptOffer(offerId, playerId) - Accept and book
  • declineOffer(offerId, playerId) - Decline and cascade
  • expireOffers() - Scheduled job to expire and cascade
  • cascadeToNext(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:

ComponentDescription
WaitlistSettingsTabConfigure queue size, offer duration, cascade rules
WaitlistAnalyticsTabView waitlist metrics and trends
WaitlistQueuePanelReal-time queue management per date

Location: apps/teetime/teetime-admin/src/app/waitlist/


Player UI

Status: Implemented (December 2025)

  • Mobile: WaitlistAcceptView at libs/ui/tee-time-ui-mobile/.../WaitlistAcceptView.tsx
  • Web: WaitlistAcceptPage at libs/tee-time-ui/.../WaitlistAcceptPage.tsx
  • Deep Link: /waitlist/accept?id=OFFER_ID wired 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