Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Complete rewrite from Express to NestJS with enterprise-grade features: ## Backend Improvements - Migrated from Express to NestJS 11.0.1 with TypeScript - Implemented Prisma ORM 7.3.0 for type-safe database access - Added CASL authorization system replacing role-based guards - Created global exception filters with structured logging - Implemented Auth0 JWT authentication with Passport.js - Added vehicle management with conflict detection - Enhanced event scheduling with driver/vehicle assignment - Comprehensive error handling and logging ## Frontend Improvements - Upgraded to React 19.2.0 with Vite 7.2.4 - Implemented CASL-based permission system - Added AbilityContext for declarative permissions - Created ErrorHandler utility for consistent error messages - Enhanced API client with request/response logging - Added War Room (Command Center) dashboard - Created VIP Schedule view with complete itineraries - Implemented Vehicle Management UI - Added mock data generators for testing (288 events across 20 VIPs) ## New Features - Vehicle fleet management (types, capacity, status tracking) - Complete 3-day Jamboree schedule generation - Individual VIP schedule pages with PDF export (planned) - Real-time War Room dashboard with auto-refresh - Permission-based navigation filtering - First user auto-approval as administrator ## Documentation - Created CASL_AUTHORIZATION.md (comprehensive guide) - Created ERROR_HANDLING.md (error handling patterns) - Updated CLAUDE.md with new architecture - Added migration guides and best practices ## Technical Debt Resolved - Removed custom authentication in favor of Auth0 - Replaced role checks with CASL abilities - Standardized error responses across API - Implemented proper TypeScript typing - Added comprehensive logging Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
137
backend/prisma/migrations/20260125085806_init/migration.sql
Normal file
137
backend/prisma/migrations/20260125085806_init/migration.sql
Normal file
@@ -0,0 +1,137 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Role" AS ENUM ('ADMINISTRATOR', 'COORDINATOR', 'DRIVER');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Department" AS ENUM ('OFFICE_OF_DEVELOPMENT', 'ADMIN');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ArrivalMode" AS ENUM ('FLIGHT', 'SELF_DRIVING');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EventType" AS ENUM ('TRANSPORT', 'MEETING', 'EVENT', 'MEAL', 'ACCOMMODATION');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EventStatus" AS ENUM ('SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"auth0Id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
"picture" TEXT,
|
||||
"role" "Role" NOT NULL DEFAULT 'COORDINATOR',
|
||||
"isApproved" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "vips" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"organization" TEXT,
|
||||
"department" "Department" NOT NULL,
|
||||
"arrivalMode" "ArrivalMode" NOT NULL,
|
||||
"expectedArrival" TIMESTAMP(3),
|
||||
"airportPickup" BOOLEAN NOT NULL DEFAULT false,
|
||||
"venueTransport" BOOLEAN NOT NULL DEFAULT false,
|
||||
"notes" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "vips_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "flights" (
|
||||
"id" TEXT NOT NULL,
|
||||
"vipId" TEXT NOT NULL,
|
||||
"flightNumber" TEXT NOT NULL,
|
||||
"flightDate" TIMESTAMP(3) NOT NULL,
|
||||
"segment" INTEGER NOT NULL DEFAULT 1,
|
||||
"departureAirport" TEXT NOT NULL,
|
||||
"arrivalAirport" TEXT NOT NULL,
|
||||
"scheduledDeparture" TIMESTAMP(3),
|
||||
"scheduledArrival" TIMESTAMP(3),
|
||||
"actualDeparture" TIMESTAMP(3),
|
||||
"actualArrival" TIMESTAMP(3),
|
||||
"status" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "flights_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "drivers" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"phone" TEXT NOT NULL,
|
||||
"department" "Department",
|
||||
"userId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "drivers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "schedule_events" (
|
||||
"id" TEXT NOT NULL,
|
||||
"vipId" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"location" TEXT,
|
||||
"startTime" TIMESTAMP(3) NOT NULL,
|
||||
"endTime" TIMESTAMP(3) NOT NULL,
|
||||
"description" TEXT,
|
||||
"type" "EventType" NOT NULL DEFAULT 'TRANSPORT',
|
||||
"status" "EventStatus" NOT NULL DEFAULT 'SCHEDULED',
|
||||
"driverId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "schedule_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_auth0Id_key" ON "users"("auth0Id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "flights_vipId_idx" ON "flights"("vipId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "flights_flightNumber_flightDate_idx" ON "flights"("flightNumber", "flightDate");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "drivers_userId_key" ON "drivers"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "schedule_events_vipId_idx" ON "schedule_events"("vipId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "schedule_events_driverId_idx" ON "schedule_events"("driverId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "schedule_events_startTime_endTime_idx" ON "schedule_events"("startTime", "endTime");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "flights" ADD CONSTRAINT "flights_vipId_fkey" FOREIGN KEY ("vipId") REFERENCES "vips"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "drivers" ADD CONSTRAINT "drivers_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "schedule_events" ADD CONSTRAINT "schedule_events_vipId_fkey" FOREIGN KEY ("vipId") REFERENCES "vips"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "schedule_events" ADD CONSTRAINT "schedule_events_driverId_fkey" FOREIGN KEY ("driverId") REFERENCES "drivers"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,50 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VehicleType" AS ENUM ('VAN', 'SUV', 'SEDAN', 'BUS', 'GOLF_CART', 'TRUCK');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VehicleStatus" AS ENUM ('AVAILABLE', 'IN_USE', 'MAINTENANCE', 'RESERVED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "drivers" ADD COLUMN "isAvailable" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "shiftEndTime" TIMESTAMP(3),
|
||||
ADD COLUMN "shiftStartTime" TIMESTAMP(3);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "schedule_events" ADD COLUMN "actualEndTime" TIMESTAMP(3),
|
||||
ADD COLUMN "actualStartTime" TIMESTAMP(3),
|
||||
ADD COLUMN "dropoffLocation" TEXT,
|
||||
ADD COLUMN "notes" TEXT,
|
||||
ADD COLUMN "pickupLocation" TEXT,
|
||||
ADD COLUMN "vehicleId" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "vehicles" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "VehicleType" NOT NULL DEFAULT 'VAN',
|
||||
"licensePlate" TEXT,
|
||||
"seatCapacity" INTEGER NOT NULL,
|
||||
"status" "VehicleStatus" NOT NULL DEFAULT 'AVAILABLE',
|
||||
"currentDriverId" TEXT,
|
||||
"notes" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "vehicles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "vehicles_currentDriverId_key" ON "vehicles"("currentDriverId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "schedule_events_vehicleId_idx" ON "schedule_events"("vehicleId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "schedule_events_status_idx" ON "schedule_events"("status");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_currentDriverId_fkey" FOREIGN KEY ("currentDriverId") REFERENCES "drivers"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "schedule_events" ADD CONSTRAINT "schedule_events_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "vehicles"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
3
backend/prisma/migrations/migration_lock.toml
Normal file
3
backend/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
227
backend/prisma/schema.prisma
Normal file
227
backend/prisma/schema.prisma
Normal file
@@ -0,0 +1,227 @@
|
||||
// VIP Coordinator - Prisma Schema
|
||||
// This is your database schema (source of truth)
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
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[]
|
||||
events ScheduleEvent[]
|
||||
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
|
||||
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")
|
||||
|
||||
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())
|
||||
vipId String
|
||||
vip VIP @relation(fields: [vipId], references: [id], onDelete: Cascade)
|
||||
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
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime? // Soft delete
|
||||
|
||||
@@map("schedule_events")
|
||||
@@index([vipId])
|
||||
@@index([driverId])
|
||||
@@index([vehicleId])
|
||||
@@index([startTime, endTime])
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
TRANSPORT
|
||||
MEETING
|
||||
EVENT
|
||||
MEAL
|
||||
ACCOMMODATION
|
||||
}
|
||||
|
||||
enum EventStatus {
|
||||
SCHEDULED
|
||||
IN_PROGRESS
|
||||
COMPLETED
|
||||
CANCELLED
|
||||
}
|
||||
165
backend/prisma/seed.ts
Normal file
165
backend/prisma/seed.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { PrismaClient, Role, Department, ArrivalMode, EventType, EventStatus } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding database...');
|
||||
|
||||
// Clean up existing data (careful in production!)
|
||||
await prisma.scheduleEvent.deleteMany({});
|
||||
await prisma.flight.deleteMany({});
|
||||
await prisma.driver.deleteMany({});
|
||||
await prisma.vIP.deleteMany({});
|
||||
await prisma.user.deleteMany({});
|
||||
|
||||
console.log('✅ Cleared existing data');
|
||||
|
||||
// Create sample users
|
||||
const admin = await prisma.user.create({
|
||||
data: {
|
||||
auth0Id: 'auth0|admin-sample-id',
|
||||
email: 'admin@example.com',
|
||||
name: 'Admin User',
|
||||
role: Role.ADMINISTRATOR,
|
||||
isApproved: true,
|
||||
},
|
||||
});
|
||||
|
||||
const coordinator = await prisma.user.create({
|
||||
data: {
|
||||
auth0Id: 'auth0|coordinator-sample-id',
|
||||
email: 'coordinator@example.com',
|
||||
name: 'Coordinator User',
|
||||
role: Role.COORDINATOR,
|
||||
isApproved: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created sample users');
|
||||
|
||||
// Create sample drivers
|
||||
const driver1 = await prisma.driver.create({
|
||||
data: {
|
||||
name: 'John Smith',
|
||||
phone: '+1 (555) 123-4567',
|
||||
department: Department.OFFICE_OF_DEVELOPMENT,
|
||||
},
|
||||
});
|
||||
|
||||
const driver2 = await prisma.driver.create({
|
||||
data: {
|
||||
name: 'Jane Doe',
|
||||
phone: '+1 (555) 987-6543',
|
||||
department: Department.ADMIN,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created sample drivers');
|
||||
|
||||
// Create sample VIPs
|
||||
const vip1 = await prisma.vIP.create({
|
||||
data: {
|
||||
name: 'Dr. Robert Johnson',
|
||||
organization: 'Tech Corporation',
|
||||
department: Department.OFFICE_OF_DEVELOPMENT,
|
||||
arrivalMode: ArrivalMode.FLIGHT,
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
notes: 'Prefers window seat, dietary restriction: vegetarian',
|
||||
flights: {
|
||||
create: [
|
||||
{
|
||||
flightNumber: 'AA123',
|
||||
flightDate: new Date('2026-02-15'),
|
||||
segment: 1,
|
||||
departureAirport: 'JFK',
|
||||
arrivalAirport: 'LAX',
|
||||
scheduledDeparture: new Date('2026-02-15T08:00:00'),
|
||||
scheduledArrival: new Date('2026-02-15T11:30:00'),
|
||||
status: 'scheduled',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const vip2 = await prisma.vIP.create({
|
||||
data: {
|
||||
name: 'Ms. Sarah Williams',
|
||||
organization: 'Global Foundation',
|
||||
department: Department.ADMIN,
|
||||
arrivalMode: ArrivalMode.SELF_DRIVING,
|
||||
expectedArrival: new Date('2026-02-16T14:00:00'),
|
||||
airportPickup: false,
|
||||
venueTransport: true,
|
||||
notes: 'Bringing assistant',
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created sample VIPs');
|
||||
|
||||
// Create sample events
|
||||
await prisma.scheduleEvent.create({
|
||||
data: {
|
||||
vipId: vip1.id,
|
||||
title: 'Airport Pickup',
|
||||
location: 'LAX Terminal 4',
|
||||
startTime: new Date('2026-02-15T11:30:00'),
|
||||
endTime: new Date('2026-02-15T12:30:00'),
|
||||
description: 'Pick up Dr. Johnson from LAX',
|
||||
type: EventType.TRANSPORT,
|
||||
status: EventStatus.SCHEDULED,
|
||||
driverId: driver1.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.scheduleEvent.create({
|
||||
data: {
|
||||
vipId: vip1.id,
|
||||
title: 'Welcome Dinner',
|
||||
location: 'Grand Hotel Restaurant',
|
||||
startTime: new Date('2026-02-15T19:00:00'),
|
||||
endTime: new Date('2026-02-15T21:00:00'),
|
||||
description: 'Welcome dinner with board members',
|
||||
type: EventType.MEAL,
|
||||
status: EventStatus.SCHEDULED,
|
||||
driverId: driver2.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.scheduleEvent.create({
|
||||
data: {
|
||||
vipId: vip2.id,
|
||||
title: 'Conference Transport',
|
||||
location: 'Convention Center',
|
||||
startTime: new Date('2026-02-16T14:30:00'),
|
||||
endTime: new Date('2026-02-16T15:00:00'),
|
||||
description: 'Transport to conference venue',
|
||||
type: EventType.TRANSPORT,
|
||||
status: EventStatus.SCHEDULED,
|
||||
driverId: driver1.id,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created sample events');
|
||||
|
||||
console.log('\n🎉 Database seeded successfully!');
|
||||
console.log('\nSample Users:');
|
||||
console.log('- Admin: admin@example.com');
|
||||
console.log('- Coordinator: coordinator@example.com');
|
||||
console.log('\nSample VIPs:');
|
||||
console.log('- Dr. Robert Johnson (Flight arrival)');
|
||||
console.log('- Ms. Sarah Williams (Self-driving)');
|
||||
console.log('\nSample Drivers:');
|
||||
console.log('- John Smith');
|
||||
console.log('- Jane Doe');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error('❌ Error seeding database:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user