diff --git a/prisma/migrations/20260428161639_add_logistics_models/migration.sql b/prisma/migrations/20260428161639_add_logistics_models/migration.sql new file mode 100644 index 0000000..99062b2 --- /dev/null +++ b/prisma/migrations/20260428161639_add_logistics_models/migration.sql @@ -0,0 +1,49 @@ +-- CreateEnum +CREATE TYPE "FlightDetailStatus" AS ENUM ('PENDING', 'CONFIRMED'); + +-- CreateTable +CREATE TABLE "Hotel" ( + "id" TEXT NOT NULL, + "programId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "address" TEXT, + "link" TEXT, + "notes" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Hotel_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "FlightDetail" ( + "id" TEXT NOT NULL, + "attendingMemberId" TEXT NOT NULL, + "arrivalAt" TIMESTAMP(3), + "arrivalFlightNumber" TEXT, + "arrivalAirport" TEXT, + "departureAt" TIMESTAMP(3), + "departureFlightNumber" TEXT, + "departureAirport" TEXT, + "status" "FlightDetailStatus" NOT NULL DEFAULT 'PENDING', + "adminNotes" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "FlightDetail_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Hotel_programId_key" ON "Hotel"("programId"); + +-- CreateIndex +CREATE UNIQUE INDEX "FlightDetail_attendingMemberId_key" ON "FlightDetail"("attendingMemberId"); + +-- CreateIndex +CREATE INDEX "FlightDetail_status_idx" ON "FlightDetail"("status"); + +-- AddForeignKey +ALTER TABLE "Hotel" ADD CONSTRAINT "Hotel_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "FlightDetail" ADD CONSTRAINT "FlightDetail_attendingMemberId_fkey" FOREIGN KEY ("attendingMemberId") REFERENCES "AttendingMember"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3ebc63d..d5ccdbd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -505,6 +505,7 @@ model Program { // Grand-finale logistics finalistSlotQuotas FinalistSlotQuota[] waitlistEntries WaitlistEntry[] + hotel Hotel? @@unique([name, year]) @@index([status]) @@ -2722,7 +2723,49 @@ model AttendingMember { confirmation FinalistConfirmation @relation(fields: [confirmationId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) + flightDetail FlightDetail? @@unique([confirmationId, userId]) @@index([userId]) } + +// ───────────────────────────────────────────────────────────────────────────── +// Grand-finale logistics (PR 2: hotels + flight tracking) +// ───────────────────────────────────────────────────────────────────────────── + +enum FlightDetailStatus { + PENDING // team submitted details, admin not yet reviewed + CONFIRMED // admin verified booking +} + +model Hotel { + id String @id @default(cuid()) + programId String @unique // 1:1 — one hotel per edition + name String + address String? @db.Text + link String? // external URL to hotel page / booking confirmation + notes String? @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + program Program @relation(fields: [programId], references: [id], onDelete: Cascade) +} + +model FlightDetail { + id String @id @default(cuid()) + attendingMemberId String @unique // 1:1 + arrivalAt DateTime? + arrivalFlightNumber String? + arrivalAirport String? + departureAt DateTime? + departureFlightNumber String? + departureAirport String? + status FlightDetailStatus @default(PENDING) + adminNotes String? @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + attendingMember AttendingMember @relation(fields: [attendingMemberId], references: [id], onDelete: Cascade) + + @@index([status]) +}