TeeTime Tournament Management Admin UI — Product Spec
Status: Draft · Owners: TeeTime Eng · Last Updated: 2025-12-10 Implementation snapshot: Core lifecycle, draw, scoring, leaderboard, matchplay, side comps, prizes, series, appeals, notifications, and handicap posting are implemented in teetime-admin. Remaining UX polish: richer create wizard (association mapping/CSS templates), division templates/assignment modes, and projector/shotgun visuals (partially delivered).
Comprehensive admin workspace for tournament operations: create competitions, manage entries, generate draws, live scoring, and results management.
Quick Orientation
- Purpose: Give club administrators full control over tournament lifecycle—from creation through finalization.
- Audience: Tournament directors, club ops, scorers, and handicap committees.
- Backend Status: Comprehensive tournament services already implemented in
@digiwedge/tee-time-services/tournaments. This spec defines the Admin UI layer.
Related Documents
| Document | Description |
|---|---|
| Tournament Backend Docs | Service documentation with API reference |
| Tee Sheet Management | Integration with tee sheet grid |
| Tournament Wireframes | UI wireframes for tournament screens |
Experience Principles
- Guided creation: Wizard-style competition setup with sensible defaults and format-specific configuration.
- Live leaderboard: Real-time scoring updates with position changes highlighted.
- Draw flexibility: Multiple draw methods with manual override capability.
- WHS compliance: Automatic handicap capture, CSS/PCC calculation, and association posting.
- Audit confidence: Complete scoring history with attestation workflow.
Personas & Core Jobs
| Persona | Key Jobs | Success Signal |
|---|---|---|
| Tournament Director | Create competitions, configure rules, finalize results | Smooth setup, no scoring disputes |
| Club Admin | Manage entries, generate draws, assign tee times | Full field, balanced groups |
| Scorer | Enter hole-by-hole scores, attest scorecards | Fast entry, no data loss |
| Handicap Committee | Review results, resolve appeals, post to association | Compliant posting, resolved appeals |
Navigation & Surfaces
Primary Navigation
Tournaments (main nav)
├── Calendar View (upcoming/past)
├── Active Competitions
├── Series / Order of Merit
└── Prize Templates
Competition Detail Surfaces
| Surface | Purpose | States |
|---|---|---|
| Competition Overview | Summary, status, quick actions | Draft, Open, In Progress, Completed |
| Entries List | Player entries, divisions, team members | Entered, Withdrawn |
| Draw & Pairings | Generated draw, manual adjustments | Not Generated, Generated, Locked |
| Live Leaderboard | Real-time positions, scores, thru | In Progress, Final |
| Scoring Console | Hole-by-hole entry, scorecard view | Pending, In Progress, Attested, Locked |
| Results & Prizes | Final positions, prize allocation, export | Draft, Finalized |
| Matchplay Bracket | Bracket visualization, match scoring | Seeded, In Progress, Complete |
Scope
In Scope
| Area | Description |
|---|---|
| Competition CRUD | Create, edit, delete competitions with all configuration |
| 10 Competition Formats | Stableford, Stroke, Matchplay, 4BBB, Foursomes, Greensome, Scramble, Texas Scramble, Ambrose, Par/Bogey |
| Entry Management | Player entries, divisions, team events, withdrawals |
| Handicap Integration | Snapshot capture, WHS compliance, association posting |
| Draw Generation | Random, handicap order, seeded, tee-time order draws |
| Shotgun Starts | Multi-hole simultaneous starts |
| Live Scoring | Hole-by-hole entry, real-time leaderboard |
| Scorecard Workflow | Entry → Attestation → Lock → Post |
| Countback Resolution | Automatic tie-breaking per WHS rules |
| Matchplay Brackets | Knockout tournaments, seeding, hole-by-hole match scoring |
| Side Competitions | Nearest pin, longest drive, 2's club |
| Series/OoM | Order of Merit across multiple events |
| Prize Management | Templates, allocation, prize pools |
| Appeals Workflow | Open, review, resolve scoring disputes |
| CSV/PDF Export | Leaderboards, draws, starter sheets |
| Tee Sheet Integration | Link rounds to tee sheets, sync bookings to entries |
Out of Scope
- Player-facing scoring app (separate mobile app)
- Payment processing for entry fees
- Real-time GPS tracking during rounds
- External tournament systems integration (USGA GHIN, etc.)
Fees scope alignment: entryFee and entryFee9Holes are collected for display/export only. No payment, invoicing, or wallet settlement flows are covered here; collection occurs offline or via separate billing modules.
Competition Lifecycle
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CREATE │───▶│ ENTRIES │───▶│ DRAW │─── ▶│ SCORING │
│ (Draft) │ │ (Open) │ │ (Generated) │ │(In Progress)│
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
┌─────────────┐ ┌─────────────┐ │
│ COMPLETED │◀───│ FINALIZE │◀─────────┘
│ (Posted) │ │ (Results) │
└─────────────┘ └─────────────┘
Status Definitions
| Status | Description | Allowed Actions |
|---|---|---|
DRAFT | Competition created, not open for entries | Edit all settings, Delete |
OPEN | Accepting entries | Add/edit entries, Generate draw |
IN_PROGRESS | Scoring underway | Score entry, View leaderboard |
COMPLETED | Results finalized, handicaps posted | Export, View results, Appeals |
CANCELLED | Competition cancelled | Archive |
Capability Highlights
Competition Formats
| Format | Type | Scoring | Players |
|---|---|---|---|
| Stableford | Individual | Points (higher better) | 1 |
| Stroke/Medal | Individual | Net strokes (lower better) | 1 |
| Par/Bogey | Individual | +/- vs par | 1 |
| Matchplay | Individual | Holes won/lost | 1v1 |
| Four-Ball Better Ball | Team | Better ball points | 2 |
| Foursomes | Team | Alternate shot | 2 |
| Greensome | Team | Best drive + alternate | 2 |
| Scramble | Team | Best shot | 2-4 |
| Texas Scramble | Team | Best shot + min drives | 4 |
| Ambrose | Team | Scramble + team handicap | 2-4 |
Division System
Automatic or manual division assignment based on:
- Handicap index range (e.g., 0-12, 13-24, 25+)
- Gender (Men, Ladies)
- Age (Juniors, Seniors, Super Seniors)
- Tee set (Championship, Medal, Forward)
Draw Types
| Type | Use Case | Algorithm |
|---|---|---|
| Random | Social events | Shuffle entries |
| Handicap Order | Club comps | Sort by HCP, group |
| Seeded | Championships | Manual seed positions |
| Tee Time Order | Pre-booked | Use existing bookings |
Start Types
| Type | Description |
|---|---|
| Tee Times | Sequential from 1st tee |
| Shotgun | All groups start simultaneously |
| Two-Tee | Groups from 1st and 10th |
| Mixed | Combination shotgun + tee times |
Key Workflows
1. Create Competition
Competition Wizard:
├── Step 1: Basics (name, format, dates, course)
├── Step 2: Handicap Settings (qualifying, allowance, CSS)
├── Step 3: Divisions (add/edit division rules)
├── Step 4: Entry Settings (fees, limits, windows)
├── Step 5: Rounds (multi-round configuration)
└── Step 6: Review & Publish
2. Entry Management
Entries Panel:
├── Add Entry (search player, select division)
├── Team Entry (captain + team members)
├── Guest Entry (manual handicap override)
├── Linked Entry (from tee time booking)
├── Bulk Import (CSV upload)
└── Withdraw Entry (reason tracking)
3. Draw Generation
Draw Panel:
├── Select Draw Type
├── Configure (group size, start time, interval)
├── Preview Draw
├── Generate
├── Manual Adjustments (swap, move, add late entry)
└── Lock Draw
4. Live Scoring
Scoring Console:
├── Select Round
├── Scorecard Grid (all players)
├── Individual Scorecard Entry
├── Bulk Score Entry (per group)
├── Attest Scorecard
└── Lock Round
5. Results & Finalization
Results Panel:
├── Live Leaderboard (refreshes every 30s)
├── Division Standings
├── Countback Details (for ties)
├── Assign Prizes (from templates)
├── Finalize Results
├── Post to Association (GolfRSA/DotGolf)
└── Export (CSV, PDF)
6. Matchplay Flow
Matchplay Panel:
├── Create Bracket (size: 8/16/32/64)
├── Seed Bracket (auto or manual)
├── View Bracket (interactive visualization)
├── Record Match (hole-by-hole or result only)
├── Advance Bracket
└── Final Result
Admin API Endpoints
Status Legend
| Tag | Meaning |
|---|---|
| ✅ Available | Implemented in backend |
| 🧭 Required | Admin UI adapter needed |
| 🔒 Admin | Implemented and protected by teetime-admin audience |
Competition Management
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | GET | /api/competitions | List competitions |
| ✅ | POST | /api/competitions | Create competition |
| ✅ | GET | /api/competitions/:id | Get competition detail |
| ✅ | PUT | /api/competitions/:id | Update competition |
| ✅ | DELETE | /api/competitions/:id | Delete competition |
| 🔒 | POST | /api/competitions/:id/publish | Open for entries |
| 🔒 | POST | /api/competitions/:id/cancel | Cancel competition |
Entry Management
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | GET | /api/competitions/:id/entries | List entries |
| ✅ | POST | /api/competitions/:id/entries | Create entry |
| 🔒 | POST | /api/competitions/:id/entries/bulk | Bulk import entries |
| ✅ | GET | /api/competitions/entries/:entryId | Get entry detail |
| ✅ | POST | /api/competitions/entries/:entryId/withdraw | Withdraw entry |
| 🔒 | POST | /api/competitions/admin/rounds/:id/sync-tee-sheet | Admin sync from tee sheet |
Draw & Pairings
| Status | Method | Path | Description |
|---|---|---|---|
| 🔒 | POST | /api/competitions/rounds/:roundId/draw | Generate draw |
| ✅ | GET | /api/competitions/rounds/:roundId/draw | Get draw |
| 🔒 | DELETE | /api/competitions/rounds/:roundId/draw | Clear draw |
| 🔒 | POST | /api/competitions/rounds/:roundId/draw/swap | Swap players |
| 🔒 | POST | /api/competitions/rounds/:roundId/draw/move | Move player |
| 🔒 | POST | /api/competitions/rounds/:roundId/draw/lock | Lock draw |
Scoring
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | GET | /api/competitions/rounds/:roundId/scorecards | List scorecards |
| ✅ | GET | /api/competitions/scorecards/:id | Get scorecard |
| ✅ | PUT | /api/competitions/scorecards/:id | Update scores |
| ✅ | POST | /api/competitions/scorecards/:id/attest | Attest scorecard |
| 🔒 | POST | /api/competitions/scorecards/:id/lock | Lock scorecard |
| 🔒 | POST | /api/competitions/scorecards/:id/unlock | Unlock (admin) |
Scorecard update (hole payload)
PUT /api/competitions/scorecards/:id
{
"holes": [
{ "holeNumber": 1, "grossStrokes": 4, "putts": 2 },
{ "holeNumber": 2, "grossStrokes": 5, "isNoReturn": false }
]
}
Scorecard attestation
POST /api/competitions/scorecards/:id/attest
{
"markerId": "player-456",
"confirmation": "ATTESTED",
"disputeReason": null
}
Leaderboard & Results
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | GET | /api/competitions/:id/leaderboard | Get leaderboard |
| ✅ | GET | /api/competitions/:id/leaderboard.csv | Export CSV |
| 🔒 | POST | /api/competitions/:id/finalize | Finalize results |
| ✅ | GET | /api/competitions/:id/stats | Competition stats |
| 🔒 | POST | /api/competitions/:id/results/recalculate | Recalculate |
Payload reference
Finalize results
POST /api/competitions/:id/finalize
{
"applyCountback": true,
"assignPrizes": false
}
- Applies countback ordering before persisting
competitionResult. - Optionally triggers prize auto-allocation when a prize pool/template exists.
Competition stats (response)
GET /api/competitions/:id/stats
{
"competitionId": "comp-123",
"entriesTotal": 120,
"entriesWithdrawn": 3,
"entriesCompleted": 118,
"averageGross": 82.4,
"averageNet": 70.8,
"averageStableford": 33.1,
"lowestGross": 71,
"lowestNet": 66,
"highestStableford": 41,
"css": 71,
"pcc": 1,
"pccByRound": [
{ "roundId": "r1", "pcc": 1, "source": "MANUAL" }
]
}
Matchplay
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | POST | /api/competitions/:id/matchplay/brackets | Create bracket |
| ✅ | POST | /api/competitions/:id/matchplay/brackets/seed | Seed bracket |
| ✅ | GET | /api/competitions/:id/matchplay/brackets/:bracketId | Get bracket |
| ✅ | POST | /api/competitions/:id/matchplay/matches/:matchId/holes | Record holes |
| ✅ | POST | /api/competitions/:id/matchplay/brackets/:bracketId/advance | Advance bracket |
Side Competitions
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | GET | /api/competitions/:id/nearest-pin | Get NTP results |
| ✅ | POST | /api/competitions/:id/nearest-pin | Record NTP |
| ✅ | GET | /api/competitions/:id/longest-drive | Get LD results |
| ✅ | POST | /api/competitions/:id/longest-drive | Record LD |
| ✅ | GET | /api/competitions/:id/twos-club | Get 2's club |
Prizes & Appeals
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | POST | /api/competitions/:id/prizes | Assign prizes |
| ✅ | GET | /api/competitions/clubs/:clubId/prize-templates | List templates |
| ✅ | POST | /api/competitions/:id/prize-pool/apply-template | Apply template |
| ✅ | POST | /api/competitions/:id/appeals | Open appeal |
| ✅ | GET | /api/competitions/:id/appeals | List appeals |
| ✅ | POST | /api/competitions/appeals/:appealId | Update appeal |
Series (Order of Merit)
| Status | Method | Path | Description |
|---|---|---|---|
| ✅ | POST | /api/competitions/series | Create series |
| ✅ | GET | /api/competitions/series/:seriesId | Get series |
| ✅ | GET | /api/competitions/series/:seriesId/standings | Get standings |
| ✅ | POST | /api/competitions/series/:seriesId/link | Link competition |
| ✅ | POST | /api/competitions/series/:seriesId/recalculate | Recalculate |
Access Control & Audit
| Action | Role Requirement | Audit Notes |
|---|---|---|
| Create/Edit/Delete competition | Tournament Director or Club Admin | Log user and timestamp |
| Publish/Cancel competition | Tournament Director | Capture status change + reason |
| Generate/Lock/Unlock draw | Tournament Director | Include before/after pairing snapshot hash |
| Add/Withdraw entries | Club Admin | Capture withdraw reason where provided |
| Score entry/attest scorecard | Scorer (scoring role) | Log hole updates and attestation user |
| Lock/Unlock scorecard | Tournament Director | Unlock requires reason |
| Finalize results | Tournament Director + Handicap Committee confirmation | Record countback applied + prize assignment flag |
| Post handicaps | Handicap Committee | Store provider + submission id |
| Appeals create/update | Tournament Director or Handicap Committee | Record resolution outcome |
All admin endpoints tagged 🔒 require the teetime-admin audience plus the mapped role above. Audit events should be emitted to the existing admin audit stream.
Data Models
Competition
interface Competition {
id: string;
clubId: string;
name: string;
description?: string;
format: CompetitionFormat;
status: 'DRAFT' | 'OPEN' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';
// Handicap settings
isHandicapQualifying: boolean;
handicapAllowance: number; // 0-100 percentage
handicapPostingMode: 'AUTO' | 'MANUAL' | 'NONE';
// Team settings
isTeamEvent: boolean;
teamSize?: number;
// Entry settings
maxEntries?: number;
entryFee?: number; // In cents
entryFee9Holes?: number;
entryOpens?: Date;
entryCloses?: Date;
// Relations
rounds: CompetitionRound[];
divisions: CompetitionDivision[];
eligibility?: EligibilityConfig;
localRules?: LocalRule[];
// Metadata
seasonId?: string;
seriesIds?: string[];
createdAt: Date;
updatedAt: Date;
}
type CompetitionFormat =
| 'STABLEFORD'
| 'STROKE'
| 'PAR_BOGEY'
| 'MATCHPLAY'
| 'FOUR_BALL_BETTER_BALL'
| 'FOURSOMES'
| 'GREENSOME'
| 'SCRAMBLE'
| 'TEXAS_SCRAMBLE'
| 'AMBROSE';
CompetitionRound
interface CompetitionRound {
id: string;
competitionId: string;
roundNumber: number;
date: Date;
courseId: string;
courseName?: string;
// Start configuration
startType: 'TEE_TIMES' | 'SHOTGUN' | 'TWO_TEE' | 'MIXED';
shotgunTime?: Date;
startingHoles?: number[];
// Draw settings
drawType?: 'RANDOM' | 'HANDICAP_ORDER' | 'SEEDED' | 'TEE_TIME_ORDER';
groupSize?: number;
intervalMinutes?: number;
drawLocked: boolean;
// Tee sheet link
teeSheetId?: string;
// 9-hole support
isNineHole: boolean;
holesInPlay: number;
}
CompetitionEntry
interface CompetitionEntry {
id: string;
competitionId: string;
playerId: string;
playerName: string;
divisionId?: string;
// Status
enteredAt: Date;
withdrawnAt?: Date;
withdrawalReason?: string;
// Handicap
handicapSnapshot: HandicapSnapshot;
handicapIndexOverride?: number;
// Team
teamName?: string; // Optional custom name for team events
teamMembers?: TeamMember[];
// Tee sheet link
teeTimeSlotId?: string;
// Pairing (after draw)
pairingNumber?: number;
teeTime?: Date;
startingHole?: number;
}
Scorecard
interface Scorecard {
id: string;
entryId: string;
roundId: string;
status: 'PENDING' | 'IN_PROGRESS' | 'ATTESTED' | 'LOCKED';
// Totals
grossTotal?: number;
netTotal?: number;
stablefordPoints?: number;
// Handicap applied
courseHandicap: number;
playingHandicap: number;
// Scores
holes: HoleScore[];
countback?: CountbackScores;
// Attestation
attestedAt?: Date;
attestedBy?: string;
lockedAt?: Date;
}
interface HoleScore {
holeNumber: number;
par: number;
strokeIndex: number;
grossStrokes?: number;
netStrokes?: number;
stablefordPoints?: number;
isNoReturn: boolean;
}
MatchplayBracket
interface MatchplayBracket {
id: string;
competitionId: string;
name: string;
size: 4 | 8 | 16 | 32 | 64;
currentRound: number;
status: 'PENDING' | 'SEEDED' | 'IN_PROGRESS' | 'COMPLETED';
matches: MatchplayMatch[];
}
interface MatchplayMatch {
id: string;
bracketId: string;
round: number;
position: number;
entry1Id?: string;
entry2Id?: string;
winnerId?: string;
status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED';
result?: string; // "3&2", "1 up", "19th"
holes: MatchplayHole[];
}
Prize Templates & Allocation
interface PrizeTemplate {
id: string;
clubId: string;
name: string;
currency: string;
lines: PrizeTemplateLine[];
}
interface PrizeTemplateLine {
positionFrom: number;
positionTo: number;
amount: number; // minor units
divisionId?: string;
sideCompetition?: 'NEAREST_PIN' | 'LONGEST_DRIVE' | 'TWOS';
}
interface PrizeAllocation {
competitionId: string;
entryId: string;
prizeName: string;
amount: number; // minor units
currency: string;
position: number;
divisionId?: string;
sourceTemplateId?: string;
}
Side Competitions
interface SideCompetitionResult {
id: string;
competitionId: string;
type: 'NEAREST_PIN' | 'LONGEST_DRIVE' | 'TWOS';
roundId: string;
holeNumber: number;
entryId: string;
distanceFeet?: number; // NTP
driveYards?: number; // LD
recordedAt: Date;
}
Appeals
interface Appeal {
id: string;
competitionId: string;
scorecardId?: string;
entryId?: string;
status: 'OPEN' | 'UNDER_REVIEW' | 'RESOLVED' | 'REJECTED';
reason: string;
resolution?: string;
createdBy: string;
createdAt: Date;
updatedAt: Date;
}
Series (Order of Merit)
interface Series {
id: string;
clubId: string;
name: string;
seasonId?: string;
scoringRule: 'POINTS' | 'AVERAGE_POSITION';
competitions: SeriesLink[];
}
interface SeriesLink {
competitionId: string;
weight: number; // multiplier for points
}
interface SeriesStanding {
seriesId: string;
entryId: string;
playerId: string;
points: number;
eventsPlayed: number;
position: number;
}
UI Components
Component Library
Use existing Ant Design components from teetime-admin:
| Component | Usage |
|---|---|
Table | Entries list, leaderboard, pairings |
Drawer | Entry form, scorecard entry |
Modal | Confirmations, prize assignment |
Form | Competition wizard, entry forms |
Steps | Competition creation wizard |
Tabs | Competition detail sections |
Card | Competition summary, round cards |
Tag | Status badges, division chips |
Tree | Bracket visualization |
Calendar | Competition calendar view |
Statistic | Competition stats display |
Progress | Scoring progress indicators |
Badge | Live indicator, update count |
Custom Components Needed
| Component | Purpose |
|---|---|
BracketViz | Interactive bracket visualization |
ScorecardGrid | Hole-by-hole score entry grid |
LeaderboardRow | Position, player, scores, thru |
DrawPreview | Visual draw preview before generation |
DivisionAssigner | Auto-assign entries to divisions |
HandicapBadge | Handicap index with snapshot indicator |
Telemetry
Events to Track
| Event | Properties |
|---|---|
tournament.create | clubId, format, isTeamEvent |
tournament.publish | competitionId, entryCount |
tournament.entry.add | competitionId, divisionId |
tournament.entry.withdraw | competitionId, reason |
tournament.draw.generate | competitionId, drawType, groupCount |
tournament.draw.manual_edit | competitionId, editType |
tournament.score.enter | competitionId, roundId, entryId |
tournament.scorecard.attest | competitionId, scorecardId |
tournament.results.finalize | competitionId, entryCount |
tournament.handicap.post | competitionId, provider, count |
tournament.matchplay.record | competitionId, matchId, result |
tournament.appeal.open | competitionId, reason |
Metrics
- Competition creation → completion rate
- Average entries per competition
- Draw generation time
- Scoring completion rate
- Time from last score to finalization
- Handicap posting success rate
- Appeal resolution time
Decisions & Defaults
- Live scoring transport: WebSocket channel for score updates and leaderboard deltas; fall back to 30s polling if the socket is unavailable.
- Multi-round/multi-course: Each
CompetitionRoundowns itsdateandcourseId; rounds across different days or courses are supported.startingHolesmaps shotgun/two-tee layouts. - Handicap freshness: Block new entries and tee-sheet sync when the latest handicap snapshot is older than 30 days; prompt admins to refresh.
- Prize handling: Prize pools and allocations are informational only; no wallet/ledger crediting. Exports and admin audit logs are the source of record.
- Results communication: On finalize, trigger the existing
tournament_results_finalizednotification template (email/push/SMS per tenant config); allow per-competition opt-out. - Scoring devices: Scoring console must be tablet-friendly (>=1024px) with responsive touch layouts; desktop parity retained.
- Team naming: Team events may include a
teamName; include in entries, leaderboards, and prize exports when present. - Season rollover/archive: Competitions auto-archive at season end (read-only, hidden from entry/draw actions) but remain available in history/export views.
Testing
Unit Tests
describe('CompetitionWizard', () => {
it('validates required fields per step');
it('shows format-specific options');
it('calculates entry window defaults');
});
describe('DrawGenerator', () => {
it('generates correct group sizes');
it('respects handicap ordering');
it('handles odd numbers correctly');
});
describe('LeaderboardCalculator', () => {
it('applies countback correctly');
it('handles tied positions');
it('updates positions on new scores');
});
describe('ScorecardEntry', () => {
it('calculates stableford points');
it('validates against NDB');
it('tracks attestation state');
});
E2E Tests (Playwright)
test('create stableford competition', async ({ page }) => {
// Navigate to tournaments
// Complete wizard steps
// Verify competition created
});
test('generate draw and view pairings', async ({ page }) => {
// Open competition
// Generate random draw
// Verify pairings displayed
});
test('enter scores and view leaderboard', async ({ page }) => {
// Open scoring console
// Enter hole-by-hole scores
// Verify leaderboard updates
});
test('matchplay bracket flow', async ({ page }) => {
// Create bracket
// Seed from qualifying
// Record match results
// Verify bracket advances
});
Implementation Phases
Phase 1: Core Competition Management (P0)
- Competition list view (calendar + table)
- Competition creation wizard
- Basic CRUD operations
- Entry management (add, withdraw, list)
- Division configuration
Phase 2: Draw & Pairings (P0)
- Draw type selection
- Draw generation
- Pairings view
- Manual adjustments (swap, move)
- Draw export (PDF)
Phase 3: Scoring Console (P1)
- Scorecard grid view
- Hole-by-hole entry
- Stableford/stroke calculation
- Attestation workflow
- Lock/unlock controls
Phase 4: Leaderboard & Results (P1)
- Live leaderboard with refresh
- Division filtering
- Countback display
- Finalization workflow
- CSV/PDF export
Phase 5: Matchplay (P1)
- Bracket creation
- Seeding methods
- Interactive bracket visualization
- Match scoring (hole-by-hole)
- Bracket advancement
Phase 6: Prizes & Side Competitions (P2)
- Prize templates
- Prize assignment
- Nearest pin/longest drive entry
- 2's club tracking
- Results integration
Phase 7: Series & Appeals (P2)
- Series management
- Order of Merit standings
- Competition linking
- Appeals workflow
- Appeal resolution
Phase 8: Integration & Polish (P2)
- Tee sheet integration
- Handicap posting (GolfRSA/DotGolf)
- Notifications
- Mobile-responsive views
- Performance optimization
Tee Sheet Integration
Link Round to Tee Sheet
Competition Round → Tee Sheet
│ │
└── teeSheetId ────┘
│
[Sync Tee Sheet Bookings]
│
▼
Competition Entries
Sync Behavior
- Round configured with
teeSheetId - Admin triggers "Sync from Tee Sheet"
- For each booked slot with a player:
- Create entry if not exists
- Link entry to
teeTimeSlotId - Capture handicap snapshot
- Draw inherits tee time order
Visual Indicators
On tee sheet grid, competition-linked slots show:
- Competition badge on tile
- Entry status (entered, withdrawn)
- Link to competition detail
Open Questions
None at this time.
Revision History
| Date | Author | Changes |
|---|---|---|
| 2025-12-09 | Rudi Haarhoff | Initial specification |