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

@@ -418,7 +418,45 @@ function EditRoundContent({ roundId }: { roundId: string }) {
</CardContent>
</Card>
{/* Team Notification - removed from schema, feature not implemented */}
{/* Upload Deadline Policy */}
<Card>
<CardHeader>
<CardTitle className="text-lg">Upload Deadline Policy</CardTitle>
<CardDescription>
Control how file uploads are handled after the round starts
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Select
value={(roundSettings.uploadDeadlinePolicy as string) || ''}
onValueChange={(value) =>
setRoundSettings((prev) => ({
...prev,
uploadDeadlinePolicy: value || undefined,
}))
}
>
<SelectTrigger>
<SelectValue placeholder="Default (no restriction)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="NONE">
Default - No restriction
</SelectItem>
<SelectItem value="BLOCK">
Block uploads after round starts
</SelectItem>
<SelectItem value="ALLOW_LATE">
Allow late uploads (marked as late)
</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
When set to &ldquo;Block&rdquo;, applicants cannot upload files after the voting start date.
When set to &ldquo;Allow late&rdquo;, uploads are accepted but flagged as late submissions.
</p>
</CardContent>
</Card>
{/* Evaluation Criteria */}
<Card>