From 2df9c54de2f9bf625967cb892bd24b5cbcd21842 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 2 Mar 2026 12:48:08 +0100 Subject: [PATCH] fix: backfill binaryDecision, fix boolean criterion lookup, add assign buttons - Backfilled 166 evaluations' binaryDecision from criterionScoresJson on production DB - Fixed roundEvaluationScores and ai-ranking to look in EvaluationForm.criteriaJson instead of round.configJson for the boolean "Move to the Next Stage?" criterion - Added advanceMode (count/threshold) toggle to round config Advancement Targets - Added "Assign to Jurors" button on Unassigned Projects section and Projects tab bulk bar Co-Authored-By: Claude Opus 4.6 --- .../(admin)/admin/rounds/[roundId]/page.tsx | 142 ++++++++++++------ .../assignment/round-unassigned-queue.tsx | 19 ++- .../admin/round/project-states-table.tsx | 14 +- src/server/routers/ranking.ts | 19 +-- src/server/services/ai-ranking.ts | 16 +- src/types/competition-configs.ts | 2 + 6 files changed, 142 insertions(+), 70 deletions(-) diff --git a/src/app/(admin)/admin/rounds/[roundId]/page.tsx b/src/app/(admin)/admin/rounds/[roundId]/page.tsx index 08696cd..c3b5514 100644 --- a/src/app/(admin)/admin/rounds/[roundId]/page.tsx +++ b/src/app/(admin)/admin/rounds/[roundId]/page.tsx @@ -1434,7 +1434,10 @@ export default function RoundDetailPage() { {/* ═══════════ PROJECTS TAB ═══════════ */} - + { + setActiveTab('assignments') + setTimeout(() => setPreviewSheetOpen(true), 100) + }} /> {/* ═══════════ FILTERING TAB ═══════════ */} @@ -1797,7 +1800,7 @@ export default function RoundDetailPage() { - + setPreviewSheetOpen(true)} /> @@ -2157,50 +2160,103 @@ export default function RoundDetailPage() {
- {isEvaluation && !(config.startupAdvanceCount as number) && !(config.conceptAdvanceCount as number) && ( -
-

Advancement targets not configured — all passed projects will be eligible to advance.

-
- )}

- Target number of projects per category to advance from this round + How to determine which projects advance from this round

-
-
- - { - const val = e.target.value ? parseInt(e.target.value, 10) : undefined - handleConfigChange({ ...config, startupAdvanceCount: val }) - }} - /> -
-
- - { - const val = e.target.value ? parseInt(e.target.value, 10) : undefined - handleConfigChange({ ...config, conceptAdvanceCount: val }) - }} - /> -
+ + {/* Mode toggle */} +
+ +
+ + {(config.advanceMode as string) === 'threshold' ? ( +
+ +

+ All projects scoring at or above this threshold will advance (both categories) +

+ { + const val = e.target.value ? parseFloat(e.target.value) : undefined + handleConfigChange({ ...config, advanceScoreThreshold: val }) + }} + /> +
+ ) : ( + <> + {isEvaluation && !(config.startupAdvanceCount as number) && !(config.conceptAdvanceCount as number) && ( +
+

Advancement targets not configured — all passed projects will be eligible to advance.

+
+ )} +

+ Target number of projects per category to advance +

+
+
+ + { + const val = e.target.value ? parseInt(e.target.value, 10) : undefined + handleConfigChange({ ...config, startupAdvanceCount: val }) + }} + /> +
+
+ + { + const val = e.target.value ? parseInt(e.target.value, 10) : undefined + handleConfigChange({ ...config, conceptAdvanceCount: val }) + }} + /> +
+
+ + )}
diff --git a/src/components/admin/assignment/round-unassigned-queue.tsx b/src/components/admin/assignment/round-unassigned-queue.tsx index ec3e39b..36d0134 100644 --- a/src/components/admin/assignment/round-unassigned-queue.tsx +++ b/src/components/admin/assignment/round-unassigned-queue.tsx @@ -1,16 +1,19 @@ 'use client' +import { Users } from 'lucide-react' import { trpc } from '@/lib/trpc/client' import { cn } from '@/lib/utils' import { Skeleton } from '@/components/ui/skeleton' import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' export type RoundUnassignedQueueProps = { roundId: string requiredReviews?: number + onAssignUnassigned?: () => void } -export function RoundUnassignedQueue({ roundId, requiredReviews = 3 }: RoundUnassignedQueueProps) { +export function RoundUnassignedQueue({ roundId, requiredReviews = 3, onAssignUnassigned }: RoundUnassignedQueueProps) { const { data: unassigned, isLoading } = trpc.roundAssignment.unassignedQueue.useQuery( { roundId, requiredReviews }, { refetchInterval: 15_000 }, @@ -18,9 +21,17 @@ export function RoundUnassignedQueue({ roundId, requiredReviews = 3 }: RoundUnas return (
-
-

Unassigned Projects

-

Projects with fewer than {requiredReviews} jury assignments

+
+
+

Unassigned Projects

+

Projects with fewer than {requiredReviews} jury assignments

+
+ {unassigned && unassigned.length > 0 && onAssignUnassigned && ( + + )}
{isLoading ? ( diff --git a/src/components/admin/round/project-states-table.tsx b/src/components/admin/round/project-states-table.tsx index d450357..48a8e6c 100644 --- a/src/components/admin/round/project-states-table.tsx +++ b/src/components/admin/round/project-states-table.tsx @@ -59,6 +59,7 @@ import { Search, ExternalLink, Sparkles, + Users, } from 'lucide-react' import Link from 'next/link' import type { Route } from 'next' @@ -78,9 +79,10 @@ const stateConfig: Record void } -export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTableProps) { +export function ProjectStatesTable({ competitionId, roundId, onAssignProjects }: ProjectStatesTableProps) { const [selectedIds, setSelectedIds] = useState>(new Set()) const [stateFilter, setStateFilter] = useState('ALL') const [searchQuery, setSearchQuery] = useState('') @@ -321,6 +323,16 @@ export function ProjectStatesTable({ competitionId, roundId }: ProjectStatesTabl Change State + {onAssignProjects && ( + + )}