Files
vip-coordinator/backend/prisma/schema.prisma
kyle 3b0b1205df feat: comprehensive update with Signal, Copilot, themes, and PDF features
## Signal Messaging Integration
- Added SignalService for sending messages to drivers via Signal
- SignalMessage model for tracking message history
- Driver chat modal for real-time messaging
- Send schedule via Signal (ICS + PDF attachments)

## AI Copilot
- Natural language interface for VIP Coordinator
- Capabilities: create VIPs, schedule events, assign drivers
- Help and guidance for users
- Floating copilot button in UI

## Theme System
- Dark/light/system theme support
- Color scheme selection (blue, green, purple, orange, red)
- ThemeContext for global state
- AppearanceMenu in header

## PDF Schedule Export
- VIPSchedulePDF component for schedule generation
- PDF settings (header, footer, branding)
- Preview PDF in browser
- Settings stored in database

## Database Migrations
- add_signal_messages: SignalMessage model
- add_pdf_settings: Settings model for PDF config
- add_reminder_tracking: lastReminderSent for events
- make_driver_phone_optional: phone field nullable

## Event Management
- Event status service for automated updates
- IN_PROGRESS/COMPLETED status tracking
- Reminder tracking for notifications

## UI/UX Improvements
- Driver schedule modal
- Improved My Schedule page
- Better error handling and loading states
- Responsive design improvements

## Other Changes
- AGENT_TEAM.md documentation
- Seed data improvements
- Ability factory updates
- Driver profile page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 19:30:41 +01:00

306 lines
8.3 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
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)
// 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
}