Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react'
|
|
|
|
|
import { useParams, useRouter } from 'next/navigation'
|
2026-02-17 14:45:57 +01:00
|
|
|
import { ArrowLeft, Loader2, PlayCircle, Zap } from 'lucide-react'
|
|
|
|
|
import { toast } from 'sonner'
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
import { trpc } from '@/lib/trpc/client'
|
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
|
|
|
import {
|
|
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
|
|
|
|
} from '@/components/ui/select'
|
|
|
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
|
|
|
|
import { CoverageReport } from '@/components/admin/assignment/coverage-report'
|
|
|
|
|
import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet'
|
|
|
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
|
|
|
|
|
|
|
|
export default function AssignmentsDashboardPage() {
|
|
|
|
|
const params = useParams()
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
const competitionId = params.competitionId as string
|
|
|
|
|
|
|
|
|
|
const [selectedRoundId, setSelectedRoundId] = useState<string>('')
|
|
|
|
|
const [previewSheetOpen, setPreviewSheetOpen] = useState(false)
|
|
|
|
|
|
2026-02-17 14:45:57 +01:00
|
|
|
const aiAssignmentMutation = trpc.roundAssignment.aiPreview.useMutation({
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
toast.success('AI assignments ready!', {
|
|
|
|
|
action: { label: 'Review', onClick: () => setPreviewSheetOpen(true) },
|
|
|
|
|
duration: 10000,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
onError: (err) => toast.error(`AI generation failed: ${err.message}`),
|
|
|
|
|
})
|
|
|
|
|
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
const { data: competition, isLoading: isLoadingCompetition } = trpc.competition.getById.useQuery({
|
|
|
|
|
id: competitionId,
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-17 12:33:20 +01:00
|
|
|
const { data: selectedRound } = trpc.round.getById.useQuery(
|
|
|
|
|
{ id: selectedRoundId },
|
|
|
|
|
{ enabled: !!selectedRoundId }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const requiredReviews = (selectedRound?.configJson as Record<string, unknown>)?.requiredReviewsPerProject as number || 3
|
|
|
|
|
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
const { data: unassignedQueue, isLoading: isLoadingQueue } =
|
|
|
|
|
trpc.roundAssignment.unassignedQueue.useQuery(
|
2026-02-17 12:33:20 +01:00
|
|
|
{ roundId: selectedRoundId, requiredReviews },
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
{ enabled: !!selectedRoundId }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const rounds = competition?.rounds || []
|
|
|
|
|
const currentRound = rounds.find((r) => r.id === selectedRoundId)
|
|
|
|
|
|
|
|
|
|
if (isLoadingCompetition) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="container mx-auto space-y-6 p-4 sm:p-6">
|
|
|
|
|
<Skeleton className="h-10 w-full" />
|
|
|
|
|
<Skeleton className="h-96 w-full" />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!competition) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="container mx-auto space-y-6 p-4 sm:p-6">
|
|
|
|
|
<p>Competition not found</p>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="container mx-auto space-y-6 p-4 sm:p-6">
|
|
|
|
|
<Button variant="ghost" onClick={() => router.back()} className="mb-4" aria-label="Back to competition details">
|
|
|
|
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
|
|
|
Back to Competition
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-3xl font-bold">Assignment Dashboard</h1>
|
|
|
|
|
<p className="text-muted-foreground">Manage jury assignments for rounds</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Select Round</CardTitle>
|
|
|
|
|
<CardDescription>Choose a round to view and manage assignments</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<Select value={selectedRoundId} onValueChange={setSelectedRoundId}>
|
|
|
|
|
<SelectTrigger className="w-full sm:w-[300px]">
|
|
|
|
|
<SelectValue placeholder="Select a round..." />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{rounds.length === 0 ? (
|
|
|
|
|
<div className="px-2 py-1 text-sm text-muted-foreground">No rounds available</div>
|
|
|
|
|
) : (
|
|
|
|
|
rounds.map((round) => (
|
|
|
|
|
<SelectItem key={round.id} value={round.id}>
|
|
|
|
|
{round.name} ({round.roundType})
|
|
|
|
|
</SelectItem>
|
|
|
|
|
))
|
|
|
|
|
)}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{selectedRoundId && (
|
|
|
|
|
<div className="space-y-6">
|
2026-02-17 14:45:57 +01:00
|
|
|
<div className="flex justify-end gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
aiAssignmentMutation.mutate({ roundId: selectedRoundId, requiredReviews })
|
|
|
|
|
}}
|
|
|
|
|
disabled={aiAssignmentMutation.isPending}
|
|
|
|
|
>
|
|
|
|
|
{aiAssignmentMutation.isPending ? (
|
|
|
|
|
<><Loader2 className="mr-2 h-4 w-4 animate-spin" />Generating...</>
|
|
|
|
|
) : (
|
|
|
|
|
<><Zap className="mr-2 h-4 w-4" />{aiAssignmentMutation.data ? 'Regenerate' : 'Generate with AI'}</>
|
|
|
|
|
)}
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
</Button>
|
2026-02-17 14:45:57 +01:00
|
|
|
{aiAssignmentMutation.data && (
|
|
|
|
|
<Button variant="outline" onClick={() => setPreviewSheetOpen(true)}>
|
|
|
|
|
Review Assignments
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Tabs defaultValue="coverage" className="w-full">
|
|
|
|
|
<TabsList className="grid w-full grid-cols-2">
|
|
|
|
|
<TabsTrigger value="coverage">Coverage Report</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="unassigned">Unassigned Queue</TabsTrigger>
|
|
|
|
|
</TabsList>
|
|
|
|
|
|
|
|
|
|
<TabsContent value="coverage" className="mt-6">
|
2026-02-17 12:33:20 +01:00
|
|
|
<CoverageReport roundId={selectedRoundId} requiredReviews={requiredReviews} />
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
</TabsContent>
|
|
|
|
|
|
|
|
|
|
<TabsContent value="unassigned" className="mt-6">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Unassigned Projects</CardTitle>
|
|
|
|
|
<CardDescription>
|
2026-02-17 12:33:20 +01:00
|
|
|
Projects with fewer than {requiredReviews} assignments
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
{isLoadingQueue ? (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{[1, 2, 3].map((i) => (
|
|
|
|
|
<Skeleton key={i} className="h-16 w-full" />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : unassignedQueue && unassignedQueue.length > 0 ? (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{unassignedQueue.map((project: any) => (
|
|
|
|
|
<div
|
|
|
|
|
key={project.id}
|
|
|
|
|
className="flex justify-between items-center p-3 border rounded-md"
|
|
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<p className="font-medium">{project.title}</p>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
{project.competitionCategory || 'No category'}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-sm text-muted-foreground">
|
2026-02-17 12:33:20 +01:00
|
|
|
{project.assignmentCount || 0} / {requiredReviews} assignments
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
All projects have sufficient assignments
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
|
|
<AssignmentPreviewSheet
|
|
|
|
|
roundId={selectedRoundId}
|
|
|
|
|
open={previewSheetOpen}
|
|
|
|
|
onOpenChange={setPreviewSheetOpen}
|
2026-02-17 12:33:20 +01:00
|
|
|
requiredReviews={requiredReviews}
|
2026-02-17 14:45:57 +01:00
|
|
|
aiResult={aiAssignmentMutation.data ?? null}
|
|
|
|
|
isAIGenerating={aiAssignmentMutation.isPending}
|
|
|
|
|
onGenerateAI={() => aiAssignmentMutation.mutate({ roundId: selectedRoundId, requiredReviews })}
|
|
|
|
|
onResetAI={() => aiAssignmentMutation.reset()}
|
Competition/Round architecture: full platform rewrite (Phases 1-9)
Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:04:15 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|