- Add GPS module with Traccar client service for device management - Add driver enrollment flow with QR code generation - Add real-time location tracking on driver profiles - Add GPS settings configuration in admin tools - Add Auth0 OpenID Connect setup script for Traccar - Add deployment configs for production server - Update nginx configs for SSL on GPS port 5055 - Add timezone setting support - Various UI improvements and bug fixes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
388 lines
11 KiB
Plaintext
388 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)
|
|
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)
|
|
|
|
// 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")
|
|
}
|
|
|