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:
2026-02-05 21:58:27 +01:00
parent 002a9dbfc3
commit 699248e40b
38 changed files with 5437 additions and 533 deletions

View File

@@ -370,6 +370,17 @@ export const projectRouter = router({
},
})
// Record status change in history
if (status) {
await ctx.prisma.projectStatusHistory.create({
data: {
projectId: id,
status,
changedBy: ctx.user.id,
},
})
}
// Send notifications if status changed
if (status) {
// Get round details for notification
@@ -648,6 +659,17 @@ export const projectRouter = router({
data: { status: input.status },
})
// Record status change in history for each project
if (matchingIds.length > 0) {
await ctx.prisma.projectStatusHistory.createMany({
data: matchingIds.map((projectId) => ({
projectId,
status: input.status,
changedBy: ctx.user.id,
})),
})
}
// Audit log
await logAudit({
prisma: ctx.prisma,