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:
@@ -112,6 +112,72 @@ The MOPC platform uses PostgreSQL as its primary database, accessed via Prisma O
|
||||
│ grantedBy │
|
||||
│ createdAt │
|
||||
└─────────────────────┘
|
||||
|
||||
┌─────────────────────┐
|
||||
│ ReminderLog │
|
||||
│─────────────────────│
|
||||
│ id │
|
||||
│ roundId │
|
||||
│ userId │
|
||||
│ assignmentId │
|
||||
│ type │
|
||||
│ sentAt │
|
||||
│ emailTo │
|
||||
│ status │
|
||||
└─────────────────────┘
|
||||
|
||||
┌───────────────────────┐
|
||||
│ ConflictOfInterest │
|
||||
│───────────────────────│
|
||||
│ id │
|
||||
│ assignmentId │
|
||||
│ userId │
|
||||
│ projectId │
|
||||
│ roundId │
|
||||
│ hasConflict │
|
||||
│ conflictType │
|
||||
│ description │
|
||||
│ reviewedBy │
|
||||
│ reviewedAt │
|
||||
│ reviewNotes │
|
||||
│ createdAt │
|
||||
└───────────────────────┘
|
||||
|
||||
┌───────────────────────┐
|
||||
│ EvaluationSummary │
|
||||
│───────────────────────│
|
||||
│ id │
|
||||
│ projectId │
|
||||
│ roundId │
|
||||
│ summaryJson │
|
||||
│ model │
|
||||
│ tokensUsed │
|
||||
│ createdAt │
|
||||
└───────────────────────┘
|
||||
|
||||
┌───────────────────────┐
|
||||
│ ProjectStatusHistory │
|
||||
│───────────────────────│
|
||||
│ id │
|
||||
│ projectId │
|
||||
│ oldStatus │
|
||||
│ newStatus │
|
||||
│ changedBy │
|
||||
│ reason │
|
||||
│ createdAt │
|
||||
└───────────────────────┘
|
||||
|
||||
┌───────────────────────┐
|
||||
│ MentorMessage │
|
||||
│───────────────────────│
|
||||
│ id │
|
||||
│ projectId │
|
||||
│ senderId │
|
||||
│ recipientId │
|
||||
│ content │
|
||||
│ readAt │
|
||||
│ createdAt │
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
## Prisma Schema
|
||||
@@ -137,6 +203,8 @@ enum UserRole {
|
||||
PROGRAM_ADMIN
|
||||
JURY_MEMBER
|
||||
OBSERVER
|
||||
MENTOR
|
||||
APPLICANT
|
||||
}
|
||||
|
||||
enum UserStatus {
|
||||
@@ -357,12 +425,14 @@ model Project {
|
||||
model ProjectFile {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String? // Per-round document management (Phase B)
|
||||
|
||||
// File info
|
||||
fileType FileType
|
||||
fileName String
|
||||
mimeType String
|
||||
size Int // bytes
|
||||
isLate Boolean @default(false) // Upload deadline policy tracking
|
||||
|
||||
// MinIO location
|
||||
bucket String
|
||||
@@ -376,6 +446,7 @@ model ProjectFile {
|
||||
// Indexes
|
||||
@@index([projectId])
|
||||
@@index([fileType])
|
||||
@@index([roundId])
|
||||
@@unique([bucket, objectKey])
|
||||
}
|
||||
|
||||
@@ -475,6 +546,119 @@ model AuditLog {
|
||||
@@index([entityType, entityId])
|
||||
@@index([timestamp])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// REMINDER LOGGING
|
||||
// =============================================================================
|
||||
|
||||
model ReminderLog {
|
||||
id String @id @default(cuid())
|
||||
roundId String
|
||||
userId String
|
||||
assignmentId String
|
||||
type String // e.g., "DEADLINE_APPROACHING", "OVERDUE"
|
||||
sentAt DateTime @default(now())
|
||||
emailTo String
|
||||
status String // "SENT", "FAILED"
|
||||
|
||||
// Indexes
|
||||
@@index([roundId])
|
||||
@@index([userId])
|
||||
@@index([assignmentId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CONFLICT OF INTEREST
|
||||
// =============================================================================
|
||||
|
||||
model ConflictOfInterest {
|
||||
id String @id @default(cuid())
|
||||
assignmentId String
|
||||
userId String
|
||||
projectId String
|
||||
roundId String
|
||||
hasConflict Boolean
|
||||
conflictType String? // e.g., "FINANCIAL", "PERSONAL", "ORGANIZATIONAL"
|
||||
description String? @db.Text
|
||||
|
||||
// Review fields (admin)
|
||||
reviewedBy String?
|
||||
reviewedAt DateTime?
|
||||
reviewNotes String? @db.Text
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Indexes
|
||||
@@unique([assignmentId])
|
||||
@@index([userId])
|
||||
@@index([roundId])
|
||||
@@index([hasConflict])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AI EVALUATION SUMMARY
|
||||
// =============================================================================
|
||||
|
||||
model EvaluationSummary {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
roundId String
|
||||
summaryJson Json @db.JsonB // Strengths, weaknesses, themes, scoring stats
|
||||
model String // e.g., "gpt-4o"
|
||||
tokensUsed Int
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Indexes
|
||||
@@unique([projectId, roundId])
|
||||
@@index([roundId])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROJECT STATUS HISTORY
|
||||
// =============================================================================
|
||||
|
||||
model ProjectStatusHistory {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
oldStatus String
|
||||
newStatus String
|
||||
changedBy String? // User who changed the status
|
||||
reason String? @db.Text
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Indexes
|
||||
@@index([projectId])
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MENTOR MESSAGING
|
||||
// =============================================================================
|
||||
|
||||
model MentorMessage {
|
||||
id String @id @default(cuid())
|
||||
projectId String
|
||||
senderId String
|
||||
recipientId String
|
||||
content String @db.Text
|
||||
readAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Indexes
|
||||
@@index([projectId])
|
||||
@@index([senderId])
|
||||
@@index([recipientId])
|
||||
@@index([createdAt])
|
||||
}
|
||||
```
|
||||
|
||||
## Indexing Strategy
|
||||
@@ -505,6 +689,22 @@ model AuditLog {
|
||||
| Evaluation | `submittedAt` | Sort by submission time |
|
||||
| AuditLog | `timestamp` | Time-based queries |
|
||||
| AuditLog | `entityType, entityId` | Entity history |
|
||||
| ProjectFile | `roundId` | Per-round document listing |
|
||||
| ReminderLog | `roundId` | Reminders per round |
|
||||
| ReminderLog | `userId` | User reminder history |
|
||||
| ReminderLog | `assignmentId` | Assignment reminder tracking |
|
||||
| ConflictOfInterest | `assignmentId` (unique) | COI per assignment |
|
||||
| ConflictOfInterest | `userId` | User COI declarations |
|
||||
| ConflictOfInterest | `roundId` | COI per round |
|
||||
| ConflictOfInterest | `hasConflict` | Filter active conflicts |
|
||||
| EvaluationSummary | `projectId, roundId` (unique) | One summary per project per round |
|
||||
| EvaluationSummary | `roundId` | Summaries per round |
|
||||
| ProjectStatusHistory | `projectId` | Status change timeline |
|
||||
| ProjectStatusHistory | `createdAt` | Chronological ordering |
|
||||
| MentorMessage | `projectId` | Messages per project |
|
||||
| MentorMessage | `senderId` | Sent messages |
|
||||
| MentorMessage | `recipientId` | Received messages |
|
||||
| MentorMessage | `createdAt` | Chronological ordering |
|
||||
|
||||
### JSON Field Indexes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user