Skip to main content

TeeTime Tee Sheet Management — Implementation Plan

Status: Phases 1-11, 13 Complete; Phase 12, 14-15 Pending (Phase 10 includes scheduled weather alerts + CourseStatusService; Phase 13 adds course conditions, activity log, staff notes; push certificate for Phase 7 still pending) Owners: TeeTime Eng Last Updated: 2025-12-12 Parent Document: tee-sheet-management-spec.md


Progress Summary

PhasePriorityStatusComponents
1. Core Daily ViewP0✅ CompleteTeeSheetDashboard, TeeTimeTimeline, DateNavigation, QuickStats
2. Block ManagementP0✅ CompleteBlocksTab, BlockCalendarPreview, CourseBlocksAdminController
3. Rules ManagementP1✅ CompleteRulesTab, 9 form components, 2 controllers, 2 repositories
4. Settings & ActionsP1✅ CompleteSettingsTab, CourseTeeSheetSettingsAdminController
5. Weather IntegrationP1✅ CompleteWeatherPanel, playability scoring, alerts, thresholds
6. Carts & ItemsP1✅ CompleteCartsItemsPanel, 3 repositories, CourseInventoryController
7. Real-time OperationsP2✅ Complete (push follow-up)WebSocket gateway + check-in/QR/StarterView live; FCM push wiring pending
8. Player IntelligenceP2✅ CompletePlayerProfilePopover, flags, no-show history, frequent partners
9. Waitlist & DemandP2✅ CompleteWaitlistService, SlotOfferService, WaitlistController, cron, DB-backed offers; admin UI wired (settings + analytics)
10. Communication HubP2✅ CompleteTeeTimeMessagingService, multi-channel, bulk; reminder scheduler + resolver wired; scheduled weather alerts + in-app channel
11. Tournament ManagementP2✅ CompleteSeparate module with 13 components
12. Analytics & InsightsP3🔲 PendingDemand heatmap, revenue analytics
13. Course ConditionsP2✅ CompleteCourseStatusService, CourseConditionsPanel, activity log, staff notes, pin sheet
14. Advanced UXP3🔲 PendingDrag & drop, mobile view
15. EnhancementsP3🔲 PendingDynamic pricing, multi-course

Runway Snapshot

  • Phasing: 14 phases mapped to P0/P1/P2/P3 (see table above); P0/P1 = complete.
  • Quality gates: Unit + integration for services; visual regression on grids/drawers; a11y axe scan on core flows.
  • Telemetry: Ship metrics for grid latency, WS connect rate, conflict check duration, and conversion from hold→booked.
  • Release posture: Feature flags per surface (Grid, Blocks, Rules, Starter) and per club; staged enablement with guardrails.

UI Parity vs Wireframes (Reality Check)

  • Matches: Daily grid, blocks, rules, settings, carts/items, starter view, waitlist settings/analytics, course conditions panel, activity log, staff notes follow the wireframe structure.
  • Gaps: Analytics tab (demand/revenue) not present (Phase 12); drag & drop/mobile/responsive polish tracked under Phase 14; multi-course view sits in Phase 15.

Functional Rules

Booking Windows

Rule TypePriority LogicExample
MemberLower number = checked firstPriority 10 member rule applies before priority 100
RateLower number = applies firstPriority 50 peak rate beats priority 100 standard

Block Validation

ScenarioValidationAction
Block overlaps bookingWarningList affected bookings, require confirmation
Block in pastErrorPrevent creation
Recurring block conflictsWarningShow all conflict dates
Block extends past course closeErrorPrevent end time > last tee time

Slot Status Transitions

AVAILABLE → HELD (5 min timeout)
HELD → BOOKED (payment confirmed)
HELD → AVAILABLE (timeout/cancelled)
BOOKED → AVAILABLE (cancellation)
AVAILABLE → BLOCKED (manual/event)
BLOCKED → AVAILABLE (block removed)

Validation & Error UX

Client-side Validation

  • Required field indicators (*)
  • Inline validation messages on blur
  • Disable submit until form valid
  • Time window: end > start
  • Date range: end >= start
  • Block conflict preview before save

API Error Handling

  • Preserve form state on failure
  • Show error banner with message
  • Highlight affected fields
  • Retry button for network errors
  • Optimistic UI with rollback

Destructive Actions

  • Delete block: "Delete [name]? Any recurring instances will also be removed."
  • Delete rule: "Delete [rule name]? This may affect booking availability."
  • Set offline: "Take tee sheet offline? New bookings will be blocked."
  • Cancel booking: "Cancel booking for [player]? This cannot be undone."

Success Feedback

  • Toast notification on save/delete
  • Grid row highlight animation on changes
  • Auto-close drawer on successful save
  • Optimistic status toggle updates

Accessibility

  • All form inputs labeled with aria-label or visible labels
  • Focus management: trap focus in drawers
  • Keyboard navigation: Tab through grid, Enter to select
  • Screen reader announcements for state changes
  • Color-blind safe status indicators (icons + color)
  • Minimum touch target: 44×44px

Keyboard Shortcuts

ShortcutActionContext
/ Navigate datesDaily view
TJump to todayDaily view
Ctrl/Cmd+BOpen create blockAny tab
EscapeClose drawerAny

Component Library

Use existing Ant Design components from teetime-admin:

ComponentUsage
TableTee sheet grid, rules lists, blocks list
DrawerSlot details, rule forms, block forms
Form / Form.ItemAll form fields with validation
SelectDropdowns for type, status, classifications
DatePickerDate selection, ranges
TimePickerTime windows
SwitchEnabled/online toggles
Checkbox.GroupDays of week, tees selection
InputNumberInterval, rate, priority
TagStatus badges, type indicators
AlertConflict warnings, info messages
ModalConfirmation dialogs
messageSuccess/error toasts
CalendarBlock preview calendar
ProgressUtilization bars
StatisticQuick stats display

Telemetry

Events to Track

EventProperties
teesheet.daily.viewcourseId, date
teesheet.slot.viewcourseId, teeTimeUuid
teesheet.block.createcourseId, type, isRecurring
teesheet.block.deletecourseId, blockId
teesheet.rule.createcourseId, ruleType
teesheet.rule.updatecourseId, ruleId, changedFields
teesheet.settings.updatecourseId, changedFields
teesheet.status.togglecourseId, newStatus
teesheet.startersheet.downloadcourseId, date
teesheet.weather.viewcourseId, date, playingScore
teesheet.weather.alert.viewcourseId, alertType, severity
teesheet.weather.settings.updateclubId, changedFields
teesheet.cart.createcourseId, cartType
teesheet.cart.reservecourseId, cartId, teeTimeUuid
teesheet.cart.releasecourseId, cartId, reason
teesheet.cart.status.changecourseId, cartId, oldStatus, newStatus
teesheet.item.createcourseId, itemType, category
teesheet.item.bookcourseId, itemId, teeTimeUuid, quantity
teesheet.item.togglecourseId, itemId, enabled

Metrics

  • Daily view load time (p50, p95)
  • Rules/blocks API latency
  • Error rate by operation
  • Utilization percentage tracking
  • Weather data refresh latency
  • Playing score distribution per day
  • Cart utilization rate (reserved/total)
  • Items booking rate by category
  • Revenue attribution (green fees vs carts vs items)

Testing

Unit Tests

describe('TeeSheetGrid', () => {
it('renders correct slot status colors');
it('calculates utilization percentage correctly');
it('filters tee times by tee number');
});

describe('BlockForm', () => {
it('validates date range');
it('shows conflict preview for overlapping bookings');
it('generates recurrence preview');
});

describe('RuleForm', () => {
it('validates time window (end > start)');
it('requires at least one classification');
it('generates human-readable preview');
});

E2E Tests (Playwright)

test('navigate to daily view and see tee times', async ({ page }) => {
// Navigate to tee sheet for course
// Verify grid renders with mock data
// Click on a slot and verify drawer opens
});

test('create and delete a block', async ({ page }) => {
// Open blocks tab
// Create new competition block
// Verify it appears in list
// Delete and confirm removal
});

test('toggle tee sheet offline', async ({ page }) => {
// Go to settings
// Toggle offline
// Verify confirmation dialog
// Verify status updates in sidebar
});

Implementation Phases

Phase 1: Core Daily View (P0) ✅ COMPLETE

Frontend: TeeSheetDashboard.tsx, TeeTimeTimeline.tsx, DateNavigation.tsx, QuickStats.tsx, TeeSheetGrid.tsx, SlotTile.tsx Backend: AdminCourseTeeSheetController at /admin/courses/{courseId}/tee-sheet/{date}

  • Club/course selector sidebar
  • Daily tee sheet grid with slot visualization
  • Date navigation (previous/next/today/calendar)
  • Slot detail drawer with player info, weather, and actions
  • Quick stats panel (capacity, revenue, utilization)
  • Online/offline status indicator
  • Print/Export starter sheet functionality

Phase 2: Block Management (P0) ✅ COMPLETE

Frontend: BlocksTab.tsx, BlockCalendarPreview.tsx Backend: CourseBlocksAdminController at /admin/courses/{courseId}/blocks Repository: CourseBlockRepository

  • Blocks tab with list view
  • Create/edit block drawer
  • Block conflict detection (POST /blocks/conflicts)
  • Single-day blocks
  • Recurring blocks (DAILY, WEEKLY, MONTHLY)
  • Calendar preview with visual block display
  • Day-of-week selection for recurring
  • Block notes for pro-shop reference

Block notifications implemented

  • Implemented: BlockNotificationService (libs/tee-time-services/src/lib/blocks/block-notification.service.ts) scans overlapping bookings, honors per-course notifyPlayersOnBlocks, and enqueues BulkNotice via messaging with course/club/tenant context.
  • Wired: BlocksController invokes notifier on create/update; settings flag persisted via tee-sheet.service.ts + migration 20251216_block_notifications.
  • Remaining UX: surface notification counts + per-request override in Blocks tab (future polish).

Phase 3: Rules Management (P1) ✅ COMPLETE

Frontend: RulesTab.tsx, CourseMemberRuleForm.tsx, CourseRateRuleForm.tsx, + 7 specialized form components Backend: CourseMemberRulesAdminController, CourseRateRulesAdminController Repositories: CourseMemberRuleRepository, CourseRateRuleRepository

  • Rules tab layout with Member/Rate sub-tabs
  • Member rule CRUD with booking window config
  • Rate rule CRUD with pricing tiers
  • Rule preview/simulation tool
  • Priority ordering
  • Rule conflict detection with visual warnings
  • Day-of-week, classification, association filters
  • Cart inclusion and pricing in rate rules
  • Phone booking visibility toggle

Phase 4: Settings & Actions (P1) ✅ COMPLETE

Frontend: SettingsTab.tsx Backend: CourseTeeSheetSettingsAdminController at /admin/courses/{courseId}/tee-sheet/settings

  • Settings tab with multiple configuration cards
  • Interval/crossover configuration (5-30 min presets)
  • Tee configuration (single/shotgun)
  • Booking channel toggles (online, phone, walk-in)
  • Operating hours and days
  • Weather threshold settings (wind, precip, playability)
  • Auto-block on severe weather toggle
  • Date range application for bulk settings
  • Configuration summary with capacity calculations

Settings Enforcement (Public Search):

  • Effective config lookup: TeeSheetService.getCourseTeeSheetConfig() uses overlapping date window (startDate <= end && endDate >= start) with orderBy: desc to find most specific config
  • SearchEngineService.resolveInternalConfig() maps MCA course ID → teetime DB UUID via CourseRepository.findById()
  • Booking channel enforcement: If bookingChannels.online === false, visitor/member flows return blank (admin bypass via isAdmin=true)
  • Operating days enforcement: If operatingDays empty or doesn't include sheet weekday, returns blank with offlineReason
  • Time window filtering: filterTeeTimesByWindow() filters tee times to startTime/endTime before capacity/pricing
  • DI wiring: TeeSheetModule imported in tee-time-services.module.ts so TeeSheetService injectable in SearchEngineService
  • numberOfTees enforcement: MultiClubSearchService.applyInternalConfig() filters tee <= config.numberOfTees
  • Multi-club surfaces: MultiClubSearchService.getManyClubs() and streamManyClubs() now apply config via resolveInternalConfig() + applyInternalConfig()

Phase 5: Weather Integration (P1) ✅ COMPLETE

Frontend: WeatherPanel.tsx, integrated in TeeSheetDashboard.tsx slot drawer Backend: /v1/courses/{courseId}/weather?date= endpoint

  • Weather API integration
  • Weather summary in daily view
  • Per-slot weather with hourly forecast
  • Playing score calculation (0-100) with color coding
  • Weather condition icons and warnings
  • Slot detail drawer weather panel
  • 4-hour round duration forecast projection
  • Weather alerts banner with severity levels
  • Weather settings panel in SettingsTab (thresholds)
  • Auto-block on severe weather (optional toggle) — server creates day blocks when playability is severe and flag is enabled

Phase 6: Carts & Items Management (P1) ✅ COMPLETE

Frontend: CartsItemsPanel.tsx Backend: CourseInventoryController at /v1/courses/:courseId/{carts,items,cart-reservations} Repositories: CourseCartInventoryRepository, CourseItemInventoryRepository, CourseCartReservationRepository

Backend:

  • Prisma models: CourseCartInventory, CourseItemInventory, CourseCartReservation
  • Repositories with full CRUD operations
  • Controller with cart, item, reservation endpoints
  • Auto-status computation for items (IN_STOCK, LOW_STOCK, OUT_OF_STOCK)
  • Currency conversion (cents ↔ database decimals)
  • Enum mapping (DB uppercase ↔ frontend lowercase)
  • Facilities hybrid: useFacilitiesCarts + facilitiesClubId flags on Course
  • FacilitiesCartAdapter placeholder for future Facilities integration
  • Migrations: 20251210_course_inventory_models, 20251214_course_facilities_carts
  • Tests: 18 unit tests covering CRUD and error handling

Frontend:

  • Carts & Items tab layout
  • Cart inventory list with status indicators
  • Cart add/edit with type, units, pricing
  • Cart reservation tracking by date
  • Items list with categories (rental, sale, snack)
  • Item add/edit with stock, threshold, pricing
  • Status indicators (active/inactive, in_stock/low_stock/out_of_stock)
  • Summary statistics (available carts, reserved, low stock items)

Phase 7: Real-time Operations (P2) ✅ COMPLETE (push wiring pending)

Infrastructure Assessment:

  • ✅ SSE streaming exists: multi-club-stream.controller.ts with 15s heartbeat
  • ✅ Starter sheet PDF generation: starter-sheet.controller.ts
  • ✅ WebSocket gateway: TeeSheetGateway (libs/tee-time-services/src/lib/gateway/tee-sheet.gateway.ts) with Redis fan-out + JWT room scoping
  • ✅ Check-in endpoints and models implemented
  • ✅ Pace tracking models implemented
  • ✅ Tablet-optimized StarterView implemented

Completed Implementation:

  • Check-in model: Added checkedInAt, checkedInBy, teeOffAt, lastHoleCompleted, expectedPaceMinutes to Booking
  • Migration: 20251211_booking_checkin_pace
  • Check-in endpoints: BookingCheckInAdminController with GET/POST/DELETE at /admin/bookings/:id/check-in
  • Pace endpoints: POST /admin/bookings/:id/pace for tee-off and hole updates
  • QR check-in: BookingQrController with JSON and SVG endpoints at /admin/bookings/:id/qr{,.svg}
  • Starter view: StarterView.tsx tablet-optimized React component with touch-friendly tiles
  • Starter CSS: StarterView.css with tablet media queries and large touch targets
  • Unit tests: booking-checkin-admin.controller.spec.ts, booking-qr.controller.spec.ts with edge cases
  • WebSocket gateway: TeeSheetGateway in libs/tee-time-services/src/lib/gateway/
    • JWT auth on connection with tenant/club scoping
    • Redis pub/sub for multi-instance fan-out (in-memory fallback)
    • Room subscriptions: club:{clubId}, course:{courseId}:{date}, booking:{bookingId}
    • Events: slot lifecycle, booking lifecycle, check-in/pace, tee-sheet sync
  • Pace alert service: PaceAlertService with configurable thresholds
    • Warning/critical thresholds via env vars
    • Emits pace.alert WebSocket event
    • FCM push stub (behind PACE_FCM_ENABLED flag)
  • Gateway tests: 33 tests covering auth, subscriptions, publish, room access control
  • Admin role policy: booking modifications gated for teetime-admin audience (starter=view/check-in, proshop=cancel, manager/admin=move+cancel)

Quality Gates Passed:

  • teetime-backend:lint --max-warnings=0
  • tee-time-services:lint --max-warnings=0
  • tsc -p tsconfig.app.json --noEmit
  • Gateway tests ✅ (33 tests)

Completed Gateway Wiring:

  • Gateway registered in app.module.ts with TeeSheetGateway and PaceAlertService
  • WsAdapter configured in main.ts bootstrap
  • Check-in controller wired to publish events: booking.checked-in, booking.check-in-undone, booking.tee-off, booking.pace-updated
  • Pace alert integration: calls PaceAlertService.checkPaceAndAlert() on pace updates

Remaining Implementation:

  • Full FCM push integration (wire to messaging service when available)
  • Hook BookingController events (hold/confirm/cancel/reschedule) to gateway for real-time slot updates

Phase 8: Player Intelligence (P2) ✅ COMPLETE

Frontend: PlayerProfilePopover.tsx, updated SlotTile.tsx Backend: PlayerFlagsAdminController at /admin/players/:playerId/profile, PlayerStatsRepository Database: PlayerFlag enum, flags/flagNotes/flagUpdatedAt/flagUpdatedBy on Player model

  • Enhanced slot tiles with player flags (VIP, CORPORATE, WARNING, SLOW_PLAY, STAFF, NEW_VISITOR)
  • Player profile popover on hover (lazy-loaded from /admin/players/:id/profile)
  • VIP/warning/sponsor badges with color-coded icons
  • No-show history display (last 12 months count)
  • Frequent partners display (top 3 partners, 3+ rounds in 12 months)

Phase 9: Waitlist & Demand (P2) ✅ COMPLETE

Backend Services (libs/tee-time-services/src/lib/waitlist/):

  • WaitlistService - Core logic: join, leave, match, position, expire notifications
  • SlotOfferService - Timed offer management with accept/decline/expire lifecycle
  • WaitlistExpiryCron - Expires stale notifications every 10 minutes
  • DemandAnalyticsService - Demand forecasting from BookingIntent patterns (P3)

Controller (WaitlistController at /admin/courses/:courseId/waitlist):

  • POST / - Join waitlist for slot
  • DELETE /:tenantId/:slotId/:memberId - Leave waitlist
  • GET /position/:slotId/:memberId - Get queue position
  • GET /count/:slotId - Get waitlist count for slot
  • GET /member/:tenantId/:memberId - Get member's active waitlists
  • POST /:entryId/booked - Mark entry as booked (internal)
  • GET /:date - Get waitlist for date (admin view)
  • POST /:entryId/offer - Manual offer to entry
  • POST /:entryId/accept - Accept offer endpoint
  • GET /settings + PUT /settings - Waitlist settings (per-course)

Admin Controller (WaitlistAdminController at /admin/waitlist):

  • GET /courses/:courseId/settings - Get per-course waitlist settings
  • PUT /courses/:courseId/settings - Update per-course waitlist settings
  • DELETE /courses/:courseId/settings - Reset to defaults
  • GET /courses/:courseId/analytics - Waitlist performance analytics
  • POST /courses/:courseId/expire-stale - Bulk expire stale entries
  • POST /courses/:courseId/cascade - Cascade offers on cancellation

Repository Enhancements (WaitlistEntryRepository):

  • findActiveBySlotId(slotId) - Get entries for slot
  • findActiveByMemberId(tenantId, memberId) - Get member's entries
  • findMatchingEntries(params) - FIFO matching with time window
  • hasActiveEntry(slotId, memberId) - Check existence
  • getPosition(slotId, memberId) - Get queue position
  • markNotified(entryId) - Mark as notified
  • markBooked(entryId) - Mark as booked
  • markCancelled(entryId) - Mark as cancelled
  • expireStaleNotifications(minutes) - Cleanup stale offers
  • countActiveBySlotId(slotId) - Get waitlist count

Database-Backed Offers (libs/prisma/tee-sheet-data/):

  • WaitlistOffer Prisma model with status enum (PENDING, ACCEPTED, DECLINED, EXPIRED)
  • WaitlistOfferRepository with full CRUD and stats methods
  • findPendingByEntry(), findBySlot(), countByStatus() queries
  • getOfferStats() for analytics (total, accepted, declined, expired, avg response time)
  • Migration: 20251211_waitlist_offers_settings

Per-Course Settings (libs/prisma/tee-sheet-data/):

  • CourseWaitlistSettings Prisma model with configurable fields
  • CourseWaitlistSettingsRepository with defaults fallback
  • Configurable: enabled, maxQueueSize, offerExpiryMinutes, timeWindowMinutes
  • Configurable: maxNotifications, autoExpireStale, staleExpiryMinutes, autoCascade
  • getEffectiveSettings() returns defaults when no course-specific config exists

Prometheus Metrics (libs/tee-time-services/src/lib/metrics/waitlist.metrics.ts):

  • waitlist_operations_total Counter - join/leave/match/expire by courseId
  • waitlist_offer_outcomes_total Counter - accepted/declined/expired by courseId
  • waitlist_offer_response_time_seconds Histogram - time to accept/decline
  • waitlist_queue_size Gauge - current queue size by courseId
  • waitlist_conversion_rate Gauge - conversion rate percentage by courseId

WebSocket Events (TeeSheetGateway):

  • waitlist.joined - Member joined waitlist
  • waitlist.left - Member left waitlist
  • waitlist.slot-offered - Slot offered to member
  • waitlist.offer-created - SlotOfferService created offer
  • waitlist.offer-accepted - Member accepted offer
  • waitlist.offer-declined - Member declined offer
  • waitlist.offer-expired - Offer expired

Messaging Templates:

  • WAITLIST_SLOT_AVAILABLE - Notify member slot available
  • WAITLIST_CONFIRMED - Confirm booking from waitlist
  • WAITLIST_OFFER_EXPIRING - Reminder before expiry (P3)
  • WAITLIST_POSITION_CHANGE - Position changed notification (P3)

BookingController Integration (booking.controller.ts:847):

  • maybeProcessWaitlist() called after booking cancellation
  • Finds slot info, course ID, calls waitlistService.processSlotAvailable()
  • Logs notification count for observability

Module Wiring:

  • WaitlistService provider in tee-time-services.module.ts
  • SlotOfferService provider in tee-time-services.module.ts
  • WaitlistController mounted in app.module.ts
  • WaitlistExpiryCron provider in app.module.ts
  • WaitlistAdminController exported from tee-time-services
  • WaitlistOfferRepository + CourseWaitlistSettingsRepository in tee-sheet-data

Admin UI (apps/teetime/teetime-admin/src/app/waitlist/):

  • WaitlistSettingsTab.tsx - Full settings form with all configuration options
  • WaitlistSettingsTab.css - Styled form layout
  • WaitlistSettingsTab.stories.tsx - Storybook with Default, Disabled, HighCapacity, Loading, Error variants
  • WaitlistAnalyticsTab.tsx - Analytics dashboard with entry/offer breakdowns, conversion rates
  • WaitlistAnalyticsTab.css - Dashboard styling with stat cards and progress bars
  • WaitlistAnalyticsTab.stories.tsx - Storybook with Default, HighPerformance, LowActivity, NoData, Loading, Error variants
  • index.ts - Barrel export
  • Wired into TeeSheetDashboard waitlist tab (tenant-scoped settings + analytics)

Future Enhancements (P3):

  • DemandAnalyticsService - Demand forecasting from BookingIntent patterns
  • WAITLIST_OFFER_EXPIRING template - Reminder before expiry
  • WAITLIST_POSITION_CHANGE template - Position changed notification

Phase 10: Communication Hub (P2) ✅ COMPLETE

Messaging Infrastructure (✅ Complete):

  • Multi-channel support: WhatsApp > Push > SMS > Email priority
  • TeeTimeMessagingService (libs/tee-time-services/src/lib/messaging/teetime-messaging.service.ts)
  • TeeTimeMessageQueueAdapter with BullMQ job queues
  • Channel providers: WhatsApp (Meta API), SMS (Twilio/SMSPortal), Email (SendGrid/SMTP2GO), Push
  • Template system with versioning, translations, revision history (messaging-client schema)

Notification Templates (🟡 60%):

  • Template DB models: MessageTemplate, MessageTemplateTranslation, TemplatePartial, TemplateRevision
  • TeeTime template keys defined in teetime-messaging.types.ts:
    • BOOKING_CONFIRMATION, BOOKING_REMINDER, BOOKING_CANCELLED
    • CHECKIN_CONFIRMATION, PACE_ALERT, PACE_WARNING
    • WEATHER_ALERT, PLAYER_MESSAGE, BULK_NOTICE
    • Waitlist templates (SLOT_AVAILABLE, CONFIRMED)
  • WhatsApp template sync service
  • Baseline template content (fallback copy for all tee-time keys)
  • Provider-branded/SMS-specific template variants

Send Message to Player (✅ 90%):

  • sendBookingConfirmation() - Single recipient
  • sendBookingReminder() - Single recipient (scheduler + resolver wired)
  • sendBookingCancelled() - Single recipient
  • sendCheckInConfirmation() - Single recipient
  • sendPaceAlert() - Single recipient
  • sendWeatherAlert() - Single recipient
  • sendPlayerMessage() - Direct message to player
  • Generic send() method
  • In-app messaging channel (push-backed, user-scoped)
  • Player preference integration

Bulk Notifications (✅ Complete):

  • sendBulk() - Generic bulk send
  • sendBookingReminderBulk() - Booking reminders to multiple players
  • sendPaceAlertBulk() - Pace alerts to groups
  • sendWeatherAlertBulk() - Weather alerts to affected players
  • sendBulkNotice() - Course-wide announcements
  • BlockNotificationService - Notify players affected by tee sheet blocks
  • enqueueBulk() in message queue adapter
  • Bulk send scheduling (delay/sendAt support)
  • Progress tracking for large bulk operations

Weather Alert Notifications (✅ Complete):

  • WeatherNotificationService (libs/tee-time-services/src/lib/messaging/weather-notification.service.ts)
  • Alert types: lightning, rain, wind, heat, cold, general
  • Severity levels: warning, watch, advisory
  • Time window support: effectiveFrom, effectiveUntil
  • WEATHER_ALERT template key
  • Weather threshold config in CourseTeeSheet model
  • Weather API + playability scoring via CourseWeatherController (/v1/courses/:courseId/weather) with auto-block on severe
  • Scheduled weather monitoring + alert dispatch
  • WeatherAffectedBookingsResolver port implementation

Scheduled Weather Monitoring (✅ Complete):

  • WeatherAlertScheduler (libs/tee-time-services/src/lib/messaging/weather-alert-scheduler.service.ts)
    • Cron-based polling (default: 15 * * * *, configurable via WEATHER_ALERT_CRON)
    • Lookahead window: WEATHER_ALERT_LOOKAHEAD_HOURS (default: 6 hours)
    • Cooldown deduplication: WEATHER_ALERT_COOLDOWN_MINUTES (default: 180 min)
  • WeatherMonitoringResolver port (weather-monitoring.port.ts)
    • listCoursesForMonitoring() - Returns courses with coordinates + thresholds
    • getLastAlert() - Cross-process cooldown via CourseConditionLog queries
    • recordWeatherAlert() - Audit trail for dispatched alerts
  • WeatherMonitoringResolverImpl in events-worker
    • Queries CourseTeeSheet for threshold configs (wind, precip, playability)
    • Joins today's TeeSheet to get teeSheetId for status reconciliation
    • Timezone-aware day bounds via date-fns-tz
  • CourseStatusService Integration
    • reconcileCourseStatus() calls CourseStatusService.evaluateAndTransition() after each weather check
    • Passes playability score + weather snapshot for audit
    • Automatic status transitions: OPEN → CONDITIONAL → CLOSED based on thresholds
  • Course deduplication (prefers entries with thresholds configured)
  • Tests: 6 unit tests covering alerts, cooldowns, deduplication, cross-process cooldown, severity selection

Booking Reminders (✅ Complete — worker-owned):

  • BookingNotificationListener (libs/tee-time-services/src/lib/messaging/booking-notification.listener.ts)
  • @OnEvent(BOOKING_CONFIRMED) → sends confirmation
  • @OnEvent(BOOKING_CANCELLED) → sends cancellation
  • BOOKING_REMINDER event defined in TeeTimeEvents
  • sendBookingReminder() method exists
  • BookingReminderScheduler (libs/tee-time-services/src/lib/messaging/booking-reminder-scheduler.service.ts)
    • Cron-based (default: hourly, configurable via BOOKING_REMINDER_CRON)
    • 24-hour reminders: tee times in 23-25h window
    • 2-hour reminders: tee times in 1.5-2.5h window (optional, BOOKING_REMINDER_2H_ENABLED)
    • Port interface: UpcomingBookingsResolver for database queries (events-worker)
    • Manual trigger: triggerReminderCheck() for testing/API
  • reminder24hSentAt + reminder2hSentAt fields on Booking model
  • Migration: 20251211_booking_reminder_tracking (applied)
  • UpcomingBookingsResolver port implementation (events-worker)
  • BookingDetailsResolver port implementation (events-worker)
  • WeatherAffectedBookingsResolver port implementation (events-worker)

Remaining Phase 10 polish (non-blocking):

  • Provider-branded template variants (SMS/WhatsApp) if required

Phase 11: Tournament Management (P2) ✅ COMPLETE

Note: Implemented as a separate TournamentsModule in libs/tee-time-services/src/lib/tournaments/ with comprehensive functionality far exceeding original scope.

Backend - TournamentsController (apps/teetime/teetime-backend/src/app/tournaments.controller.ts):

  • 60+ REST API endpoints with Swagger documentation
  • Guard-protected admin operations (teetime-admin audience)

Core Services (13 services exported):

  • CompetitionsService - Lifecycle (draft→open→in_progress→completed→cancelled)
  • EntriesService - Entry management, eligibility validation, withdrawals
  • ScorecardsService - Hole-by-hole scoring, attestation, locking
  • ResultsService - Leaderboards, position assignment, finalization
  • DrawsService - Pairing generation (random, handicap, seeded, tee-time order)
  • MatchplayService - Bracket generation, seeding, hole-by-hole match scoring
  • SideCompetitionsService - Nearest pin, longest drive, 2's club
  • SeriesService - Order of Merit / league standings
  • PrizeService - Prize templates, pool allocation
  • AppealsService - Appeal workflow (open→review→resolution)
  • HandicapSnapshotService - Capture handicap at entry time
  • HandicapPostingService - Post to GolfRSA/DotGolf
  • CompetitionTeeSheetSyncService - Sync tee-sheet bookings to entries

Competition Formats (10 formats):

  • Individual: STROKE, STABLEFORD, PAR_BOGEY, MATCHPLAY
  • Team: FOUR_BALL_BETTER_BALL, FOURSOMES, GREENSOME, SCRAMBLE, TEXAS_SCRAMBLE, AMBROSE

Draw & Flight Features:

  • 4 draw types: RANDOM, HANDICAP_ORDER, SEEDED, TEE_TIME_ORDER
  • Shotgun start support (multi-hole simultaneous starts)
  • Two-tee start support
  • Flight management with manual swaps/moves
  • Late entry accommodation
  • Draw lock mechanism

Scoring System:

  • Live hole-by-hole scoring
  • Stableford points calculation
  • Net score calculation with WHS compliance
  • Attestation workflow (player→marker→admin)
  • Countback tie resolution (back-9, back-6, back-3, last-hole)

Advanced Features:

  • Matchplay brackets (4-64 players), seeding, hole-by-hole scoring
  • Division/flight segmentation (handicap, gender, age)
  • PCC (Playing Conditions Calculation) support
  • CSS (Competition Standard Scratch)
  • Handicap posting to associations (GolfRSA, DotGolf)
  • Series/Order of Merit standings
  • Appeals system
  • Prize pool management with templates
  • CSV leaderboard exports

Prisma Models (20+ models in @prisma/teetime):

  • Competition, CompetitionRound, CompetitionDivision, CompetitionEntry
  • CompetitionScorecard, CompetitionScorecardHole, CompetitionResult
  • CompetitionPairing, CompetitionPairingPlayer, HandicapSnapshot
  • MatchplayBracket, MatchplayMatch, MatchplayHole, CompetitionAppeal
  • Season, CourseLayout, CourseLayoutHole, DailyCourseConditions

Documentation (apps/teetime/docs/docs/tournaments/):

  • 10 comprehensive guides: api.md, formats.md, draws.md, entries.md, scoring.md, results.md, handicaps.md, matchplay.md, side-competitions.md

Phase 12: Analytics & Insights (P3)

  • Analytics tab
  • Demand heatmap
  • Utilization trends
  • Revenue analytics
  • Revenue forecast
  • Cancellation insights

Phase 13: Course Conditions (P2) ✅ COMPLETE

Backend Services (libs/tee-time-services/src/lib/course-status/):

  • CourseStatusService - Core state machine for course status transitions
    • State transitions: OPEN → CONDITIONAL → CLOSED → OPEN
    • Playability thresholds with hysteresis to prevent rapid toggling
    • Default thresholds: conditional < 60, closed < 30, open > 70
  • evaluateAndTransition() - Automatic status changes based on playability score (requires teeSheetId)
  • evaluateForCourseDate() - Convenience wrapper that resolves teeSheet by courseId/date
  • applyManualOverride() - Staff can override status with expiry time
  • clearManualOverride() - Remove override and optionally re-evaluate
  • getStatus() - Current status, playability score, override info
  • getConditionHistory() - Audit trail queries

Database Models (@prisma/tee-sheet-data):

  • CourseStatus enum: OPEN, CONDITIONAL, CLOSED
  • ConditionChangeSource enum: AUTO_WEATHER, MANUAL_OVERRIDE, SYSTEM
  • CourseConditionLog model - Full audit trail with:
    • previousStatus, newStatus, playabilityScore
    • source, reason, weatherSnapshot, alertTypes
    • notificationsSent, bookingsAffected, createdBy
  • TeeSheet fields: courseStatus, playabilityScore, lastWeatherCheck
  • TeeSheet override fields: manualOverride, overrideReason, overrideExpiry, overrideBy
  • TeeSheet cart suspension: cartsSuspended
  • Migration: 20251217_course_conditions

Event System:

  • COURSE_STATUS_CHANGED_EVENT - Emitted on status transitions
  • CourseStatusChangedEvent interface with full context
  • EventEmitter2 integration for notification triggers

Integration Points:

  • WeatherAlertScheduler calls CourseStatusService.evaluateForCourseDate() after weather checks
    • Derives playability thresholds from course config (windWarningKmh, playabilityWarningThreshold)
    • Passes weather snapshot (maxWindKmh, maxPrecipMm, windowEnd) for audit
  • Automatic cart suspension on CLOSED status
  • Manual override respected (skips auto-transition while active)

Tests (course-status.service.spec.ts):

  • Status getter with manual override details (3 tests)
  • evaluateAndTransition full coverage (8 tests)
  • applyManualOverride (6 tests)
  • clearManualOverride (3 tests)
  • getConditionHistory (1 test)
  • Hysteresis dead-zone behavior (2 tests)
  • Event emission without emitter (1 test)

Controller (CourseStatusController at /admin/courses/:courseId/status):

  • GET /:teeSheetId - Get current status (playability, override, cart suspension)
  • GET /history - Get condition change history for date range
  • POST /override - Apply manual status override with expiry
  • POST /clear-override - Clear manual override, optionally re-evaluate

Frontend Integration (✅ Complete):

  • Course conditions panel in TeeSheetDashboard (CourseConditionsPanel.tsx)
  • Real-time status updates via WebSocket
  • Notification triggers on status change (notify affected bookings)
  • Pin sheet management UI (hole status, pin positions)
  • Activity log display in drawer (TeeTimeActivityService, TeeTimeActivityController)

Phase 14: Advanced UX (P3)

Completed:

  • Staff notes (StaffNotesService, StaffNotesController at /admin/courses/:courseId/tee-times/:teeTimeId/notes)
    • Create, list notes per tee time
    • Optional slot/booking linking
    • User attribution
    • Frontend integration in slot drawer

Pending:

  • Drag & drop booking moves
  • Mobile responsive view
  • Bulk block operations
  • Rate override per slot

Phase 15: Enhancements (P3)

  • Cart check-out/return tracking
  • Low inventory alerts for items
  • Weather-based booking recommendations
  • Send weather notifications to booked players
  • Dynamic pricing integration
  • Multi-course view (Confirmed) — Combined dashboard showing all courses for clubs with multiple courses

Open Questions

  1. Booking modification: RESOLVED — Admin audience follows role gates: starter = view + check-in only; proshop = cancel; manager/admin = move + cancel. Enforced on booking cancel/reschedule endpoints for teetime-admin audience.
  2. Multi-course view: Should there be a combined view showing all courses for a club? RESOLVED: Yes — Add combined multi-course view for clubs with multiple courses.
  3. Rate simulation: RESOLVED — Implement scenario-based preview (POST /admin/courses/:courseId/rules/rate/preview) returning the winning rule + evaluation list for visitor/member cases (date/time/ball count/gender/age/classification/public-holiday).
  4. Notifications: Should creating a block that affects bookings trigger player notifications? RESOLVED: Yes — Notify impacted players; control via admin panel with per-club configuration.
  5. Integration: How should this integrate with POS systems for walk-in bookings?
  6. Weather data refresh: How frequently should weather data be refreshed? Real-time for current conditions, hourly for forecasts?
  7. Playing score algorithm: Should the playing score algorithm be configurable per club based on their member preferences?
  8. Cart tracking: Should carts have GPS tracking integration for real-time location on the course?
  9. Items inventory: Should additional items support real-time inventory deduction and low-stock alerts?
  10. Weather-based pricing: Should dynamic pricing integrate with weather conditions (discounts on poor weather days)?

Technical Dependencies

Frontend

  • React 18+
  • Ant Design 5.x
  • React Query for data fetching
  • WebSocket client for real-time updates
  • Chart.js or similar for analytics

Backend

  • NestJS API endpoints
  • PostgreSQL database
  • Redis for caching (weather data)
  • WebSocket server (Socket.io)
  • Search & Enrichment Services integration

External Services

  • Weather API (via Search & Enrichment Services)
  • SMS provider (for notifications)
  • Email provider (for notifications)
  • PDF generation service (starter sheets)

Revision History

DateAuthorChanges
2025-12-09Rudi HaarhoffInitial implementation plan
2025-12-09Rudi HaarhoffAdded phases 5-7 for weather, carts, items
2025-12-09Rudi HaarhoffExpanded to 14 phases covering all features
2025-12-09Rudi HaarhoffSplit from main spec into focused document
2025-12-10Rudi HaarhoffAdded runway snapshot (quality gates, telemetry, flags)
2025-12-10Rudi HaarhoffPhase 6 backend: Prisma models, repositories, controller, tests
2025-12-10Rudi HaarhoffFull assessment: Phases 1-6 marked COMPLETE with component details
2025-12-10Rudi HaarhoffPhase 7 assessment: SSE exists, WebSocket/check-in/pace not implemented
2025-12-10Rudi HaarhoffPhase 8 COMPLETE: PlayerProfilePopover, PlayerStatsRepository, PlayerFlagsAdminController
2025-12-11Rudi HaarhoffPhase 7 IN PROGRESS: BookingCheckInAdminController, BookingQrController, StarterView.tsx
2025-12-11Rudi HaarhoffPhase 7 Gateway Wiring: TeeSheetGateway + PaceAlertService wired to app.module, WsAdapter in main.ts, check-in events publishing
2025-12-11Rudi HaarhoffPhase 11 COMPLETE: Documented comprehensive TournamentsModule (13 services, 60+ endpoints, 10 formats, matchplay, handicap integration)
2025-12-11Rudi HaarhoffPhase 7 Assessment Update: WebSocket gateway confirmed; check-in + pace flows live; remaining gaps: FCM push wiring, BookingController event fan-out
2025-12-11Rudi HaarhoffPhase 7 Role Policy: Admin booking cancel/reschedule gated by role (starter=view/check-in, proshop=cancel, manager/admin=move+cancel)
2025-12-11Rudi HaarhoffDecision: Multi-course view confirmed for Phase 14 — combined dashboard for clubs with multiple courses
2025-12-11Rudi HaarhoffRate Preview: Added scenario-based rate preview endpoint (/admin/courses/:courseId/rules/rate/preview) and resolved open question
2025-12-11Rudi HaarhoffDecision: Blocks that impact bookings trigger player notifications; toggles live in admin panel and configurable per club
2025-12-11Rudi HaarhoffPhase 9 IN PROGRESS: WaitlistService, SlotOfferService, WaitlistController, WaitlistExpiryCron implemented; BookingController integration live; WebSocket events added
2025-12-11Rudi HaarhoffPhase 9 COMPLETE: DB-backed offers (WaitlistOffer model), per-course settings (CourseWaitlistSettings), Prometheus metrics, WaitlistAdminController, Admin UI (WaitlistSettingsTab, WaitlistAnalyticsTab with Storybook)
2025-12-11Rudi HaarhoffPhase 10 Assessment: ~70% complete; TeeTimeMessagingService, multi-channel providers, bulk notifications exist; CRITICAL gap: BookingReminderScheduler missing
2025-12-11Rudi HaarhoffPhase 10 Booking Reminders: BookingReminderScheduler implemented with cron, 24h/2h windows, UpcomingBookingsResolver port; reminder tracking fields on Booking model
2025-12-11Rudi HaarhoffPhase 10 Build Verification: tee-time-services + teetime-backend builds green; UpcomingBookingsResolver wired; RulesEngine export added; type fixes in course-rate-rules-admin; migration 20251211_booking_reminder_tracking pending apply
2025-12-12Rudi HaarhoffPhase 10 schedulers/listeners moved to events-worker; migration applied; BookingDetailsResolver + WeatherAffectedBookingsResolver implemented in worker; backend providers pruned to avoid duplicates
2025-12-12Rudi HaarhoffUpdated statuses: Phase 7 marked complete (push pending), Waitlist UI wiring marked pending, Weather alert flow reflects live CourseWeatherController, clarified Phase 10 remaining work
2025-12-12Rudi HaarhoffWired waitlist settings/analytics into TeeSheetDashboard; Phase 9 marked complete; parity notes refreshed
2025-12-11Rudi HaarhoffPhase 13 Course Conditions: Added CourseStatusService with state machine (OPEN→CONDITIONAL→CLOSED), playability thresholds with hysteresis, manual overrides, CourseConditionLog audit trail; WeatherAlertScheduler now integrates with CourseStatusService for automatic status transitions
2025-12-11Rudi HaarhoffPhase 10 Weather Monitoring: Documented WeatherAlertScheduler details (cron, cooldowns, env vars), WeatherMonitoringResolver port with getLastAlert() for cross-process cooldown, course deduplication, 6 unit tests
2025-12-11Rudi HaarhoffRenumbered phases: Phase 13 = Course Conditions (P2, in progress), Phase 14 = Advanced UX (P3), Phase 15 = Enhancements (P3)
2025-12-12Rudi HaarhoffPhase 13 Enhancement: Added evaluateForCourseDate() convenience method to CourseStatusService; WeatherAlertScheduler now uses this method + derives thresholds from course config; 24 unit tests for CourseStatusService (200 suites, 1901 tests total)
2025-12-12Rudi HaarhoffPhase 4 Settings Enforcement: Course settings now enforced in public search — TeeSheetService.getCourseTeeSheetConfig() uses overlapping date window query; SearchEngineService enforces booking channels, operating days, and time windows; MultiClubSearchService applies same config (including numberOfTees) via applyInternalConfig(); DI wiring via TeeSheetModule import
2025-12-12Claude AssessmentPhase 13 COMPLETE: Verified CourseConditionsPanel.tsx, TeeTimeActivityService, StaffNotesService implementations; all frontend integration complete; fixed duplicate import in tee-time-services.module.ts; updated status and documentation alignment