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

@@ -16,6 +16,7 @@ import { Badge } from '@/components/ui/badge'
import { Skeleton } from '@/components/ui/skeleton'
import { Separator } from '@/components/ui/separator'
import { FileViewer } from '@/components/shared/file-viewer'
import { MentorChat } from '@/components/shared/mentor-chat'
import { ProjectLogoWithUrl } from '@/components/shared/project-logo-with-url'
import {
ArrowLeft,
@@ -30,6 +31,7 @@ import {
Calendar,
FileText,
ExternalLink,
MessageSquare,
} from 'lucide-react'
import { formatDateOnly, getInitials } from '@/lib/utils'
@@ -52,6 +54,17 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
projectId,
})
const { data: mentorMessages, isLoading: messagesLoading } = trpc.mentor.getMessages.useQuery({
projectId,
})
const utils = trpc.useUtils()
const sendMessage = trpc.mentor.sendMessage.useMutation({
onSuccess: () => {
utils.mentor.getMessages.invalidate({ projectId })
},
})
if (isLoading) {
return <ProjectDetailSkeleton />
}
@@ -363,6 +376,30 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
)}
</CardContent>
</Card>
{/* Messaging Section */}
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<MessageSquare className="h-5 w-5" />
Messages
</CardTitle>
<CardDescription>
Communicate with the project team
</CardDescription>
</CardHeader>
<CardContent>
<MentorChat
messages={mentorMessages || []}
currentUserId={project.mentorAssignment?.mentor?.id || ''}
onSendMessage={async (message) => {
await sendMessage.mutateAsync({ projectId, message })
}}
isLoading={messagesLoading}
isSending={sendMessage.isPending}
/>
</CardContent>
</Card>
</div>
)
}