Files
vip-coordinator/backend/prisma/schema.prisma
kyle 8e8bbad3fc feat: add party size tracking and master event linking
Add partySize field to VIP model (default 1) to track total people
traveling with each VIP including entourage/handlers/spouses. Vehicle
capacity checks now sum party sizes instead of just counting VIPs.

Add masterEventId self-reference to ScheduleEvent for linking transport
legs to shared itinerary items (events, meetings, meals). When creating
a transport event, users can link it to a shared activity and VIPs
auto-populate from the linked event.

Changes:
- Schema: partySize on VIP, masterEventId on ScheduleEvent
- Backend: party-size-aware capacity checks, master/child event includes
- VIP Form: party size input with helper text
- Event Form: party-size capacity display, master event selector
- Event List: party size in capacity and VIP names, master event badges
- Command Center: all VIP names shown with party size indicators

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 23:40:44 +01:00

394 lines
11 KiB
Plaintext

// VIP Coordinator - Prisma Schema
// This is your database schema (source of truth)
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ============================================
// User Management
// ============================================
model User {
id String @id @default(uuid())
auth0Id String @unique // Auth0 sub claim
email String @unique
name String?
picture String?
role Role @default(COORDINATOR)
isApproved Boolean @default(false)
driver Driver? // Optional linked driver account
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
@@map("users")
}
enum Role {
ADMINISTRATOR
COORDINATOR
DRIVER
}
// ============================================
// VIP Management
// ============================================
model VIP {
id String @id @default(uuid())
name String
organization String?
department Department
arrivalMode ArrivalMode
expectedArrival DateTime? // For self-driving arrivals
airportPickup Boolean @default(false)
venueTransport Boolean @default(false)
partySize Int @default(1) // Total people: VIP + entourage
notes String? @db.Text
flights Flight[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
@@map("vips")
}
enum Department {
OFFICE_OF_DEVELOPMENT
ADMIN
}
enum ArrivalMode {
FLIGHT
SELF_DRIVING
}
// ============================================
// Flight Tracking
// ============================================
model Flight {
id String @id @default(uuid())
vipId String
vip VIP @relation(fields: [vipId], references: [id], onDelete: Cascade)
flightNumber String
flightDate DateTime
segment Int @default(1) // For multi-segment itineraries
departureAirport String // IATA code (e.g., "JFK")
arrivalAirport String // IATA code (e.g., "LAX")
scheduledDeparture DateTime?
scheduledArrival DateTime?
actualDeparture DateTime?
actualArrival DateTime?
status String? // scheduled, delayed, landed, etc.
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("flights")
@@index([vipId])
@@index([flightNumber, flightDate])
}
// ============================================
// Driver Management
// ============================================
model Driver {
id String @id @default(uuid())
name String
phone String? // Optional - driver should add via profile
department Department?
userId String? @unique
user User? @relation(fields: [userId], references: [id])
// Shift/Availability
shiftStartTime DateTime? // When driver's shift starts
shiftEndTime DateTime? // When driver's shift ends
isAvailable Boolean @default(true)
events ScheduleEvent[]
assignedVehicle Vehicle? @relation("AssignedDriver")
messages SignalMessage[] // Signal chat messages
gpsDevice GpsDevice? // GPS tracking device
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
@@map("drivers")
}
// ============================================
// Vehicle Management
// ============================================
model Vehicle {
id String @id @default(uuid())
name String // "Blue Van", "Suburban #3"
type VehicleType @default(VAN)
licensePlate String?
seatCapacity Int // Total seats (e.g., 8)
status VehicleStatus @default(AVAILABLE)
// Current assignment
currentDriverId String? @unique
currentDriver Driver? @relation("AssignedDriver", fields: [currentDriverId], references: [id])
// Relationships
events ScheduleEvent[]
notes String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
@@map("vehicles")
}
enum VehicleType {
VAN // 7-15 seats
SUV // 5-8 seats
SEDAN // 4-5 seats
BUS // 15+ seats
GOLF_CART // 2-6 seats
TRUCK // For equipment/supplies
}
enum VehicleStatus {
AVAILABLE // Ready to use
IN_USE // Currently on a trip
MAINTENANCE // Out of service
RESERVED // Scheduled for upcoming trip
}
// ============================================
// Schedule & Event Management
// ============================================
model ScheduleEvent {
id String @id @default(uuid())
vipIds String[] // Array of VIP IDs for multi-passenger trips
title String
// Location details
pickupLocation String?
dropoffLocation String?
location String? // For non-transport events
// Timing
startTime DateTime
endTime DateTime
actualStartTime DateTime?
actualEndTime DateTime?
description String? @db.Text
type EventType @default(TRANSPORT)
status EventStatus @default(SCHEDULED)
// Assignments
driverId String?
driver Driver? @relation(fields: [driverId], references: [id], onDelete: SetNull)
vehicleId String?
vehicle Vehicle? @relation(fields: [vehicleId], references: [id], onDelete: SetNull)
// Master/child event hierarchy (shared activity → transport legs)
masterEventId String?
masterEvent ScheduleEvent? @relation("EventHierarchy", fields: [masterEventId], references: [id], onDelete: SetNull)
childEvents ScheduleEvent[] @relation("EventHierarchy")
// Metadata
notes String? @db.Text
// Reminder tracking
reminder20MinSent Boolean @default(false)
reminder5MinSent Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // Soft delete
@@map("schedule_events")
@@index([driverId])
@@index([vehicleId])
@@index([startTime, endTime])
@@index([status])
}
enum EventType {
TRANSPORT
MEETING
EVENT
MEAL
ACCOMMODATION
}
enum EventStatus {
SCHEDULED
IN_PROGRESS
COMPLETED
CANCELLED
}
// ============================================
// Signal Messaging
// ============================================
model SignalMessage {
id String @id @default(uuid())
driverId String
driver Driver @relation(fields: [driverId], references: [id], onDelete: Cascade)
direction MessageDirection
content String @db.Text
timestamp DateTime @default(now())
isRead Boolean @default(false)
signalTimestamp String? // Signal's message timestamp for deduplication
@@map("signal_messages")
@@index([driverId])
@@index([driverId, isRead])
@@index([timestamp])
}
enum MessageDirection {
INBOUND // Message from driver
OUTBOUND // Message to driver
}
// ============================================
// PDF Settings (Singleton)
// ============================================
model PdfSettings {
id String @id @default(uuid())
// Branding
organizationName String @default("VIP Coordinator")
logoUrl String? @db.Text // Base64 data URL or external URL
accentColor String @default("#2c3e50") // Hex color
tagline String?
// Contact Info
contactEmail String @default("contact@example.com")
contactPhone String @default("555-0100")
secondaryContactName String?
secondaryContactPhone String?
contactLabel String @default("Questions or Changes?")
// Document Options
showDraftWatermark Boolean @default(false)
showConfidentialWatermark Boolean @default(false)
showTimestamp Boolean @default(true)
showAppUrl Boolean @default(false)
pageSize PageSize @default(LETTER)
// Timezone for correspondence and display (IANA timezone format)
timezone String @default("America/New_York")
// Content Toggles
showFlightInfo Boolean @default(true)
showDriverNames Boolean @default(true)
showVehicleNames Boolean @default(true)
showVipNotes Boolean @default(true)
showEventDescriptions Boolean @default(true)
// Custom Text
headerMessage String? @db.Text
footerMessage String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("pdf_settings")
}
enum PageSize {
LETTER
A4
}
// ============================================
// GPS Tracking
// ============================================
model GpsDevice {
id String @id @default(uuid())
driverId String @unique
driver Driver @relation(fields: [driverId], references: [id], onDelete: Cascade)
// Traccar device information
traccarDeviceId Int @unique // Traccar's internal device ID
deviceIdentifier String @unique // Unique ID for Traccar Client app
// Privacy & Consent
enrolledAt DateTime @default(now())
consentGiven Boolean @default(false)
consentGivenAt DateTime?
lastActive DateTime? // Last location report timestamp
// Settings
isActive Boolean @default(true)
// Location history
locationHistory GpsLocationHistory[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("gps_devices")
}
model GpsLocationHistory {
id String @id @default(uuid())
deviceId String
device GpsDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)
latitude Float
longitude Float
altitude Float?
speed Float? // km/h
course Float? // Bearing in degrees
accuracy Float? // Meters
battery Float? // Battery percentage (0-100)
timestamp DateTime
createdAt DateTime @default(now())
@@map("gps_location_history")
@@index([deviceId, timestamp])
@@index([timestamp]) // For cleanup job
}
model GpsSettings {
id String @id @default(uuid())
// Update frequency (seconds)
updateIntervalSeconds Int @default(60)
// Shift-based tracking (4AM - 1AM next day)
shiftStartHour Int @default(4) // 4 AM
shiftStartMinute Int @default(0)
shiftEndHour Int @default(1) // 1 AM next day
shiftEndMinute Int @default(0)
// Data retention (days)
retentionDays Int @default(30)
// Traccar credentials
traccarAdminUser String @default("admin")
traccarAdminPassword String? // Encrypted or hashed
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("gps_settings")
}