feat: schema for visa tracking (additive)

Adds VisaStatus enum, VisaApplication model 1:1 with AttendingMember
(cascade-delete), and Program.visaStatusVisibleToMembers Boolean
toggle. The model intentionally stores process metadata only — status,
optional nationality, key dates, free-text notes. Sensitive documents
(passport scans, invitation letters, decision papers) continue to flow
over email and are never persisted in the platform.

Migration is purely additive: CREATE TYPE / CREATE TABLE / ADD COLUMN /
ADD FK. No DROP / ALTER on existing data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-04-28 19:28:53 +02:00
parent 6e5f607425
commit 289903c8bd
3 changed files with 338 additions and 4 deletions

View File

@@ -487,7 +487,8 @@ model Program {
settingsJson Json? @db.JsonB
// Grand-finale logistics
defaultAttendeeCap Int @default(3) // Max team members allowed at the grand finale per finalist team
defaultAttendeeCap Int @default(3) // Max team members allowed at the grand finale per finalist team
visaStatusVisibleToMembers Boolean @default(true) // Whether team members see their own visa status on the applicant dashboard
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -2726,9 +2727,10 @@ model AttendingMember {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
confirmation FinalistConfirmation @relation(fields: [confirmationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
flightDetail FlightDetail?
confirmation FinalistConfirmation @relation(fields: [confirmationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
flightDetail FlightDetail?
visaApplication VisaApplication?
@@unique([confirmationId, userId])
@@index([userId])
@@ -2774,3 +2776,36 @@ model FlightDetail {
@@index([status])
}
// ─────────────────────────────────────────────────────────────────────────────
// Grand-finale visa tracking (PR 4)
// Process metadata only — no document storage. Passport scans / invitation
// letters / decision documents are exchanged over email; this model just
// records what stage the application is at, key dates, and free-text notes.
// ─────────────────────────────────────────────────────────────────────────────
enum VisaStatus {
NOT_NEEDED
REQUESTED
INVITATION_SENT
APPOINTMENT_BOOKED
GRANTED
DENIED
}
model VisaApplication {
id String @id @default(cuid())
attendingMemberId String @unique // 1:1
status VisaStatus @default(REQUESTED)
nationality String? // self-declared, optional
invitationSentAt DateTime?
appointmentAt DateTime?
decisionAt DateTime? // GRANTED or DENIED date
notes String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
attendingMember AttendingMember @relation(fields: [attendingMemberId], references: [id], onDelete: Cascade)
@@index([status])
}