feat(hotel): many hotels per edition + HotelStay (room assignment)

- Hotel.programId no longer unique (many hotels per edition)
- New HotelStay 1:1 with AttendingMember (hotelId, room, check-in/out)
- Program.hotel -> hotels[]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-04 19:13:52 +02:00
parent 200b5e0cb9
commit 75e63eb47f
2 changed files with 58 additions and 3 deletions

View File

@@ -0,0 +1,33 @@
-- DropIndex
DROP INDEX "Hotel_programId_key";
-- CreateTable
CREATE TABLE "HotelStay" (
"id" TEXT NOT NULL,
"attendingMemberId" TEXT NOT NULL,
"hotelId" TEXT NOT NULL,
"roomNumber" TEXT,
"checkInAt" TIMESTAMP(3),
"checkOutAt" TIMESTAMP(3),
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "HotelStay_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "HotelStay_attendingMemberId_key" ON "HotelStay"("attendingMemberId");
-- CreateIndex
CREATE INDEX "HotelStay_hotelId_idx" ON "HotelStay"("hotelId");
-- CreateIndex
CREATE INDEX "Hotel_programId_idx" ON "Hotel"("programId");
-- AddForeignKey
ALTER TABLE "HotelStay" ADD CONSTRAINT "HotelStay_attendingMemberId_fkey" FOREIGN KEY ("attendingMemberId") REFERENCES "AttendingMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "HotelStay" ADD CONSTRAINT "HotelStay_hotelId_fkey" FOREIGN KEY ("hotelId") REFERENCES "Hotel"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -506,7 +506,7 @@ model Program {
// Grand-finale logistics // Grand-finale logistics
finalistSlotQuotas FinalistSlotQuota[] finalistSlotQuotas FinalistSlotQuota[]
waitlistEntries WaitlistEntry[] waitlistEntries WaitlistEntry[]
hotel Hotel? hotels Hotel[]
lunchEvent LunchEvent? lunchEvent LunchEvent?
@@unique([name, year]) @@unique([name, year])
@@ -2786,6 +2786,7 @@ model AttendingMember {
flightDetail FlightDetail? flightDetail FlightDetail?
visaApplication VisaApplication? visaApplication VisaApplication?
lunchPick MemberLunchPick? lunchPick MemberLunchPick?
hotelStay HotelStay?
@@unique([confirmationId, userId]) @@unique([confirmationId, userId])
@@index([userId]) @@index([userId])
@@ -2802,7 +2803,7 @@ enum FlightDetailStatus {
model Hotel { model Hotel {
id String @id @default(cuid()) id String @id @default(cuid())
programId String @unique // 1:1 — one hotel per edition programId String // many hotels per edition
name String name String
address String? @db.Text address String? @db.Text
link String? // external URL to hotel page / booking confirmation link String? // external URL to hotel page / booking confirmation
@@ -2810,7 +2811,28 @@ model Hotel {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
program Program @relation(fields: [programId], references: [id], onDelete: Cascade) program Program @relation(fields: [programId], references: [id], onDelete: Cascade)
stays HotelStay[]
@@index([programId])
}
/// Per-attendee hotel/room assignment (1:1 with AttendingMember, mirrors FlightDetail).
model HotelStay {
id String @id @default(cuid())
attendingMemberId String @unique
hotelId String
roomNumber String?
checkInAt DateTime?
checkOutAt DateTime?
notes String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
attendingMember AttendingMember @relation(fields: [attendingMemberId], references: [id], onDelete: Cascade)
hotel Hotel @relation(fields: [hotelId], references: [id], onDelete: Restrict)
@@index([hotelId])
} }
model FlightDetail { model FlightDetail {