Implement 10 platform features: evaluation UX, admin tools, AI summaries, applicant portal
Batch 1 - Quick Wins: - F1: Evaluation progress indicator with touch tracking in sticky status bar - F2: Export filtering results as CSV with dynamic AI column flattening - F3: Observer access to analytics dashboards (8 procedures changed to observerProcedure) Batch 2 - Jury Experience: - F4: Countdown timer component with urgency colors + email reminder service with cron endpoint - F5: Conflict of interest declaration system (dialog, admin management, review workflow) Batch 3 - Admin & AI Enhancements: - F6: Bulk status update UI with selection checkboxes, floating toolbar, status history recording - F7: AI-powered evaluation summary with anonymized data, OpenAI integration, scoring patterns - F8: Smart assignment improvements (geo diversity penalty, round familiarity bonus, COI blocking) Batch 4 - Form Flexibility & Applicant Portal: - F9: Evaluation form flexibility (text, boolean, section_header types, conditional visibility) - F10: Applicant portal (status timeline, per-round documents, mentor messaging) Schema: 5 new models (ReminderLog, ConflictOfInterest, EvaluationSummary, ProjectStatusHistory, MentorMessage), ProjectFile extended with roundId + isLate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -256,6 +256,19 @@ model User {
|
||||
notifications InAppNotification[] @relation("UserNotifications")
|
||||
notificationSettingsUpdated NotificationEmailSetting[] @relation("NotificationSettingUpdater")
|
||||
|
||||
// Reminder logs
|
||||
reminderLogs ReminderLog[]
|
||||
|
||||
// Conflict of interest
|
||||
conflictsOfInterest ConflictOfInterest[]
|
||||
coiReviews ConflictOfInterest[] @relation("COIReviewedBy")
|
||||
|
||||
// Evaluation summaries
|
||||
generatedSummaries EvaluationSummary[] @relation("EvaluationSummaryGeneratedBy")
|
||||
|
||||
// Mentor messages
|
||||
mentorMessages MentorMessage[] @relation("MentorMessageSender")
|
||||
|
||||
// NextAuth relations
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
@@ -377,6 +390,8 @@ model Round {
|
||||
filteringJobs FilteringJob[]
|
||||
assignmentJobs AssignmentJob[]
|
||||
taggingJobs TaggingJob[]
|
||||
reminderLogs ReminderLog[]
|
||||
projectFiles ProjectFile[]
|
||||
|
||||
@@index([programId])
|
||||
@@index([status])
|
||||
@@ -480,6 +495,9 @@ model Project {
|
||||
awardVotes AwardVote[]
|
||||
wonAwards SpecialAward[] @relation("AwardWinner")
|
||||
projectTags ProjectTag[]
|
||||
statusHistory ProjectStatusHistory[]
|
||||
mentorMessages MentorMessage[]
|
||||
evaluationSummaries EvaluationSummary[]
|
||||
|
||||
@@index([roundId])
|
||||
@@index([status])
|
||||
@@ -494,6 +512,7 @@ model Project {
|
||||
model ProjectFile {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String? // Which round this file was submitted for
|
||||
|
||||
// File info
|
||||
fileType FileType
|
||||
@@ -505,13 +524,17 @@ model ProjectFile {
|
||||
bucket String
|
||||
objectKey String
|
||||
|
||||
isLate Boolean @default(false) // Uploaded after round deadline
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
round Round? @relation(fields: [roundId], references: [id])
|
||||
|
||||
@@unique([bucket, objectKey])
|
||||
@@index([projectId])
|
||||
@@index([roundId])
|
||||
@@index([fileType])
|
||||
}
|
||||
|
||||
@@ -539,10 +562,11 @@ model Assignment {
|
||||
createdBy String? // Admin who created the assignment
|
||||
|
||||
// Relations
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
|
||||
evaluation Evaluation?
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
|
||||
evaluation Evaluation?
|
||||
conflictOfInterest ConflictOfInterest?
|
||||
|
||||
@@unique([userId, projectId, roundId])
|
||||
@@index([userId])
|
||||
@@ -1297,3 +1321,109 @@ model AwardVote {
|
||||
@@index([userId])
|
||||
@@index([projectId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// REMINDER LOG (Evaluation Deadline Reminders)
|
||||
// =============================================================================
|
||||
|
||||
model ReminderLog {
|
||||
id String @id @default(cuid())
|
||||
roundId String
|
||||
userId String
|
||||
type String // "3_DAYS", "24H", "1H"
|
||||
sentAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([roundId, userId, type])
|
||||
@@index([roundId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CONFLICT OF INTEREST
|
||||
// =============================================================================
|
||||
|
||||
model ConflictOfInterest {
|
||||
id String @id @default(cuid())
|
||||
assignmentId String @unique
|
||||
userId String
|
||||
projectId String
|
||||
roundId String
|
||||
hasConflict Boolean @default(false)
|
||||
conflictType String? // "financial", "personal", "organizational", "other"
|
||||
description String? @db.Text
|
||||
declaredAt DateTime @default(now())
|
||||
|
||||
// Admin review
|
||||
reviewedById String?
|
||||
reviewedAt DateTime?
|
||||
reviewAction String? // "cleared", "reassigned", "noted"
|
||||
|
||||
// Relations
|
||||
assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
reviewedBy User? @relation("COIReviewedBy", fields: [reviewedById], references: [id])
|
||||
|
||||
@@index([userId])
|
||||
@@index([roundId, hasConflict])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AI EVALUATION SUMMARY
|
||||
// =============================================================================
|
||||
|
||||
model EvaluationSummary {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String
|
||||
summaryJson Json @db.JsonB
|
||||
generatedAt DateTime @default(now())
|
||||
generatedById String
|
||||
model String
|
||||
tokensUsed Int
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
generatedBy User @relation("EvaluationSummaryGeneratedBy", fields: [generatedById], references: [id])
|
||||
|
||||
@@unique([projectId, roundId])
|
||||
@@index([roundId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROJECT STATUS HISTORY
|
||||
// =============================================================================
|
||||
|
||||
model ProjectStatusHistory {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
status ProjectStatus
|
||||
changedAt DateTime @default(now())
|
||||
changedBy String?
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([projectId, changedAt])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MENTOR MESSAGES
|
||||
// =============================================================================
|
||||
|
||||
model MentorMessage {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
senderId String
|
||||
message String @db.Text
|
||||
isRead Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
sender User @relation("MentorMessageSender", fields: [senderId], references: [id])
|
||||
|
||||
@@index([projectId, createdAt])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user