Add file requirements per round and super admin promotion via UI
Part A: File Requirements per Round - New FileRequirement model with name, description, accepted MIME types, max size, required flag, sort order - Added requirementId FK to ProjectFile for linking uploads to requirements - Backend CRUD (create/update/delete/reorder) in file router with audit logging - Mime type validation and team member upload authorization in applicant router - Admin UI: FileRequirementsEditor component in round edit page - Applicant UI: RequirementUploadSlot/List components in submission detail and team pages - Viewer UI: RequirementChecklist with fulfillment status in file-viewer Part B: Super Admin Promotion - Added SUPER_ADMIN to role enums in user create/update/bulkCreate with guards - Member detail page: SUPER_ADMIN dropdown option with AlertDialog confirmation - Invite page: SUPER_ADMIN option visible only to super admins Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -452,6 +452,7 @@ model Round {
|
||||
taggingJobs TaggingJob[]
|
||||
reminderLogs ReminderLog[]
|
||||
projectFiles ProjectFile[]
|
||||
fileRequirements FileRequirement[]
|
||||
evaluationDiscussions EvaluationDiscussion[]
|
||||
messages Message[]
|
||||
|
||||
@@ -581,10 +582,31 @@ model Project {
|
||||
@@index([country])
|
||||
}
|
||||
|
||||
model FileRequirement {
|
||||
id String @id @default(cuid())
|
||||
roundId String
|
||||
name String
|
||||
description String?
|
||||
acceptedMimeTypes String[] // e.g. ["application/pdf", "video/*"]
|
||||
maxSizeMB Int? // Max file size in MB
|
||||
isRequired Boolean @default(true)
|
||||
sortOrder Int @default(0)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Relations
|
||||
round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
|
||||
files ProjectFile[]
|
||||
|
||||
@@index([roundId])
|
||||
}
|
||||
|
||||
model ProjectFile {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String? // Which round this file was submitted for
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String? // Which round this file was submitted for
|
||||
requirementId String? // FK to FileRequirement (if uploaded against a requirement)
|
||||
|
||||
// File info
|
||||
fileType FileType
|
||||
@@ -605,16 +627,18 @@ model ProjectFile {
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
round Round? @relation(fields: [roundId], references: [id])
|
||||
replacedBy ProjectFile? @relation("FileVersions", fields: [replacedById], references: [id], onDelete: SetNull)
|
||||
replacements ProjectFile[] @relation("FileVersions")
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
round Round? @relation(fields: [roundId], references: [id])
|
||||
requirement FileRequirement? @relation(fields: [requirementId], references: [id], onDelete: SetNull)
|
||||
replacedBy ProjectFile? @relation("FileVersions", fields: [replacedById], references: [id], onDelete: SetNull)
|
||||
replacements ProjectFile[] @relation("FileVersions")
|
||||
|
||||
@@unique([bucket, objectKey])
|
||||
@@index([projectId])
|
||||
@@index([roundId])
|
||||
@@index([projectId, roundId])
|
||||
@@index([fileType])
|
||||
@@index([requirementId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user