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>
This commit is contained in:
2026-02-01 19:30:41 +01:00
parent 2d842ed294
commit 3b0b1205df
84 changed files with 12330 additions and 2103 deletions

View File

@@ -0,0 +1,27 @@
-- CreateEnum
CREATE TYPE "MessageDirection" AS ENUM ('INBOUND', 'OUTBOUND');
-- CreateTable
CREATE TABLE "signal_messages" (
"id" TEXT NOT NULL,
"driverId" TEXT NOT NULL,
"direction" "MessageDirection" NOT NULL,
"content" TEXT NOT NULL,
"timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isRead" BOOLEAN NOT NULL DEFAULT false,
"signalTimestamp" TEXT,
CONSTRAINT "signal_messages_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "signal_messages_driverId_idx" ON "signal_messages"("driverId");
-- CreateIndex
CREATE INDEX "signal_messages_driverId_isRead_idx" ON "signal_messages"("driverId", "isRead");
-- CreateIndex
CREATE INDEX "signal_messages_timestamp_idx" ON "signal_messages"("timestamp");
-- AddForeignKey
ALTER TABLE "signal_messages" ADD CONSTRAINT "signal_messages_driverId_fkey" FOREIGN KEY ("driverId") REFERENCES "drivers"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,32 @@
-- CreateEnum
CREATE TYPE "PageSize" AS ENUM ('LETTER', 'A4');
-- CreateTable
CREATE TABLE "pdf_settings" (
"id" TEXT NOT NULL,
"organizationName" TEXT NOT NULL DEFAULT 'VIP Coordinator',
"logoUrl" TEXT,
"accentColor" TEXT NOT NULL DEFAULT '#2c3e50',
"tagline" TEXT,
"contactEmail" TEXT NOT NULL DEFAULT 'contact@example.com',
"contactPhone" TEXT NOT NULL DEFAULT '555-0100',
"secondaryContactName" TEXT,
"secondaryContactPhone" TEXT,
"contactLabel" TEXT NOT NULL DEFAULT 'Questions or Changes?',
"showDraftWatermark" BOOLEAN NOT NULL DEFAULT false,
"showConfidentialWatermark" BOOLEAN NOT NULL DEFAULT false,
"showTimestamp" BOOLEAN NOT NULL DEFAULT true,
"showAppUrl" BOOLEAN NOT NULL DEFAULT false,
"pageSize" "PageSize" NOT NULL DEFAULT 'LETTER',
"showFlightInfo" BOOLEAN NOT NULL DEFAULT true,
"showDriverNames" BOOLEAN NOT NULL DEFAULT true,
"showVehicleNames" BOOLEAN NOT NULL DEFAULT true,
"showVipNotes" BOOLEAN NOT NULL DEFAULT true,
"showEventDescriptions" BOOLEAN NOT NULL DEFAULT true,
"headerMessage" TEXT,
"footerMessage" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "pdf_settings_pkey" PRIMARY KEY ("id")
);

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "schedule_events" ADD COLUMN "reminder20MinSent" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "reminder5MinSent" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "drivers" ALTER COLUMN "phone" DROP NOT NULL;

View File

@@ -102,7 +102,7 @@ model Flight {
model Driver {
id String @id @default(uuid())
name String
phone String
phone String? // Optional - driver should add via profile
department Department?
userId String? @unique
user User? @relation(fields: [userId], references: [id])
@@ -114,6 +114,7 @@ model Driver {
events ScheduleEvent[]
assignedVehicle Vehicle? @relation("AssignedDriver")
messages SignalMessage[] // Signal chat messages
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -198,6 +199,10 @@ model ScheduleEvent {
// 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
@@ -224,3 +229,77 @@ enum EventStatus {
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
}