Player Intelligence
The player intelligence system provides admin-managed flags, behavioral analytics, and membership tracking to support operational decisions.
Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Admin UI │────▶│ PlayerFlagsAdmin│────▶│ Player Model │
│ (flags panel) │ │ Controller │ │ (flags, notes) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
┌─────────────────┐ ┌──────────────────┐ ▼
│ Booking Events │────▶│ PlayerStats │◀───────────┘
│ (no-shows) │ │ Repository │
└─────────────────┘ └──────────────────┘
│
▼
┌──────────────────┐
│ Intelligence │
│ (no-shows, │
│ partners) │
└──────────────────┘
Sources:
apps/teetime/teetime-backend/src/admin/player-flags-admin.controller.tslibs/prisma/tee-sheet-data/src/lib/repositories/player-stats/player-stats.repository.ts
Player Flags
Admin-managed tags for player categorization:
| Flag | Description | Use Case |
|---|---|---|
VIP | High-value player | Priority booking, perks |
CORPORATE | Corporate client | Group rates, invoicing |
WARNING | Requires caution | Behavior notes, restrictions |
SLOW_PLAY | Slow play history | Pace monitoring |
STAFF | Club staff member | Staff rates, access |
NEW_VISITOR | First-time visitor | Welcome messaging |
Flag Management
// PUT /admin/players/:playerId/flags
{
"flags": ["VIP", "CORPORATE"],
"flagNotes": "Annual corporate client - ABC Corp"
}
Flags include audit trail:
flagUpdatedAt— Timestamp of last changeflagUpdatedBy— Admin user who made the change
Player Statistics
No-Show Tracking
Counts no-shows in rolling 12-month window:
// In PlayerStatsRepository
async countNoShows(playerId: string, months = 12): Promise<number> {
const since = subMonths(new Date(), months);
return this.prisma.booking.count({
where: {
playerId,
status: 'NO_SHOW',
createdAt: { gte: since }
}
});
}
Frequent Partners
Identifies top 3 playing partners (minimum 3 shared rounds):
interface FrequentPartner {
playerId: string;
firstName: string;
lastName: string;
roundCount: number; // Shared rounds in last 12 months
}
Query aggregates bookings where players appeared on same tee time.
Player Profile DTO
Admin profile endpoint returns unified intelligence:
// GET /admin/players/:playerId/profile
{
"id": "player-123",
"firstName": "John",
"lastName": "Smith",
"email": "john@example.com",
"phone": "+1234567890",
"memberNumber": "M-001",
"handicap": 12.5,
"flags": ["VIP", "CORPORATE"],
"flagNotes": "Annual corporate client",
"noShowCount": 1,
"frequentPartners": [
{
"playerId": "player-456",
"firstName": "Jane",
"lastName": "Doe",
"roundCount": 8
}
]
}
Membership Tracking
Club Membership
interface PlayerClubMembership {
id: string;
playerId: string;
clubId: string;
membershipType: string; // e.g., "Full", "Social", "Junior"
memberNumber: string;
memberStatus: 'ACTIVE' | 'SUSPENDED' | 'EXPIRED';
isMainMember: boolean; // Primary club association
startDate: Date;
expiryDate?: Date;
}
Association Membership
Tracks golf association affiliations:
interface PlayerAssociation {
id: string;
playerId: string;
provider: 'GOLFRSA' | 'DOTGOLF' | 'SAGA';
memberNumber: string;
handicap?: number;
handicapUpdatedAt?: Date;
expiresAt?: Date;
}
API Endpoints
Admin Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /admin/players/:playerId/profile | Get player with intelligence |
PUT | /admin/players/:playerId/flags | Update player flags |
GET | /admin/players/search | Search players |
Player Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /v1/players/me | Get own profile |
PUT | /v1/players/me | Update own profile |
GET | /v1/players/me/preferences | Get tee time preferences |
PUT | /v1/players/me/preferences | Update preferences |
Social Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /v1/players/me/buddies | Get buddy list |
POST | /v1/players/me/buddies | Send buddy invite |
DELETE | /v1/players/me/buddies/:id | Remove buddy |
GET | /v1/players/me/favorites | Get favorite clubs |
POST | /v1/players/me/favorites | Add favorite |
Player Registration Flow
IDP Authentication
│
▼
POST /v1/players/complete-registration
│
▼
PlayerRegistrationService
│
├─► Create Player record
│
├─► Link UserIdentity
│
└─► Link Association (if provided)
│
▼
McaImportService
│
└─► Enrich from provider (handicap, membership)
Intelligence Use Cases
Booking Flow
- Display flags in admin booking drawer
- Show no-show count for risk assessment
- Suggest frequent partners for group bookings
Check-In
- Highlight VIP for special treatment
- Show warning flags for staff awareness
- Track slow-play for pace management
Waitlist
- Priority VIP offers (if configured)
- Partner suggestions for partial group fills
Data Model
model Player {
id String @id @default(uuid())
firstName String
lastName String
email String?
phone String?
// Intelligence
flags PlayerFlag[]
flagNotes String?
flagUpdatedAt DateTime?
flagUpdatedBy String?
// Relations
homeClubId String?
homeClub Club? @relation(fields: [homeClubId])
memberships PlayerClubMembership[]
associations PlayerAssociation[]
bookings Booking[]
}
enum PlayerFlag {
VIP
CORPORATE
WARNING
SLOW_PLAY
STAFF
NEW_VISITOR
}
Metrics
| Metric | Description |
|---|---|
player_flags_total | Flags by type |
player_noshow_rate | No-show percentage |
player_registration_total | New registrations |
Troubleshooting
Missing Intelligence Data
- Verify player exists in database
- Check booking history for stats calculation
- Review association provider connectivity
Flag Not Displaying
- Verify flag update was saved
- Check admin user has write permissions
- Review audit trail for changes