feat: add competition heats system backend

- Add 3 new database tables: divisions, competition_types, event_user_heats
- Add seed data for 6 divisions (NEW, NOV, INT, ADV, ALL, CHA) and 2 competition types (J&J, STR)
- Add API endpoints for divisions and competition types
- Add heats management endpoints in events route (POST/GET/DELETE)
- Implement unique constraint: cannot have same role in same division+competition type
- Add participant verification before allowing heats management
- Support heat numbers 1-9 with optional Leader/Follower role
This commit is contained in:
Radosław Gierwiało
2025-11-14 15:32:40 +01:00
parent 0e5dc34cbf
commit 02d3d7ac42
7 changed files with 584 additions and 8 deletions

View File

@@ -54,6 +54,7 @@ model User {
ratingsGiven Rating[] @relation("RaterRatings")
ratingsReceived Rating[] @relation("RatedRatings")
eventParticipants EventParticipant[]
heats EventUserHeat[]
@@map("users")
}
@@ -76,6 +77,7 @@ model Event {
matches Match[]
participants EventParticipant[]
checkinToken EventCheckinToken?
userHeats EventUserHeat[]
@@map("events")
}
@@ -187,3 +189,53 @@ model EventParticipant {
@@index([eventId])
@@map("event_participants")
}
// Competition divisions (Newcomer, Novice, Intermediate, etc.)
model Division {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(50)
abbreviation String @unique @db.VarChar(3)
displayOrder Int @map("display_order")
// Relations
userHeats EventUserHeat[]
@@map("divisions")
}
// Competition types (Jack & Jill, Strictly, etc.)
model CompetitionType {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(50)
abbreviation String @unique @db.VarChar(3)
// Relations
userHeats EventUserHeat[]
@@map("competition_types")
}
// User's declared heats for matchmaking
model EventUserHeat {
id Int @id @default(autoincrement())
userId Int @map("user_id")
eventId Int @map("event_id")
divisionId Int @map("division_id")
competitionTypeId Int @map("competition_type_id")
heatNumber Int @map("heat_number") // 1-9
role String? @db.VarChar(10) // 'Leader', 'Follower', or NULL
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
division Division @relation(fields: [divisionId], references: [id])
competitionType CompetitionType @relation(fields: [competitionTypeId], references: [id])
// Constraint: Cannot have same role in same division+competition type
@@unique([userId, eventId, divisionId, competitionTypeId, role])
@@index([userId, eventId])
@@index([eventId])
@@map("event_user_heats")
}