Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
'use client'
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
import { useState , useMemo , useCallback , useRef , useEffect } from 'react'
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
import { useParams , useSearchParams } from 'next/navigation'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import Link from 'next/link'
import type { Route } from 'next'
import { trpc } from '@/lib/trpc/client'
import { toast } from 'sonner'
2026-02-16 09:20:02 +01:00
import { cn } from '@/lib/utils'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import { Button } from '@/components/ui/button'
2026-02-16 09:20:02 +01:00
import { Card , CardContent , CardDescription , CardHeader , CardTitle } from '@/components/ui/card'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import { Tabs , TabsContent , TabsList , TabsTrigger } from '@/components/ui/tabs'
import { Skeleton } from '@/components/ui/skeleton'
import { Badge } from '@/components/ui/badge'
Rounds overhaul: full CRUD submission windows, scheduling UI, analytics, design refresh
- Fix special award FK crash: replace 4x raw auditLog.create with logAudit() helper
- Add updateSubmissionWindow + deleteSubmissionWindow mutations to round router
- Add per-round analytics (_count, juryGroup) to competition.getById
- Remove redundant acceptedCategories from intake config
- Rewrite submission window manager with full CRUD, all fields, date pickers
- Add round scheduling card (open/close dates) to round detail page
- Add project count, assignment count, jury group to round list cards
- Visual redesign: pipeline view, brand colors, progress bars, enhanced cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 07:07:09 +01:00
import { Input } from '@/components/ui/input'
2026-02-16 09:20:02 +01:00
import { Switch } from '@/components/ui/switch'
import { Label } from '@/components/ui/label'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import {
DropdownMenu ,
DropdownMenuContent ,
DropdownMenuItem ,
DropdownMenuSeparator ,
DropdownMenuTrigger ,
} from '@/components/ui/dropdown-menu'
2026-02-16 09:20:02 +01:00
import {
AlertDialog ,
AlertDialogAction ,
AlertDialogCancel ,
AlertDialogContent ,
AlertDialogDescription ,
AlertDialogFooter ,
AlertDialogHeader ,
AlertDialogTitle ,
AlertDialogTrigger ,
} from '@/components/ui/alert-dialog'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import {
Dialog ,
DialogContent ,
DialogDescription ,
DialogFooter ,
DialogHeader ,
DialogTitle ,
} from '@/components/ui/dialog'
2026-02-16 09:20:02 +01:00
import {
Select ,
SelectContent ,
SelectItem ,
SelectTrigger ,
SelectValue ,
} from '@/components/ui/select'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import {
ArrowLeft ,
2026-02-23 16:08:46 +01:00
ArrowRightLeft ,
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
Save ,
Loader2 ,
ChevronDown ,
2026-02-24 17:44:55 +01:00
ChevronRight ,
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
Play ,
Square ,
Archive ,
2026-02-16 09:20:02 +01:00
Layers ,
Rounds overhaul: full CRUD submission windows, scheduling UI, analytics, design refresh
- Fix special award FK crash: replace 4x raw auditLog.create with logAudit() helper
- Add updateSubmissionWindow + deleteSubmissionWindow mutations to round router
- Add per-round analytics (_count, juryGroup) to competition.getById
- Remove redundant acceptedCategories from intake config
- Rewrite submission window manager with full CRUD, all fields, date pickers
- Add round scheduling card (open/close dates) to round detail page
- Add project count, assignment count, jury group to round list cards
- Visual redesign: pipeline view, brand colors, progress bars, enhanced cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 07:07:09 +01:00
Users ,
2026-02-16 09:20:02 +01:00
CalendarDays ,
BarChart3 ,
ClipboardList ,
Settings ,
Zap ,
Shield ,
2026-02-23 16:08:46 +01:00
Mail ,
Shuffle ,
2026-02-16 09:20:02 +01:00
UserPlus ,
CheckCircle2 ,
AlertTriangle ,
Trophy ,
Download ,
Plus ,
Trash2 ,
ArrowRight ,
2026-02-16 12:06:07 +01:00
RotateCcw ,
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
} from 'lucide-react'
2026-02-19 12:59:35 +01:00
import {
Tooltip ,
TooltipContent ,
TooltipProvider ,
TooltipTrigger ,
} from '@/components/ui/tooltip'
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
import { RoundConfigForm } from '@/components/admin/competition/round-config-form'
import { ProjectStatesTable } from '@/components/admin/round/project-states-table'
2026-02-16 16:43:23 +01:00
// SubmissionWindowManager removed — round dates + file requirements in Config are sufficient
2026-02-16 09:20:02 +01:00
import { FileRequirementsEditor } from '@/components/admin/round/file-requirements-editor'
import { FilteringDashboard } from '@/components/admin/round/filtering-dashboard'
2026-02-27 09:41:59 +01:00
import { RankingDashboard } from '@/components/admin/round/ranking-dashboard'
2026-02-16 09:20:02 +01:00
import { CoverageReport } from '@/components/admin/assignment/coverage-report'
import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet'
2026-02-16 12:38:28 +01:00
import { AnimatedCard } from '@/components/shared/animated-container'
2026-02-16 16:43:23 +01:00
import { DateTimePicker } from '@/components/ui/datetime-picker'
2026-02-16 12:46:01 +01:00
import { AddMemberDialog } from '@/components/admin/jury/add-member-dialog'
2026-02-16 12:38:28 +01:00
import { motion } from 'motion/react'
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
import {
roundTypeConfig as sharedRoundTypeConfig ,
roundStatusConfig as sharedRoundStatusConfig ,
projectStateConfig ,
} from '@/lib/round-config'
import { InlineMemberCap } from '@/components/admin/jury/inline-member-cap'
import { RoundUnassignedQueue } from '@/components/admin/assignment/round-unassigned-queue'
import { JuryProgressTable } from '@/components/admin/assignment/jury-progress-table'
2026-02-23 16:08:46 +01:00
import { TransferAssignmentsDialog } from '@/components/admin/assignment/transfer-assignments-dialog'
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
import { ReassignmentHistory } from '@/components/admin/assignment/reassignment-history'
import { ScoreDistribution } from '@/components/admin/round/score-distribution'
import { SendRemindersButton } from '@/components/admin/assignment/send-reminders-button'
import { NotifyJurorsButton } from '@/components/admin/assignment/notify-jurors-button'
import { ExportEvaluationsDialog } from '@/components/admin/round/export-evaluations-dialog'
import { IndividualAssignmentsTable } from '@/components/admin/assignment/individual-assignments-table'
import { AdvanceProjectsDialog } from '@/components/admin/round/advance-projects-dialog'
import { AIRecommendationsDisplay } from '@/components/admin/round/ai-recommendations-display'
import { EvaluationCriteriaEditor } from '@/components/admin/round/evaluation-criteria-editor'
import { COIReviewSection } from '@/components/admin/assignment/coi-review-section'
import { ConfigSectionHeader } from '@/components/admin/rounds/config/config-section-header'
2026-02-25 15:19:30 +01:00
import { AdvancementSummaryCard } from '@/components/admin/round/advancement-summary-card'
2026-02-16 09:20:02 +01:00
2026-02-19 12:59:35 +01:00
// ── Helpers ────────────────────────────────────────────────────────────────
function getRelativeTime ( date : Date ) : string {
const now = new Date ( )
const diffMs = date . getTime ( ) - now . getTime ( )
const absDiffMs = Math . abs ( diffMs )
const minutes = Math . floor ( absDiffMs / 60 _000 )
const hours = Math . floor ( absDiffMs / 3 _600_000 )
const days = Math . floor ( absDiffMs / 86 _400_000 )
const label = days > 0 ? ` ${ days } d ` : hours > 0 ? ` ${ hours } h ` : ` ${ minutes } m `
return diffMs > 0 ? ` in ${ label } ` : ` ${ label } ago `
}
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
// ── Status & type config maps (from shared lib) ────────────────────────────
const roundStatusConfig = sharedRoundStatusConfig
const roundTypeConfig = sharedRoundTypeConfig
const stateColors : Record < string , string > = Object . fromEntries (
Object . entries ( projectStateConfig ) . map ( ( [ k , v ] ) = > [ k , v . bg ] )
)
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-16 09:20:02 +01:00
// ═══════════════════════════════════════════════════════════════════════════
// Main Page Component
// ═══════════════════════════════════════════════════════════════════════════
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
export default function RoundDetailPage() {
const params = useParams ( )
const roundId = params . roundId as string
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
const searchParams = useSearchParams ( )
const backUrl = searchParams . get ( 'from' )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
const [ config , setConfig ] = useState < Record < string , unknown > > ( { } )
2026-02-17 12:33:20 +01:00
const [ autosaveStatus , setAutosaveStatus ] = useState < 'idle' | 'saving' | 'saved' | 'error' > ( 'idle' )
2026-02-16 09:20:02 +01:00
const [ activeTab , setActiveTab ] = useState ( 'overview' )
const [ previewSheetOpen , setPreviewSheetOpen ] = useState ( false )
2026-02-17 14:45:57 +01:00
// AI assignment generation (lifted to page level so it persists when sheet closes)
const aiAssignmentMutation = trpc . roundAssignment . aiPreview . useMutation ( {
onSuccess : ( ) = > {
toast . success ( 'AI assignments ready!' , {
action : {
label : 'Review' ,
onClick : ( ) = > setPreviewSheetOpen ( true ) ,
} ,
duration : 10000 ,
} )
} ,
onError : ( err ) = > {
2026-02-18 17:24:16 +01:00
toast . error ( ` AI generation failed: ${ err . message } ` , { duration : 15000 } )
console . error ( '[AI Assignment]' , err )
2026-02-17 14:45:57 +01:00
} ,
} )
2026-02-16 09:20:02 +01:00
const [ exportOpen , setExportOpen ] = useState ( false )
const [ advanceDialogOpen , setAdvanceDialogOpen ] = useState ( false )
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
const [ aiRecommendations , setAiRecommendations ] = useState < {
STARTUP : Array < { projectId : string ; rank : number ; score : number ; category : string ; strengths : string [ ] ; concerns : string [ ] ; recommendation : string } >
BUSINESS_CONCEPT : Array < { projectId : string ; rank : number ; score : number ; category : string ; strengths : string [ ] ; concerns : string [ ] ; recommendation : string } >
} | null > ( null )
const [ shortlistDialogOpen , setShortlistDialogOpen ] = useState ( false )
2026-02-16 12:46:01 +01:00
const [ createJuryOpen , setCreateJuryOpen ] = useState ( false )
const [ newJuryName , setNewJuryName ] = useState ( '' )
const [ addMemberOpen , setAddMemberOpen ] = useState ( false )
2026-02-16 16:43:23 +01:00
const [ closeAndAdvance , setCloseAndAdvance ] = useState ( false )
2026-02-19 12:59:35 +01:00
const [ editingName , setEditingName ] = useState ( false )
const [ nameValue , setNameValue ] = useState ( '' )
const nameInputRef = useRef < HTMLInputElement > ( null )
const [ statusConfirmAction , setStatusConfirmAction ] = useState < 'activate' | 'close' | 'reopen' | 'archive' | null > ( null )
2026-03-03 09:43:28 +01:00
const [ statusDropdownOpen , setStatusDropdownOpen ] = useState ( false )
2026-02-24 17:44:55 +01:00
const [ coverageOpen , setCoverageOpen ] = useState ( false )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
const utils = trpc . useUtils ( )
2026-02-16 09:20:02 +01:00
// ── Core data queries ──────────────────────────────────────────────────
2026-02-16 09:30:19 +01:00
const { data : round , isLoading } = trpc . round . getById . useQuery (
{ id : roundId } ,
2026-02-16 12:06:07 +01:00
{ refetchInterval : 15_000 , refetchOnWindowFocus : true } ,
2026-02-16 09:30:19 +01:00
)
const { data : projectStates } = trpc . roundEngine . getProjectStates . useQuery (
{ roundId } ,
2026-02-16 12:06:07 +01:00
{ refetchInterval : 10_000 , refetchOnWindowFocus : true } ,
2026-02-16 09:30:19 +01:00
)
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-16 09:20:02 +01:00
const competitionId = round ? . competitionId ? ? ''
const { data : juryGroups } = trpc . juryGroup . list . useQuery (
{ competitionId } ,
2026-02-16 12:06:07 +01:00
{ enabled : ! ! competitionId , refetchInterval : 30_000 , refetchOnWindowFocus : true } ,
2026-02-16 09:30:19 +01:00
)
const { data : fileRequirements } = trpc . file . listRequirements . useQuery (
{ roundId } ,
2026-02-16 12:06:07 +01:00
{ refetchInterval : 15_000 , refetchOnWindowFocus : true } ,
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
)
// Fetch awards linked to this round
2026-02-16 09:20:02 +01:00
const { data : competition } = trpc . competition . getById . useQuery (
{ id : competitionId } ,
2026-02-16 09:30:19 +01:00
{ enabled : ! ! competitionId , refetchInterval : 60_000 } ,
2026-02-16 09:20:02 +01:00
)
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
const programId = competition ? . programId
const { data : awards } = trpc . specialAward . list . useQuery (
{ programId : programId ! } ,
2026-02-16 09:30:19 +01:00
{ enabled : ! ! programId , refetchInterval : 60_000 } ,
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
)
2026-02-16 09:20:02 +01:00
const roundAwards = awards ? . filter ( ( a ) = > a . evaluationRoundId === roundId ) ? ? [ ]
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-24 17:44:55 +01:00
// Jury workload (for assignments tab coverage auto-open logic)
const { data : juryWorkload } = trpc . analytics . getJurorWorkload . useQuery (
{ roundId } ,
{ enabled : round?.roundType === 'EVALUATION' , refetchInterval : 15_000 } ,
)
2026-02-17 16:25:59 +01:00
// Filtering results stats (only for FILTERING rounds)
const { data : filteringStats } = trpc . filtering . getResultStats . useQuery (
{ roundId } ,
{ enabled : round?.roundType === 'FILTERING' , refetchInterval : 5_000 } ,
)
2026-02-17 18:53:51 +01:00
// Initialize config from server on load; re-sync after saves
2026-02-17 16:43:47 +01:00
const serverConfig = useMemo ( ( ) = > ( round ? . configJson as Record < string , unknown > ) ? ? { } , [ round ? . configJson ] )
const configInitialized = useRef ( false )
2026-02-17 18:53:51 +01:00
const savingRef = useRef ( false )
// Sync local config with server: on initial load AND whenever serverConfig
// changes after a save completes (so Zod-applied defaults get picked up)
useEffect ( ( ) = > {
if ( ! round ) return
if ( ! configInitialized . current ) {
configInitialized . current = true
setConfig ( serverConfig )
} else if ( ! savingRef . current ) {
// Server changed (e.g. after save invalidation) — re-sync
setConfig ( serverConfig )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
}
2026-02-17 18:53:51 +01:00
} , [ serverConfig , round ] )
2026-02-17 16:43:47 +01:00
const hasUnsavedConfig = useMemo (
( ) = > configInitialized . current && JSON . stringify ( config ) !== JSON . stringify ( serverConfig ) ,
[ config , serverConfig ] ,
)
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-24 17:44:55 +01:00
// Auto-open coverage section when no assignments exist yet
useEffect ( ( ) = > {
if ( juryWorkload && juryWorkload . length === 0 ) {
setCoverageOpen ( true )
}
} , [ juryWorkload ] )
2026-02-16 09:20:02 +01:00
// ── Mutations ──────────────────────────────────────────────────────────
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
const updateMutation = trpc . round . update . useMutation ( {
onSuccess : ( ) = > {
2026-02-17 18:53:51 +01:00
savingRef . current = false
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
utils . round . getById . invalidate ( { id : roundId } )
2026-02-17 12:33:20 +01:00
setAutosaveStatus ( 'saved' )
setTimeout ( ( ) = > setAutosaveStatus ( 'idle' ) , 2000 )
} ,
onError : ( err ) = > {
2026-02-17 18:53:51 +01:00
savingRef . current = false
2026-02-17 12:33:20 +01:00
setAutosaveStatus ( 'error' )
toast . error ( err . message )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
} ,
} )
const activateMutation = trpc . roundEngine . activate . useMutation ( {
onSuccess : ( ) = > {
utils . round . getById . invalidate ( { id : roundId } )
toast . success ( 'Round activated' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
const closeMutation = trpc . roundEngine . close . useMutation ( {
onSuccess : ( ) = > {
utils . round . getById . invalidate ( { id : roundId } )
toast . success ( 'Round closed' )
2026-02-16 16:43:23 +01:00
if ( closeAndAdvance ) {
setCloseAndAdvance ( false )
// Small delay to let cache invalidation complete before opening dialog
setTimeout ( ( ) = > setAdvanceDialogOpen ( true ) , 300 )
}
} ,
onError : ( err ) = > {
setCloseAndAdvance ( false )
toast . error ( err . message )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
} ,
} )
2026-02-16 12:06:07 +01:00
const reopenMutation = trpc . roundEngine . reopen . useMutation ( {
onSuccess : ( data ) = > {
utils . round . getById . invalidate ( { id : roundId } )
utils . roundEngine . getProjectStates . invalidate ( { roundId } )
const msg = data . pausedRounds ? . length
? ` Round reopened. Paused: ${ data . pausedRounds . join ( ', ' ) } `
: 'Round reopened'
toast . success ( msg )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
const archiveMutation = trpc . roundEngine . archive . useMutation ( {
onSuccess : ( ) = > {
utils . round . getById . invalidate ( { id : roundId } )
toast . success ( 'Round archived' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
2026-02-16 09:20:02 +01:00
const assignJuryMutation = trpc . round . update . useMutation ( {
onSuccess : ( ) = > {
utils . round . getById . invalidate ( { id : roundId } )
2026-02-16 12:46:01 +01:00
utils . juryGroup . list . invalidate ( { competitionId } )
2026-02-16 09:20:02 +01:00
toast . success ( 'Jury group updated' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
2026-02-16 12:46:01 +01:00
// Jury group detail query (for the assigned group)
const juryGroupId = round ? . juryGroupId ? ? ''
const { data : juryGroupDetail } = trpc . juryGroup . getById . useQuery (
{ id : juryGroupId } ,
{ enabled : ! ! juryGroupId , refetchInterval : 10_000 } ,
)
const createJuryMutation = trpc . juryGroup . create . useMutation ( {
onSuccess : ( newGroup ) = > {
utils . juryGroup . list . invalidate ( { competitionId } )
// Auto-assign the new jury group to this round
assignJuryMutation . mutate ( { id : roundId , juryGroupId : newGroup.id } )
toast . success ( ` Jury " ${ newGroup . name } " created and assigned ` )
setCreateJuryOpen ( false )
setNewJuryName ( '' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
const deleteJuryMutation = trpc . juryGroup . delete . useMutation ( {
onSuccess : ( result ) = > {
utils . juryGroup . list . invalidate ( { competitionId } )
utils . round . getById . invalidate ( { id : roundId } )
toast . success ( ` Jury " ${ result . name } " deleted ` )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
const removeJuryMemberMutation = trpc . juryGroup . removeMember . useMutation ( {
onSuccess : ( ) = > {
if ( juryGroupId ) utils . juryGroup . getById . invalidate ( { id : juryGroupId } )
toast . success ( 'Member removed' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
2026-02-18 18:23:54 +01:00
const updateJuryMemberMutation = trpc . juryGroup . updateMember . useMutation ( {
onSuccess : ( ) = > {
if ( juryGroupId ) utils . juryGroup . getById . invalidate ( { id : juryGroupId } )
toast . success ( 'Cap updated' )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
2026-02-23 16:08:46 +01:00
// Jury member quick actions (same as in JuryProgressTable)
const [ memberTransferJuror , setMemberTransferJuror ] = useState < { id : string ; name : string } | null > ( null )
const notifyMemberMutation = trpc . assignment . notifySingleJurorOfAssignments . useMutation ( {
onSuccess : ( data ) = > {
toast . success ( ` Notified juror of ${ data . projectCount } assignment(s) ` )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
const redistributeMemberMutation = trpc . assignment . redistributeJurorAssignments . useMutation ( {
onSuccess : ( data ) = > {
utils . assignment . listByStage . invalidate ( { roundId } )
utils . roundEngine . getProjectStates . invalidate ( { roundId } )
utils . analytics . getJurorWorkload . invalidate ( { roundId } )
utils . roundAssignment . unassignedQueue . invalidate ( { roundId } )
if ( data . failedCount > 0 ) {
toast . warning ( ` Reassigned ${ data . movedCount } project(s). ${ data . failedCount } could not be reassigned. ` )
} else {
toast . success ( ` Reassigned ${ data . movedCount } project(s) to other jurors. ` )
}
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
const reshuffleMemberMutation = trpc . assignment . reassignDroppedJuror . useMutation ( {
onSuccess : ( data ) = > {
utils . assignment . listByStage . invalidate ( { roundId } )
utils . roundEngine . getProjectStates . invalidate ( { roundId } )
utils . analytics . getJurorWorkload . invalidate ( { roundId } )
utils . roundAssignment . unassignedQueue . invalidate ( { roundId } )
if ( data . failedCount > 0 ) {
toast . warning ( ` Dropped juror and reassigned ${ data . movedCount } project(s). ${ data . failedCount } could not be reassigned. ` )
} else {
toast . success ( ` Dropped juror and reassigned ${ data . movedCount } project(s) evenly across available jurors. ` )
}
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
2026-02-16 09:20:02 +01:00
const advanceMutation = trpc . round . advanceProjects . useMutation ( {
onSuccess : ( data ) = > {
utils . round . getById . invalidate ( { id : roundId } )
utils . roundEngine . getProjectStates . invalidate ( { roundId } )
2026-02-16 19:09:23 +01:00
const msg = data . autoPassedCount
? ` Passed ${ data . autoPassedCount } and advanced ${ data . advancedCount } project(s) to ${ data . targetRoundName } `
: ` Advanced ${ data . advancedCount } project(s) to ${ data . targetRoundName } `
toast . success ( msg )
2026-02-16 09:20:02 +01:00
setAdvanceDialogOpen ( false )
} ,
onError : ( err ) = > toast . error ( err . message ) ,
} )
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
const shortlistMutation = trpc . round . generateAIRecommendations . useMutation ( {
onSuccess : ( data ) = > {
if ( data . success ) {
setAiRecommendations ( data . recommendations )
toast . success (
` AI recommendations generated: ${ data . recommendations . STARTUP . length } startups, ${ data . recommendations . BUSINESS_CONCEPT . length } concepts ` +
( data . tokensUsed ? ` ( ${ data . tokensUsed } tokens) ` : '' ) ,
)
} else {
toast . error ( data . errors ? . join ( '; ' ) || 'AI shortlist failed' )
}
setShortlistDialogOpen ( false )
} ,
onError : ( err ) = > {
toast . error ( err . message )
setShortlistDialogOpen ( false )
} ,
} )
2026-02-16 12:06:07 +01:00
const isTransitioning = activateMutation . isPending || closeMutation . isPending || reopenMutation . isPending || archiveMutation . isPending
2026-02-16 09:20:02 +01:00
const handleConfigChange = useCallback ( ( newConfig : Record < string , unknown > ) = > {
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
setConfig ( newConfig )
2026-02-17 16:43:47 +01:00
} , [ ] )
const saveConfig = useCallback ( ( ) = > {
2026-02-17 18:53:51 +01:00
savingRef . current = true
2026-02-17 12:33:20 +01:00
setAutosaveStatus ( 'saving' )
2026-02-17 16:43:47 +01:00
updateMutation . mutate ( { id : roundId , configJson : config } )
} , [ config , roundId , updateMutation ] )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
// ── Auto-save: debounce config changes and save automatically ────────
2026-02-17 18:53:51 +01:00
const configJson = JSON . stringify ( config )
const serverJson = JSON . stringify ( serverConfig )
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
useEffect ( ( ) = > {
if ( ! configInitialized . current ) return
2026-02-17 18:53:51 +01:00
if ( configJson === serverJson ) return
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
const timer = setTimeout ( ( ) = > {
2026-02-17 18:53:51 +01:00
savingRef . current = true
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
setAutosaveStatus ( 'saving' )
updateMutation . mutate ( { id : roundId , configJson : config } )
} , 800 )
return ( ) = > clearTimeout ( timer )
// eslint-disable-next-line react-hooks/exhaustive-deps
2026-02-17 18:53:51 +01:00
} , [ configJson ] )
Fix filtering config save, auto-save, streamed results, improved AI prompt
- Add missing fields to FilteringConfigSchema (aiParseFiles, startupAdvanceCount,
conceptAdvanceCount, notifyOnEntry, notifyOnAdvance) — Zod was silently
stripping them on save
- Restore auto-save with 800ms debounce on config changes
- Add staggered animations for filtering results (stream in one-by-one)
- Improve AI screening prompt: file type label mappings, soft cap handling,
missing documents = fail, better user prompt structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:18:04 +01:00
2026-02-16 09:20:02 +01:00
// ── Computed values ────────────────────────────────────────────────────
2026-02-19 09:56:09 +01:00
const projectCount = projectStates ? . length ? ? round ? . _count ? . projectRoundStates ? ? 0
2026-02-16 09:20:02 +01:00
const stateCounts = useMemo ( ( ) = >
projectStates ? . reduce ( ( acc : Record < string , number > , ps : any ) = > {
acc [ ps . state ] = ( acc [ ps . state ] || 0 ) + 1
return acc
} , { } as Record < string , number > ) ? ? { } ,
[ projectStates ] )
const passedCount = stateCounts [ 'PASSED' ] ? ? 0
const juryGroup = round ? . juryGroup
2026-02-19 11:11:00 +01:00
const juryMemberCount = juryGroupDetail ? . members ? . length ? ? 0
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-16 09:20:02 +01:00
const isFiltering = round ? . roundType === 'FILTERING'
const isEvaluation = round ? . roundType === 'EVALUATION'
2026-02-16 16:43:23 +01:00
const hasJury = [ 'EVALUATION' , 'LIVE_FINAL' , 'DELIBERATION' ] . includes ( round ? . roundType ? ? '' )
2026-03-01 14:37:45 +01:00
const hasAwards = roundAwards . length > 0
2026-02-16 19:09:23 +01:00
const isSimpleAdvance = [ 'INTAKE' , 'SUBMISSION' , 'MENTORING' ] . includes ( round ? . roundType ? ? '' )
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
const poolLink = ` /admin/projects?hasAssign=false&round= ${ roundId } ` as Route
2026-02-16 09:20:02 +01:00
// ── Loading state ──────────────────────────────────────────────────────
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
if ( isLoading ) {
return (
< div className = "space-y-6" >
2026-02-16 12:38:28 +01:00
{ /* Header skeleton — dark gradient placeholder */ }
< div className = "rounded-xl bg-gradient-to-r from-[#053d57]/20 to-[#0a5a7c]/20 p-6 animate-pulse" >
< div className = "flex items-center gap-3" >
< Skeleton className = "h-8 w-8 rounded bg-white/20" / >
< div className = "space-y-2" >
< Skeleton className = "h-7 w-64 bg-white/20" / >
< Skeleton className = "h-4 w-40 bg-white/20" / >
< / div >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / div >
< / div >
< Skeleton className = "h-10 w-full" / >
2026-02-16 12:38:28 +01:00
< Skeleton className = "h-96 w-full rounded-lg" / >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / div >
)
}
if ( ! round ) {
return (
< div className = "space-y-6" >
< div className = "flex items-center gap-3" >
2026-02-16 09:20:02 +01:00
< Link href = { '/admin/rounds' as Route } >
< Button variant = "ghost" size = "icon" className = "h-8 w-8" aria-label = "Back" >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< ArrowLeft className = "h-4 w-4" / >
< / Button >
< / Link >
< div >
< h1 className = "text-xl font-bold" > Round Not Found < / h1 >
2026-02-16 09:20:02 +01:00
< p className = "text-sm text-muted-foreground" > This round does not exist . < / p >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / div >
< / div >
< / div >
)
}
2026-02-16 09:20:02 +01:00
const status = round . status as keyof typeof roundStatusConfig
const statusCfg = roundStatusConfig [ status ] || roundStatusConfig . ROUND_DRAFT
const typeCfg = roundTypeConfig [ round . roundType ] || roundTypeConfig . INTAKE
// ── Readiness checklist ────────────────────────────────────────────────
const readinessItems = [
{
label : 'Projects assigned' ,
ready : projectCount > 0 ,
detail : projectCount > 0 ? ` ${ projectCount } projects ` : 'No projects yet' ,
action : projectCount === 0 ? poolLink : undefined ,
actionLabel : 'Assign Projects' ,
} ,
2026-02-16 16:43:23 +01:00
. . . ( hasJury
2026-02-16 09:20:02 +01:00
? [ {
label : 'Jury group set' ,
ready : ! ! juryGroup ,
detail : juryGroup ? ` ${ juryGroup . name } ( ${ juryMemberCount } members) ` : 'No jury group assigned' ,
action : undefined as Route | undefined ,
actionLabel : undefined as string | undefined ,
} ]
: [ ] ) ,
{
label : 'Dates configured' ,
ready : ! ! round . windowOpenAt && ! ! round . windowCloseAt ,
detail :
round . windowOpenAt && round . windowCloseAt
? ` ${ new Date ( round . windowOpenAt ) . toLocaleDateString ( ) } \ u2014 ${ new Date ( round . windowCloseAt ) . toLocaleDateString ( ) } `
: 'No dates set \u2014 configure in Config tab' ,
action : undefined as Route | undefined ,
actionLabel : undefined as string | undefined ,
} ,
2026-02-17 14:13:25 +01:00
. . . ( ( isEvaluation && ! ( config . requireDocumentUpload as boolean ) )
? [ ]
: [ {
label : 'File requirements set' ,
ready : ( fileRequirements ? . length ? ? 0 ) > 0 ,
detail :
( fileRequirements ? . length ? ? 0 ) > 0
? ` ${ fileRequirements ? . length } requirement(s) `
: 'No file requirements \u2014 configure in Config tab' ,
action : undefined as Route | undefined ,
actionLabel : undefined as string | undefined ,
} ] ) ,
2026-02-16 09:20:02 +01:00
]
const readyCount = readinessItems . filter ( ( i ) = > i . ready ) . length
// ═════════════════════════════════════════════════════════════════════════
// Render
// ═════════════════════════════════════════════════════════════════════════
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
return (
< div className = "space-y-6" >
2026-02-16 12:38:28 +01:00
{ /* ===== HEADER — Dark Blue gradient banner ===== */ }
< motion.div
initial = { { opacity : 0 , y : - 8 } }
animate = { { opacity : 1 , y : 0 } }
transition = { { duration : 0.4 , ease : 'easeOut' } }
className = "rounded-xl bg-gradient-to-r from-[#053d57] to-[#0a5a7c] p-5 sm:p-6 text-white shadow-lg"
>
< div className = "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between" >
< div className = "flex items-start gap-3 min-w-0" >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< Link href = { ( backUrl ? ? ( round . specialAwardId ? ` /admin/awards/ ${ round . specialAwardId } ` : '/admin/rounds' ) ) as Route } className = "mt-0.5 shrink-0" >
2026-02-19 12:59:35 +01:00
< Button variant = "ghost" size = "sm" className = "h-8 text-white/80 hover:text-white hover:bg-white/10 gap-1.5" aria-label = { round . specialAwardId ? 'Back to Award' : 'Back to rounds' } >
2026-02-16 12:38:28 +01:00
< ArrowLeft className = "h-4 w-4" / >
2026-02-19 12:59:35 +01:00
< span className = "text-xs hidden sm:inline" > { round . specialAwardId ? 'Back to Award' : 'Back to Rounds' } < / span >
2026-02-16 12:38:28 +01:00
< / Button >
< / Link >
< div className = "min-w-0" >
< div className = "flex flex-wrap items-center gap-2.5" >
2026-02-19 12:59:35 +01:00
{ /* 4.6 Inline-editable round name */ }
{ editingName ? (
< Input
ref = { nameInputRef }
value = { nameValue }
onChange = { ( e ) = > setNameValue ( e . target . value ) }
onBlur = { ( ) = > {
const trimmed = nameValue . trim ( )
if ( trimmed && trimmed !== round . name ) {
updateMutation . mutate ( { id : roundId , name : trimmed } )
}
setEditingName ( false )
} }
onKeyDown = { ( e ) = > {
if ( e . key === 'Enter' ) {
( e . target as HTMLInputElement ) . blur ( )
}
if ( e . key === 'Escape' ) {
setNameValue ( round . name )
setEditingName ( false )
}
} }
className = "text-xl font-bold tracking-tight bg-white/10 border-white/30 text-white h-8 w-64"
autoFocus
/ >
) : (
< button
type = "button"
className = "text-xl font-bold tracking-tight truncate hover:bg-white/10 rounded px-1 -mx-1 transition-colors cursor-text"
onClick = { ( ) = > {
setNameValue ( round . name )
setEditingName ( true )
setTimeout ( ( ) = > nameInputRef . current ? . focus ( ) , 0 )
} }
title = "Click to edit round name"
>
{ round . name }
< / button >
) }
2026-02-16 12:38:28 +01:00
< Badge variant = "secondary" className = "text-xs shrink-0 bg-white/15 text-white border-white/20 hover:bg-white/20" >
{ typeCfg . label }
< / Badge >
2026-02-19 12:59:35 +01:00
{ /* Status dropdown with confirmation dialogs (4.1) */ }
< TooltipProvider >
2026-03-03 09:43:28 +01:00
< Tooltip open = { statusDropdownOpen ? false : undefined } >
2026-02-19 12:59:35 +01:00
< TooltipTrigger asChild >
< span >
2026-03-03 09:43:28 +01:00
< DropdownMenu onOpenChange = { setStatusDropdownOpen } >
2026-02-19 12:59:35 +01:00
< DropdownMenuTrigger asChild >
< button
className = { cn (
'inline-flex items-center gap-1.5 text-[11px] font-medium px-2.5 py-1 rounded-full transition-colors shrink-0' ,
'bg-white/15 text-white hover:bg-white/25' ,
) }
>
< span className = { cn ( 'h-1.5 w-1.5 rounded-full' , statusCfg . dotClass ) } / >
{ statusCfg . label }
< ChevronDown className = "h-3 w-3" / >
< / button >
< / DropdownMenuTrigger >
< DropdownMenuContent align = "start" >
{ status === 'ROUND_DRAFT' && (
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< TooltipProvider >
< Tooltip >
< TooltipTrigger asChild >
< span >
< DropdownMenuItem
onClick = { ( ) = > setStatusConfirmAction ( 'activate' ) }
disabled = { isTransitioning || readyCount < readinessItems . length }
>
< Play className = "h-4 w-4 mr-2 text-emerald-600" / >
Activate Round
{ readyCount < readinessItems . length && (
< span className = "ml-auto text-[10px] text-muted-foreground" >
{ readyCount } / { readinessItems . length }
< / span >
) }
< / DropdownMenuItem >
< / span >
< / TooltipTrigger >
{ readyCount < readinessItems . length && (
< TooltipContent side = "right" className = "max-w-xs" >
< p className = "text-xs font-medium mb-1" > Not ready to activate : < / p >
< ul className = "space-y-0.5" >
{ readinessItems . filter ( ( i ) = > ! i . ready ) . map ( ( item ) = > (
< li key = { item . label } className = "text-xs" > & bull ; { item . label } < / li >
) ) }
< / ul >
< / TooltipContent >
) }
< / Tooltip >
< / TooltipProvider >
2026-02-19 12:59:35 +01:00
) }
{ status === 'ROUND_ACTIVE' && (
< DropdownMenuItem
onClick = { ( ) = > setStatusConfirmAction ( 'close' ) }
disabled = { isTransitioning }
>
< Square className = "h-4 w-4 mr-2 text-blue-600" / >
Close Round
< / DropdownMenuItem >
) }
{ status === 'ROUND_CLOSED' && (
< >
< DropdownMenuItem
onClick = { ( ) = > setStatusConfirmAction ( 'reopen' ) }
disabled = { isTransitioning }
>
< Play className = "h-4 w-4 mr-2 text-emerald-600" / >
Reopen Round
< / DropdownMenuItem >
< DropdownMenuSeparator / >
< DropdownMenuItem
onClick = { ( ) = > setStatusConfirmAction ( 'archive' ) }
disabled = { isTransitioning }
>
< Archive className = "h-4 w-4 mr-2" / >
Archive Round
< / DropdownMenuItem >
< / >
) }
{ isTransitioning && (
< div className = "flex items-center gap-2 px-2 py-1.5 text-xs text-muted-foreground" >
< Loader2 className = "h-3 w-3 animate-spin" / >
Updating . . .
< / div >
) }
< / DropdownMenuContent >
< / DropdownMenu >
< / span >
< / TooltipTrigger >
< TooltipContent side = "bottom" >
< p > { statusCfg . description } < / p >
< / TooltipContent >
< / Tooltip >
< / TooltipProvider >
{ /* Status change confirmation dialog (4.1) */ }
< AlertDialog open = { ! ! statusConfirmAction } onOpenChange = { ( open ) = > { if ( ! open ) setStatusConfirmAction ( null ) } } >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle >
{ statusConfirmAction === 'activate' && 'Activate this round?' }
{ statusConfirmAction === 'close' && 'Close this round?' }
{ statusConfirmAction === 'reopen' && 'Reopen this round?' }
{ statusConfirmAction === 'archive' && 'Archive this round?' }
< / AlertDialogTitle >
< AlertDialogDescription >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
{ statusConfirmAction === 'activate' && (
< >
The round will go live . Projects can be processed and jury members will be able to see their assignments .
{ readyCount < readinessItems . length && (
< span className = "block mt-2 text-amber-600" >
Warning : { readinessItems . length - readyCount } readiness item ( s ) not yet complete ( { readinessItems . filter ( ( i ) = > ! i . ready ) . map ( ( i ) = > i . label ) . join ( ', ' ) } ) .
< / span >
) }
< / >
) }
2026-02-19 12:59:35 +01:00
{ statusConfirmAction === 'close' && 'No further changes will be accepted. You can reactivate later if needed.' }
{ statusConfirmAction === 'reopen' && 'The round will become active again. Any rounds after this one that are currently active will be paused automatically.' }
{ statusConfirmAction === 'archive' && 'The round will be archived. It will only be available as a historical record.' }
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction
onClick = { ( ) = > {
if ( statusConfirmAction === 'activate' ) activateMutation . mutate ( { roundId } )
else if ( statusConfirmAction === 'close' ) closeMutation . mutate ( { roundId } )
else if ( statusConfirmAction === 'reopen' ) reopenMutation . mutate ( { roundId } )
else if ( statusConfirmAction === 'archive' ) archiveMutation . mutate ( { roundId } )
setStatusConfirmAction ( null )
} }
2026-02-16 09:20:02 +01:00
>
2026-02-19 12:59:35 +01:00
{ statusConfirmAction === 'activate' && 'Activate' }
{ statusConfirmAction === 'close' && 'Close Round' }
{ statusConfirmAction === 'reopen' && 'Reopen' }
{ statusConfirmAction === 'archive' && 'Archive' }
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
2026-02-16 12:38:28 +01:00
< / div >
< p className = "text-sm text-white/60 mt-1" > { typeCfg . description } < / p >
2026-02-24 17:44:55 +01:00
{ ( round . windowOpenAt || round . windowCloseAt ) && (
< div className = "flex items-center gap-1.5 text-white/60 text-xs mt-1" >
< CalendarDays className = "h-3 w-3" / >
{ round . windowOpenAt ? new Date ( round . windowOpenAt ) . toLocaleDateString ( ) : 'No start' }
{ ' \u2014 ' }
{ round . windowCloseAt ? new Date ( round . windowCloseAt ) . toLocaleDateString ( ) : 'No deadline' }
{ ( ( ) = > {
const now = new Date ( )
const openAt = round . windowOpenAt ? new Date ( round . windowOpenAt ) : null
const closeAt = round . windowCloseAt ? new Date ( round . windowCloseAt ) : null
if ( openAt && now < openAt ) {
return < span className = "text-[#80c4dc] font-medium ml-1" > Opens { getRelativeTime ( openAt ) } < / span >
}
if ( closeAt && now < closeAt ) {
return < span className = "text-amber-300 font-medium ml-1" > Closes { getRelativeTime ( closeAt ) } < / span >
}
if ( closeAt && now >= closeAt ) {
return < span className = "text-white/40 ml-1" > Closed { getRelativeTime ( closeAt ) } < / span >
}
return null
} ) ( ) }
< / div >
) }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / div >
< / div >
2026-02-16 12:38:28 +01:00
{ /* Action buttons */ }
< div className = "flex items-center gap-2 shrink-0 flex-wrap" >
2026-02-17 12:33:20 +01:00
{ autosaveStatus === 'saved' && (
< span className = "flex items-center gap-1.5 text-xs text-emerald-300" >
< CheckCircle2 className = "h-3.5 w-3.5" / >
Saved
< / span >
) }
2026-02-16 12:38:28 +01:00
< Link href = { poolLink } >
2026-02-16 13:21:35 +01:00
< Button variant = "outline" size = "sm" className = "border-white/40 bg-white/15 text-white hover:bg-white/30 hover:text-white" >
2026-02-16 12:38:28 +01:00
< Layers className = "h-4 w-4 mr-1.5" / >
Project Pool
< / Button >
< / Link >
< / div >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / div >
2026-02-16 12:38:28 +01:00
< / motion.div >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-16 12:38:28 +01:00
{ /* ===== TABS — Underline style ===== */ }
2026-02-16 09:20:02 +01:00
< Tabs value = { activeTab } onValueChange = { setActiveTab } className = "space-y-4" >
2026-02-16 12:38:28 +01:00
< div className = "border-b overflow-x-auto" >
< TabsList className = "bg-transparent h-auto p-0 gap-0 w-full sm:w-auto" >
{ [
{ value : 'overview' , label : 'Overview' , icon : Zap } ,
{ value : 'projects' , label : 'Projects' , icon : Layers } ,
. . . ( isFiltering ? [ { value : 'filtering' , label : 'Filtering' , icon : Shield } ] : [ ] ) ,
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
. . . ( isEvaluation ? [ { value : 'assignments' , label : 'Assignments & Jury' , icon : ClipboardList } ] : [ ] ) ,
2026-02-27 09:41:59 +01:00
. . . ( isEvaluation ? [ { value : 'ranking' , label : 'Ranking' , icon : BarChart3 } ] : [ ] ) ,
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
. . . ( hasJury && ! isEvaluation ? [ { value : 'jury' , label : 'Jury' , icon : Users } ] : [ ] ) ,
2026-02-16 12:38:28 +01:00
{ value : 'config' , label : 'Config' , icon : Settings } ,
2026-02-16 16:43:23 +01:00
. . . ( hasAwards ? [ { value : 'awards' , label : 'Awards' , icon : Trophy } ] : [ ] ) ,
2026-02-16 12:38:28 +01:00
] . map ( ( tab ) = > (
< TabsTrigger
key = { tab . value }
value = { tab . value }
className = { cn (
'relative rounded-none border-b-2 border-transparent px-4 py-2.5 text-sm font-medium transition-all' ,
'data-[state=active]:border-b-[#de0f1e] data-[state=active]:text-[#053d57] data-[state=active]:font-semibold data-[state=active]:shadow-none' ,
'text-muted-foreground hover:text-foreground' ,
'bg-transparent data-[state=active]:bg-transparent' ,
) }
>
< tab.icon className = { cn ( 'h-3.5 w-3.5 mr-1.5' , activeTab === tab . value ? 'text-[#557f8c]' : '' ) } / >
{ tab . label }
{ tab . value === 'awards' && roundAwards . length > 0 && (
< Badge variant = "secondary" className = "ml-1.5 h-5 min-w-5 text-[10px] px-1.5 bg-[#de0f1e] text-white" >
{ roundAwards . length }
< / Badge >
) }
< / TabsTrigger >
) ) }
< / TabsList >
< / div >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-16 09:20:02 +01:00
{ /* ═══════════ OVERVIEW TAB ═══════════ */ }
< TabsContent value = "overview" className = "space-y-6" >
2026-02-16 12:38:28 +01:00
{ /* Readiness Checklist with Progress Ring */ }
< AnimatedCard index = { 0 } >
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-4" >
{ /* SVG Progress Ring */ }
< div className = "relative h-14 w-14 shrink-0" >
< svg className = "h-14 w-14 -rotate-90" viewBox = "0 0 56 56" >
< circle cx = "28" cy = "28" r = "24" fill = "none" stroke = "currentColor" strokeWidth = "3" className = "text-muted/30" / >
< circle
cx = "28" cy = "28" r = "24" fill = "none"
strokeWidth = "3" strokeLinecap = "round"
stroke = { readyCount === readinessItems . length ? '#10b981' : '#de0f1e' }
strokeDasharray = { ` ${ ( readyCount / readinessItems . length ) * 150.8 } 150.8 ` }
className = "transition-all duration-700"
/ >
< / svg >
< span className = "absolute inset-0 flex items-center justify-center text-xs font-bold" >
{ readyCount } / { readinessItems . length }
< / span >
2026-02-16 09:20:02 +01:00
< / div >
2026-02-16 12:38:28 +01:00
< div >
< CardTitle className = "text-base" > Launch Readiness < / CardTitle >
< CardDescription >
{ readyCount === readinessItems . length
? 'All checks passed — ready to go'
: ` ${ readinessItems . length - readyCount } item(s) remaining ` }
< / CardDescription >
< / div >
< / div >
< Badge
variant = { readyCount === readinessItems . length ? 'default' : 'secondary' }
className = { cn (
'text-xs' ,
readyCount === readinessItems . length
? 'bg-emerald-100 text-emerald-700'
: 'bg-amber-100 text-amber-700' ,
2026-02-16 09:20:02 +01:00
) }
2026-02-16 12:38:28 +01:00
>
{ readyCount === readinessItems . length ? 'Ready' : 'Incomplete' }
< / Badge >
< / div >
< / CardHeader >
< CardContent >
< div className = "space-y-3" >
{ readinessItems . map ( ( item ) = > (
< div key = { item . label } className = "flex items-start gap-3" >
{ item . ready ? (
< CheckCircle2 className = "h-4 w-4 text-emerald-500 mt-0.5 shrink-0" / >
) : (
< AlertTriangle className = "h-4 w-4 text-amber-500 mt-0.5 shrink-0" / >
) }
< div className = "flex-1 min-w-0" >
2026-02-19 12:59:35 +01:00
< p className = { cn ( 'text-sm font-medium' , item . ready && 'text-emerald-600 opacity-80' ) } >
2026-02-16 12:38:28 +01:00
{ item . label }
< / p >
< p className = "text-xs text-muted-foreground" > { item . detail } < / p >
< / div >
{ item . action && (
< Link href = { item . action } >
< Button size = "sm" className = "shrink-0 text-xs bg-[#de0f1e] hover:bg-[#c00d1a] text-white" >
{ item . actionLabel }
< / Button >
< / Link >
) }
< / div >
) ) }
< / div >
< / CardContent >
< / Card >
< / AnimatedCard >
2026-02-25 15:19:30 +01:00
{ /* Advancement Votes Summary — only for EVALUATION rounds */ }
{ isEvaluation && (
< AnimatedCard index = { 1 } >
< AdvancementSummaryCard roundId = { roundId } / >
< / AnimatedCard >
) }
2026-02-17 16:25:59 +01:00
{ /* Filtering Results Summary — only for FILTERING rounds with results */ }
{ isFiltering && filteringStats && filteringStats . total > 0 && (
< AnimatedCard index = { 1 } >
< Card className = "border-l-4 border-l-purple-500" >
< CardHeader className = "pb-3" >
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-3" >
< div className = "rounded-full bg-purple-50 p-2" >
< Shield className = "h-5 w-5 text-purple-600" / >
< / div >
< div >
< CardTitle className = "text-base" > Filtering Results < / CardTitle >
< CardDescription >
{ filteringStats . total } projects evaluated
< / CardDescription >
< / div >
< / div >
< Button
size = "sm"
variant = "outline"
onClick = { ( ) = > setActiveTab ( 'filtering' ) }
>
View Details
< ArrowRight className = "h-3.5 w-3.5 ml-1.5" / >
< / Button >
< / div >
< / CardHeader >
< CardContent >
< div className = "grid grid-cols-3 gap-4 mb-4" >
< div className = "text-center p-3 rounded-lg bg-emerald-50" >
< p className = "text-2xl font-bold text-emerald-700" > { filteringStats . passed } < / p >
< p className = "text-xs text-emerald-600 font-medium" > Passed < / p >
< / div >
< div className = "text-center p-3 rounded-lg bg-red-50" >
< p className = "text-2xl font-bold text-red-700" > { filteringStats . filteredOut } < / p >
< p className = "text-xs text-red-600 font-medium" > Filtered Out < / p >
< / div >
< div className = "text-center p-3 rounded-lg bg-amber-50" >
< p className = "text-2xl font-bold text-amber-700" > { filteringStats . flagged } < / p >
< p className = "text-xs text-amber-600 font-medium" > Flagged < / p >
< / div >
< / div >
{ /* Progress bar showing pass rate */ }
< div className = "space-y-1.5" >
< div className = "flex justify-between text-xs text-muted-foreground" >
< span > Pass rate < / span >
< span > { Math . round ( ( filteringStats . passed / filteringStats . total ) * 100 ) } % < / span >
< / div >
< div className = "h-2.5 rounded-full bg-muted overflow-hidden flex" >
< div
className = "bg-emerald-500 transition-all duration-500"
style = { { width : ` ${ ( filteringStats . passed / filteringStats . total ) * 100 } % ` } }
/ >
< div
className = "bg-red-400 transition-all duration-500"
style = { { width : ` ${ ( filteringStats . filteredOut / filteringStats . total ) * 100 } % ` } }
/ >
< div
className = "bg-amber-400 transition-all duration-500"
style = { { width : ` ${ ( filteringStats . flagged / filteringStats . total ) * 100 } % ` } }
/ >
< / div >
{ filteringStats . overridden > 0 && (
< p className = "text-xs text-muted-foreground" >
{ filteringStats . overridden } result ( s ) manually overridden
< / p >
) }
< / div >
< / CardContent >
< / Card >
< / AnimatedCard >
) }
2026-02-16 12:38:28 +01:00
{ /* Quick Actions — Grouped & styled */ }
2026-02-17 16:25:59 +01:00
< AnimatedCard index = { 2 } >
2026-02-16 12:38:28 +01:00
< Card >
< CardHeader >
< CardTitle className = "text-base" > Quick Actions < / CardTitle >
< CardDescription > Common operations for this round < / CardDescription >
< / CardHeader >
< CardContent className = "space-y-4" >
{ /* Round Control Group */ }
{ ( status === 'ROUND_DRAFT' || status === 'ROUND_ACTIVE' || status === 'ROUND_CLOSED' ) && (
< div >
< p className = "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2" > Round Control < / p >
< div className = "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" >
{ status === 'ROUND_DRAFT' && (
< AlertDialog >
< AlertDialogTrigger asChild >
< button className = "flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-emerald-500 hover:-translate-y-0.5 hover:shadow-md transition-all text-left" >
< Play className = "h-5 w-5 text-emerald-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Activate Round < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Start this round and allow project processing
< / p >
< / div >
< / button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Activate this round ? < / AlertDialogTitle >
< AlertDialogDescription >
The round will go live . Projects can be processed and jury members will be able to see their assignments .
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction onClick = { ( ) = > activateMutation . mutate ( { roundId } ) } >
Activate
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
) }
{ status === 'ROUND_ACTIVE' && (
< AlertDialog >
< AlertDialogTrigger asChild >
< button className = "flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-blue-500 hover:-translate-y-0.5 hover:shadow-md transition-all text-left" >
< Square className = "h-5 w-5 text-blue-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Close Round < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Stop accepting changes and finalize results
< / p >
< / div >
< / button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Close this round ? < / AlertDialogTitle >
< AlertDialogDescription >
No further changes will be accepted . You can reactivate later if needed .
{ projectCount > 0 && (
< span className = "block mt-2" >
{ projectCount } projects are currently in this round .
< / span >
) }
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction onClick = { ( ) = > closeMutation . mutate ( { roundId } ) } >
Close Round
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
) }
{ status === 'ROUND_CLOSED' && (
< AlertDialog >
< AlertDialogTrigger asChild >
< button className = "flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-amber-500 bg-amber-50/30 hover:-translate-y-0.5 hover:shadow-md transition-all text-left" >
< RotateCcw className = "h-5 w-5 text-amber-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Reopen Round < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Reactivate this round . Any subsequent active rounds will be paused .
< / p >
< / div >
< / button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Reopen this round ? < / AlertDialogTitle >
< AlertDialogDescription >
The round will become active again . Any rounds after this one that are currently active will be paused ( closed ) automatically .
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction onClick = { ( ) = > reopenMutation . mutate ( { roundId } ) } >
Reopen
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
) }
< / div >
2026-02-16 09:20:02 +01:00
< / div >
2026-02-16 12:38:28 +01:00
) }
2026-02-16 09:20:02 +01:00
2026-02-16 12:38:28 +01:00
{ /* Project Management Group */ }
< div >
< p className = "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2" > Project Management < / p >
< div className = "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" >
< Link href = { poolLink } >
< button className = "flex items-start gap-3 p-4 rounded-lg border hover:-translate-y-0.5 hover:shadow-md transition-all text-left w-full" >
< Layers className = "h-5 w-5 text-[#557f8c] mt-0.5 shrink-0" / >
2026-02-16 09:20:02 +01:00
< div >
2026-02-16 12:38:28 +01:00
< p className = "text-sm font-medium" > Assign Projects < / p >
2026-02-16 09:20:02 +01:00
< p className = "text-xs text-muted-foreground mt-0.5" >
2026-02-16 12:38:28 +01:00
Add projects from the pool to this round
2026-02-16 09:20:02 +01:00
< / p >
< / div >
< / button >
2026-02-16 12:38:28 +01:00
< / Link >
2026-02-16 09:20:02 +01:00
2026-02-16 12:38:28 +01:00
< button
onClick = { ( ) = > setActiveTab ( 'projects' ) }
className = "flex items-start gap-3 p-4 rounded-lg border hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
>
< BarChart3 className = "h-5 w-5 text-[#557f8c] mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Manage Projects < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
View , filter , and transition project states
< / p >
< / div >
< / button >
2026-02-16 16:43:23 +01:00
{ /* Advance projects (always visible when projects exist) */ }
{ projectCount > 0 && (
2026-02-16 12:38:28 +01:00
< button
2026-02-16 19:09:23 +01:00
onClick = { ( ) = > ( isSimpleAdvance || passedCount > 0 )
2026-02-16 16:43:23 +01:00
? setAdvanceDialogOpen ( true )
: toast . info ( 'Mark projects as "Passed" first in the Projects tab' ) }
className = { cn (
'flex items-start gap-3 p-4 rounded-lg border hover:-translate-y-0.5 hover:shadow-md transition-all text-left' ,
2026-02-16 19:09:23 +01:00
( isSimpleAdvance || passedCount > 0 )
2026-02-16 16:43:23 +01:00
? 'border-l-4 border-l-emerald-500 bg-emerald-50/30'
: 'border-dashed opacity-60' ,
) }
2026-02-16 12:38:28 +01:00
>
2026-02-16 19:09:23 +01:00
< ArrowRight className = { cn ( 'h-5 w-5 mt-0.5 shrink-0' , ( isSimpleAdvance || passedCount > 0 ) ? 'text-emerald-600' : 'text-muted-foreground' ) } / >
2026-02-16 09:20:02 +01:00
< div >
2026-02-16 12:38:28 +01:00
< p className = "text-sm font-medium" > Advance Projects < / p >
2026-02-16 09:20:02 +01:00
< p className = "text-xs text-muted-foreground mt-0.5" >
2026-02-16 19:09:23 +01:00
{ isSimpleAdvance
? ` Advance all ${ projectCount } project(s) to the next round `
: passedCount > 0
? ` Move ${ passedCount } passed project(s) to the next round `
: 'Mark projects as "Passed" first, then advance' }
2026-02-16 09:20:02 +01:00
< / p >
< / div >
2026-02-16 19:09:23 +01:00
< Badge className = "ml-auto shrink-0 bg-emerald-100 text-emerald-700 text-[10px]" > { isSimpleAdvance ? projectCount : passedCount } < / Badge >
2026-02-16 09:20:02 +01:00
< / button >
2026-02-16 12:38:28 +01:00
) }
2026-02-16 09:20:02 +01:00
2026-02-16 16:43:23 +01:00
{ /* Close & Advance (active rounds with passed projects) */ }
{ status === 'ROUND_ACTIVE' && passedCount > 0 && (
< button
onClick = { ( ) = > {
setCloseAndAdvance ( true )
closeMutation . mutate ( { roundId } )
} }
disabled = { isTransitioning }
className = "flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-purple-500 bg-purple-50/30 hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
>
< Square className = "h-5 w-5 text-purple-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Close & Advance < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Close this round and advance { passedCount } passed project ( s ) to the next round
< / p >
< / div >
< / button >
) }
{ /* Jury assignment for rounds that use jury */ }
{ hasJury && ! juryGroup && (
2026-02-16 12:38:28 +01:00
< button
onClick = { ( ) = > {
const el = document . querySelector ( '[data-jury-select]' )
if ( el ) el . scrollIntoView ( { behavior : 'smooth' , block : 'center' } )
} }
className = "flex items-start gap-3 p-4 rounded-lg border border-l-4 border-l-amber-500 bg-amber-50/30 hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
>
< UserPlus className = "h-5 w-5 text-amber-600 mt-0.5 shrink-0" / >
2026-02-16 12:06:07 +01:00
< div >
2026-02-16 12:38:28 +01:00
< p className = "text-sm font-medium" > Assign Jury Group < / p >
2026-02-16 12:06:07 +01:00
< p className = "text-xs text-muted-foreground mt-0.5" >
2026-02-16 12:38:28 +01:00
No jury group assigned . Select one in the Jury card above .
2026-02-16 12:06:07 +01:00
< / p >
< / div >
< / button >
2026-02-16 12:38:28 +01:00
) }
2026-02-16 09:20:02 +01:00
2026-02-16 12:38:28 +01:00
{ /* Evaluation: manage assignments */ }
{ isEvaluation && (
< button
onClick = { ( ) = > setActiveTab ( 'assignments' ) }
className = "flex items-start gap-3 p-4 rounded-lg border hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
>
< ClipboardList className = "h-5 w-5 text-[#557f8c] mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Manage Assignments < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Generate and review jury - project assignments
< / p >
< / div >
< / button >
) }
< / div >
< / div >
2026-02-16 09:20:02 +01:00
2026-02-16 12:38:28 +01:00
{ /* AI Tools Group */ }
{ ( ( isFiltering || isEvaluation ) && projectCount > 0 ) && (
2026-02-16 09:20:02 +01:00
< div >
2026-02-16 12:38:28 +01:00
< p className = "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2" > AI Tools < / p >
< div className = "grid gap-3 sm:grid-cols-2 lg:grid-cols-3" >
{ isFiltering && (
< button
onClick = { ( ) = > setActiveTab ( 'filtering' ) }
className = "flex items-start gap-3 p-4 rounded-lg border bg-gradient-to-br from-purple-50/50 to-blue-50/50 hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
>
< Shield className = "h-5 w-5 text-purple-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" > Run AI Filtering < / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Screen projects with AI and manual review
< / p >
< / div >
< / button >
) }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
2026-02-16 12:38:28 +01:00
< button
onClick = { ( ) = > setShortlistDialogOpen ( true ) }
className = "flex items-start gap-3 p-4 rounded-lg border bg-gradient-to-br from-purple-50/50 to-blue-50/50 hover:-translate-y-0.5 hover:shadow-md transition-all text-left"
disabled = { shortlistMutation . isPending }
>
< Zap className = "h-5 w-5 text-purple-600 mt-0.5 shrink-0" / >
< div >
< p className = "text-sm font-medium" >
{ shortlistMutation . isPending ? 'Generating...' : 'AI Recommendations' }
< / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
Generate ranked shortlist per category using AI analysis
< / p >
< / div >
< / button >
2026-02-16 09:20:02 +01:00
< / div >
2026-02-16 12:38:28 +01:00
< / div >
2026-02-16 09:20:02 +01:00
) }
2026-02-16 12:38:28 +01:00
< / CardContent >
< / Card >
< / AnimatedCard >
2026-02-16 09:20:02 +01:00
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
{ /* Advance Projects Dialog */ }
< AdvanceProjectsDialog
open = { advanceDialogOpen }
onOpenChange = { setAdvanceDialogOpen }
roundId = { roundId }
2026-02-16 19:09:23 +01:00
roundType = { round ? . roundType }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
projectStates = { projectStates }
config = { config }
advanceMutation = { advanceMutation }
2026-02-16 16:43:23 +01:00
competitionRounds = { competition ? . rounds ? . map ( ( r : any ) = > ( {
id : r.id ,
name : r.name ,
sortOrder : r.sortOrder ,
roundType : r.roundType ,
} ) ) }
currentSortOrder = { round ? . sortOrder }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
/ >
{ /* AI Shortlist Confirmation Dialog */ }
< AlertDialog open = { shortlistDialogOpen } onOpenChange = { setShortlistDialogOpen } >
2026-02-16 09:20:02 +01:00
< AlertDialogContent >
< AlertDialogHeader >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< AlertDialogTitle > Generate AI Recommendations ? < / AlertDialogTitle >
2026-02-16 09:20:02 +01:00
< AlertDialogDescription >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
The AI will analyze all project evaluations and generate a ranked shortlist
for each category independently .
{ config . startupAdvanceCount ? (
< span className = "block mt-1" >
Startup target : top { String ( config . startupAdvanceCount ) }
< / span >
) : null }
{ config . conceptAdvanceCount ? (
< span className = "block" >
Business Concept target : top { String ( config . conceptAdvanceCount ) }
< / span >
) : null }
{ config . aiParseFiles ? (
< span className = "block mt-1 text-amber-600" >
Document parsing is enabled — the AI will read uploaded file contents .
< / span >
) : null }
2026-02-16 09:20:02 +01:00
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
onClick = { ( ) = > shortlistMutation . mutate ( { roundId } ) }
disabled = { shortlistMutation . isPending }
2026-02-16 09:20:02 +01:00
>
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
{ shortlistMutation . isPending && < Loader2 className = "h-4 w-4 mr-1.5 animate-spin" / > }
Generate
2026-02-16 09:20:02 +01:00
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
{ /* AI Recommendations Display */ }
{ aiRecommendations && (
< AIRecommendationsDisplay
recommendations = { aiRecommendations }
2026-02-18 15:11:20 +01:00
projectStates = { projectStates }
roundId = { roundId }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
onClear = { ( ) = > setAiRecommendations ( null ) }
2026-02-18 15:11:20 +01:00
onApplied = { ( ) = > {
setAiRecommendations ( null )
utils . roundEngine . getProjectStates . invalidate ( { roundId } )
} }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
/ >
) }
2026-02-16 09:20:02 +01:00
{ /* Round Info + Project Breakdown */ }
< div className = "grid gap-4 sm:grid-cols-2" >
2026-02-16 12:38:28 +01:00
< AnimatedCard index = { 2 } >
< Card >
< CardHeader >
< CardTitle className = "text-sm" > Round Details < / CardTitle >
< / CardHeader >
< CardContent className = "space-y-0 text-sm" >
{ [
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
{ label : 'Type' , value : < Badge variant = "secondary" className = { cn ( 'text-xs' , typeCfg . badgeClass ) } > { typeCfg . label } < / Badge > } ,
2026-02-16 12:38:28 +01:00
{ label : 'Status' , value : < span className = "font-medium" > { statusCfg . label } < / span > } ,
2026-02-19 12:59:35 +01:00
{ label : 'Position' , value : < span className = "font-medium" > { ` Round ${ ( round . sortOrder ? ? 0 ) + 1 } ${ competition ? . rounds ? ` of ${ competition . rounds . length } ` : '' } ` } < / span > } ,
2026-02-16 12:38:28 +01:00
. . . ( round . purposeKey ? [ { label : 'Purpose' , value : < span className = "font-medium" > { round . purposeKey } < / span > } ] : [ ] ) ,
{ label : 'Jury Group' , value : < span className = "font-medium" > { juryGroup ? juryGroup . name : '\u2014' } < / span > } ,
{ label : 'Opens' , value : < span className = "font-medium" > { round . windowOpenAt ? new Date ( round . windowOpenAt ) . toLocaleString ( ) : '\u2014' } < / span > } ,
{ label : 'Closes' , value : < span className = "font-medium" > { round . windowCloseAt ? new Date ( round . windowCloseAt ) . toLocaleString ( ) : '\u2014' } < / span > } ,
] . map ( ( row , i ) = > (
< div key = { row . label } className = { cn ( 'flex justify-between items-center py-2.5' , i > 0 && 'border-t border-dotted border-muted' ) } >
< span className = "text-muted-foreground" > { row . label } < / span >
{ row . value }
< / div >
) ) }
< / CardContent >
< / Card >
< / AnimatedCard >
< AnimatedCard index = { 3 } >
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< CardTitle className = "text-sm" > Project Breakdown < / CardTitle >
{ projectCount > 0 && (
< span className = "text-xs font-mono text-muted-foreground" > { projectCount } total < / span >
) }
2026-02-16 09:20:02 +01:00
< / div >
2026-02-16 12:38:28 +01:00
< / CardHeader >
< CardContent >
{ projectCount === 0 ? (
< p className = "text-sm text-muted-foreground py-4 text-center" >
No projects assigned yet
< / p >
) : (
< div className = "space-y-3" >
{ [ 'PENDING' , 'IN_PROGRESS' , 'PASSED' , 'REJECTED' , 'COMPLETED' , 'WITHDRAWN' ] . map ( ( state ) = > {
const count = stateCounts [ state ] || 0
if ( count === 0 ) return null
const pct = ( ( count / projectCount ) * 100 ) . toFixed ( 0 )
return (
< div key = { state } >
< div className = "flex justify-between text-xs mb-1.5" >
< span className = "text-muted-foreground capitalize font-medium" > { state . toLowerCase ( ) . replace ( '_' , ' ' ) } < / span >
< span className = "font-bold tabular-nums" > { count } < span className = "font-normal text-muted-foreground" > ( { pct } % ) < / span > < / span >
< / div >
< div className = "h-2 bg-muted rounded-full overflow-hidden" >
< div
className = { cn ( 'h-full rounded-full transition-all duration-500' , stateColors [ state ] ) }
style = { { width : ` ${ pct } % ` } }
/ >
< / div >
2026-02-16 09:20:02 +01:00
< / div >
2026-02-16 12:38:28 +01:00
)
} ) }
< / div >
) }
< / CardContent >
< / Card >
< / AnimatedCard >
2026-02-16 09:20:02 +01:00
< / div >
< / TabsContent >
{ /* ═══════════ PROJECTS TAB ═══════════ */ }
< TabsContent value = "projects" className = "space-y-4" >
2026-03-02 12:48:08 +01:00
< ProjectStatesTable competitionId = { competitionId } roundId = { roundId } onAssignProjects = { ( ) = > {
setActiveTab ( 'assignments' )
setTimeout ( ( ) = > setPreviewSheetOpen ( true ) , 100 )
} } / >
2026-02-16 09:20:02 +01:00
< / TabsContent >
{ /* ═══════════ FILTERING TAB ═══════════ */ }
{ isFiltering && (
< TabsContent value = "filtering" className = "space-y-4" >
< FilteringDashboard competitionId = { competitionId } roundId = { roundId } / >
< / TabsContent >
) }
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
{ /* ═══════════ JURY TAB (non-EVALUATION jury rounds: LIVE_FINAL, DELIBERATION) ═══════════ */ }
{ hasJury && ! isEvaluation && (
2026-02-16 12:46:01 +01:00
< TabsContent value = "jury" className = "space-y-6" >
{ /* Members list (only if a jury group is assigned) */ }
{ juryGroupDetail && (
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< div >
< CardTitle className = "text-base" >
Members & mdash ; { juryGroupDetail . name }
< / CardTitle >
< CardDescription >
{ juryGroupDetail . members . length } member { juryGroupDetail . members . length !== 1 ? 's' : '' }
< / CardDescription >
< / div >
< Button size = "sm" onClick = { ( ) = > setAddMemberOpen ( true ) } >
< UserPlus className = "h-4 w-4 mr-1.5" / >
Add Member
< / Button >
< / div >
< / CardHeader >
< CardContent >
{ juryGroupDetail . members . length === 0 ? (
< div className = "flex flex-col items-center justify-center py-10 text-center" >
< div className = "rounded-full bg-muted p-4 mb-4" >
< UserPlus className = "h-8 w-8 text-muted-foreground" / >
< / div >
< p className = "text-sm font-medium" > No Members Yet < / p >
< p className = "text-xs text-muted-foreground mt-1" >
Add jury members to start assigning projects for evaluation .
< / p >
< Button size = "sm" variant = "outline" className = "mt-4" onClick = { ( ) = > setAddMemberOpen ( true ) } >
< UserPlus className = "h-4 w-4 mr-1.5" / >
Add First Member
< / Button >
< / div >
) : (
< div className = "space-y-1" >
{ juryGroupDetail . members . map ( ( member : any , idx : number ) = > (
< div
key = { member . id }
className = { cn (
'flex items-center justify-between py-2.5 px-3 rounded-md transition-colors' ,
idx % 2 === 1 && 'bg-muted/30' ,
) }
>
< div className = "min-w-0 flex-1" >
< p className = "text-sm font-medium truncate" >
{ member . user . name || 'Unnamed User' }
< / p >
< p className = "text-xs text-muted-foreground truncate" > { member . user . email } < / p >
< / div >
2026-02-18 18:23:54 +01:00
< div className = "flex items-center gap-2 shrink-0" >
< InlineMemberCap
memberId = { member . id }
currentValue = { member . maxAssignmentsOverride as number | null }
2026-02-21 18:50:29 +01:00
roundId = { roundId }
jurorUserId = { member . userId }
2026-02-18 18:23:54 +01:00
onSave = { ( val ) = > updateJuryMemberMutation . mutate ( {
id : member.id ,
maxAssignmentsOverride : val ,
} ) }
/ >
2026-02-23 16:08:46 +01:00
< TooltipProvider delayDuration = { 200 } >
< Tooltip >
< TooltipTrigger asChild >
< Button
variant = "ghost"
size = "icon"
className = "h-7 w-7 text-muted-foreground hover:text-foreground"
disabled = { notifyMemberMutation . isPending }
onClick = { ( ) = > notifyMemberMutation . mutate ( { roundId , userId : member.userId } ) }
>
{ notifyMemberMutation . isPending && notifyMemberMutation . variables ? . userId === member . userId ? (
< Loader2 className = "h-3.5 w-3.5 animate-spin" / >
) : (
< Mail className = "h-3.5 w-3.5" / >
) }
< / Button >
< / TooltipTrigger >
< TooltipContent side = "left" > < p > Notify juror of assignments < / p > < / TooltipContent >
< / Tooltip >
< / TooltipProvider >
< TooltipProvider delayDuration = { 200 } >
< Tooltip >
< TooltipTrigger asChild >
< Button
variant = "ghost"
size = "icon"
className = "h-7 w-7 text-muted-foreground hover:text-foreground"
disabled = { redistributeMemberMutation . isPending }
onClick = { ( ) = > {
const ok = window . confirm (
` Reassign all pending projects from ${ member . user . name || member . user . email } to other available jurors? The juror will remain in the group but lose unsubmitted assignments. `
)
if ( ! ok ) return
redistributeMemberMutation . mutate ( { roundId , jurorId : member.userId } )
} }
>
{ redistributeMemberMutation . isPending && redistributeMemberMutation . variables ? . jurorId === member . userId ? (
< Loader2 className = "h-3.5 w-3.5 animate-spin" / >
) : (
< Shuffle className = "h-3.5 w-3.5" / >
) }
< / Button >
< / TooltipTrigger >
< TooltipContent side = "left" > < p > Reassign all pending projects to other jurors < / p > < / TooltipContent >
< / Tooltip >
< / TooltipProvider >
< TooltipProvider delayDuration = { 200 } >
< Tooltip >
< TooltipTrigger asChild >
< Button
variant = "ghost"
size = "icon"
className = "h-7 w-7 text-muted-foreground hover:text-foreground"
onClick = { ( ) = > setMemberTransferJuror ( { id : member.userId , name : member.user.name || member . user . email } ) }
>
< ArrowRightLeft className = "h-3.5 w-3.5" / >
< / Button >
< / TooltipTrigger >
< TooltipContent side = "left" > < p > Transfer specific assignments to other jurors < / p > < / TooltipContent >
< / Tooltip >
< / TooltipProvider >
< TooltipProvider delayDuration = { 200 } >
< Tooltip >
< TooltipTrigger asChild >
< Button
variant = "ghost"
size = "icon"
className = "h-7 w-7 text-muted-foreground hover:text-destructive"
disabled = { reshuffleMemberMutation . isPending }
onClick = { ( ) = > {
const ok = window . confirm (
` Remove ${ member . user . name || member . user . email } from this jury pool and reassign all their unsubmitted projects to other jurors? Submitted evaluations will be preserved. This cannot be undone. `
)
if ( ! ok ) return
reshuffleMemberMutation . mutate ( { roundId , jurorId : member.userId } )
} }
>
{ reshuffleMemberMutation . isPending && reshuffleMemberMutation . variables ? . jurorId === member . userId ? (
< Loader2 className = "h-3.5 w-3.5 animate-spin" / >
) : (
< UserPlus className = "h-3.5 w-3.5" / >
) }
< / Button >
< / TooltipTrigger >
< TooltipContent side = "left" > < p > Drop juror & reshuffle pending projects < / p > < / TooltipContent >
< / Tooltip >
< / TooltipProvider >
2026-02-16 12:46:01 +01:00
< AlertDialog >
< AlertDialogTrigger asChild >
< Button
variant = "ghost"
size = "icon"
className = "h-7 w-7 text-destructive hover:text-destructive shrink-0"
>
< Trash2 className = "h-3.5 w-3.5" / >
< / Button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Remove member ? < / AlertDialogTitle >
< AlertDialogDescription >
Remove { member . user . name || member . user . email } from { juryGroupDetail . name } ?
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction
onClick = { ( ) = > removeJuryMemberMutation . mutate ( { id : member.id } ) }
className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
Remove
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
2026-02-18 18:23:54 +01:00
< / div >
2026-02-16 12:46:01 +01:00
< / div >
) ) }
< / div >
) }
< / CardContent >
< / Card >
) }
2026-02-24 17:44:55 +01:00
{ /* Jury Group Selector (at bottom for non-evaluation jury rounds) */ }
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< div >
< CardTitle className = "text-base" > Jury Group < / CardTitle >
< CardDescription >
Select or create a jury group for this round
< / CardDescription >
< / div >
< div className = "flex items-center gap-2" >
< Button size = "sm" variant = "outline" onClick = { ( ) = > setCreateJuryOpen ( true ) } >
< Plus className = "h-4 w-4 mr-1.5" / >
New Jury
< / Button >
2026-02-16 12:46:01 +01:00
< / div >
< / div >
2026-02-24 17:44:55 +01:00
< / CardHeader >
< CardContent >
{ juryGroups && juryGroups . length > 0 ? (
< div className = "space-y-4" >
< Select
value = { round . juryGroupId ? ? '__none__' }
onValueChange = { ( value ) = > {
assignJuryMutation . mutate ( {
id : roundId ,
juryGroupId : value === '__none__' ? null : value ,
} )
} }
disabled = { assignJuryMutation . isPending }
>
< SelectTrigger className = "w-full sm:w-80" >
< SelectValue placeholder = "Select jury group..." / >
< / SelectTrigger >
< SelectContent >
< SelectItem value = "__none__" > No jury assigned < / SelectItem >
{ juryGroups . map ( ( jg : any ) = > (
< SelectItem key = { jg . id } value = { jg . id } >
{ jg . name } ( { jg . _count ? . members ? ? 0 } members )
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
{ round . juryGroupId && (
< AlertDialog >
< AlertDialogTrigger asChild >
< Button size = "sm" variant = "ghost" className = "text-destructive hover:text-destructive" >
< Trash2 className = "h-4 w-4 mr-1.5" / >
Delete & quot ; { juryGroup ? . name } & quot ;
< / Button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Delete jury group ? < / AlertDialogTitle >
< AlertDialogDescription >
This will permanently delete & quot ; { juryGroup ? . name } & quot ; and remove all its members .
Rounds using this jury group will be unlinked . This action cannot be undone .
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction
onClick = { ( ) = > deleteJuryMutation . mutate ( { id : round.juryGroupId ! } ) }
className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
disabled = { deleteJuryMutation . isPending }
>
{ deleteJuryMutation . isPending && < Loader2 className = "h-4 w-4 mr-1.5 animate-spin" / > }
Delete Jury
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
) }
< / div >
) : (
< div className = "flex flex-col items-center justify-center py-10 text-center" >
< div className = "rounded-full bg-purple-50 p-4 mb-4" >
< Users className = "h-8 w-8 text-purple-400" / >
< / div >
< p className = "text-sm font-medium" > No Jury Groups < / p >
< p className = "text-xs text-muted-foreground mt-1 max-w-sm" >
Create a jury group to assign members who will evaluate projects in this round .
< / p >
< Button size = "sm" className = "mt-4" onClick = { ( ) = > setCreateJuryOpen ( true ) } >
< Plus className = "h-4 w-4 mr-1.5" / >
Create First Jury
< / Button >
< / div >
) }
< / CardContent >
< / Card >
2026-02-16 12:46:01 +01:00
< / TabsContent >
2026-02-16 16:43:23 +01:00
) }
2026-02-16 12:46:01 +01:00
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
{ /* ═══════════ ASSIGNMENTS TAB (Evaluation rounds — includes Jury section) ═══════════ */ }
2026-02-16 09:20:02 +01:00
{ isEvaluation && (
< TabsContent value = "assignments" className = "space-y-6" >
2026-02-24 17:44:55 +01:00
{ /* 1. Jury Members & Progress (merged) */ }
{ ! round ? . juryGroupId ? (
< Card >
< CardContent className = "py-8 text-center" >
< p className = "text-sm text-muted-foreground" > Select a jury group below to get started . < / p >
< / CardContent >
< / Card >
) : (
< JuryProgressTable
roundId = { roundId }
members = { juryGroupDetail ? . members . map ( ( m : any ) = > ( {
id : m.id ,
userId : m.userId ,
name : m.user.name || 'Unnamed' ,
email : m.user.email ,
maxAssignmentsOverride : m.maxAssignmentsOverride as number | null ,
} ) ) }
onSaveCap = { ( id , val ) = > updateJuryMemberMutation . mutate ( { id , maxAssignmentsOverride : val } ) }
onRemoveMember = { ( id ) = > removeJuryMemberMutation . mutate ( { id } ) }
onAddMember = { ( ) = > setAddMemberOpen ( true ) }
/ >
) }
{ /* 2. Score Distribution (full-width) */ }
< ScoreDistribution roundId = { roundId } / >
{ /* 3. Reassignment History (always open) */ }
< ReassignmentHistory roundId = { roundId } / >
{ /* ── Remaining content only shown when jury group is assigned ── */ }
{ round ? . juryGroupId && (
< >
{ /* 4. Assignments */ }
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< div >
< CardTitle className = "text-base" > Assignments < / CardTitle >
< CardDescription > Individual jury - project assignments and actions < / CardDescription >
< / div >
< div className = "flex flex-wrap items-center gap-2" >
< SendRemindersButton roundId = { roundId } / >
< NotifyJurorsButton roundId = { roundId } / >
< Button variant = "outline" size = "sm" onClick = { ( ) = > setExportOpen ( true ) } >
< Download className = "h-4 w-4 mr-1.5" / >
Export
< / Button >
< / div >
< / div >
< / CardHeader >
< CardContent >
< IndividualAssignmentsTable roundId = { roundId } projectStates = { projectStates } / >
< / CardContent >
< / Card >
{ /* 5. Monitoring — COI + Unassigned Queue */ }
< Card >
< CardHeader >
< CardTitle className = "text-base" > Monitoring < / CardTitle >
< CardDescription > Conflict of interest declarations and unassigned projects < / CardDescription >
< / CardHeader >
< CardContent className = "space-y-6" >
< COIReviewSection roundId = { roundId } / >
2026-03-02 12:48:08 +01:00
< RoundUnassignedQueue roundId = { roundId } requiredReviews = { ( config . requiredReviewsPerProject as number ) || 3 } onAssignUnassigned = { ( ) = > setPreviewSheetOpen ( true ) } / >
2026-02-24 17:44:55 +01:00
< / CardContent >
< / Card >
{ /* 6. Coverage & Generation (collapsible, default collapsed when assignments exist) */ }
< Card >
< CardHeader
className = "cursor-pointer select-none"
onClick = { ( ) = > setCoverageOpen ( ( o ) = > ! o ) }
>
< CardTitle className = "text-base flex items-center gap-2" >
Coverage & amp ; Generation
< ChevronRight className = { cn ( 'h-4 w-4 ml-auto transition-transform' , coverageOpen && 'rotate-90' ) } / >
< / CardTitle >
< CardDescription > Assignment coverage overview and AI generation < / CardDescription >
< / CardHeader >
{ coverageOpen && (
< CardContent className = "space-y-6" >
< CoverageReport roundId = { roundId } requiredReviews = { ( config . requiredReviewsPerProject as number ) || 3 } / >
{ /* Generate Assignments */ }
< div className = { cn ( 'rounded-lg border p-4 space-y-3' , aiAssignmentMutation . isPending && 'border-violet-300 bg-violet-50/30' ) } >
< div className = "flex items-center justify-between" >
< div >
< p className = "text-sm font-medium flex items-center gap-2" >
Assignment Generation
{ aiAssignmentMutation . isPending && (
< Badge variant = "outline" className = "gap-1.5 text-violet-600 border-violet-300 animate-pulse" >
< Loader2 className = "h-3 w-3 animate-spin" / >
AI generating . . .
< / Badge >
) }
{ aiAssignmentMutation . data && ! aiAssignmentMutation . isPending && (
< Badge variant = "outline" className = "gap-1 text-emerald-600 border-emerald-300" >
< CheckCircle2 className = "h-3 w-3" / >
{ aiAssignmentMutation . data . stats . assignmentsGenerated } ready
< / Badge >
) }
< / p >
< p className = "text-xs text-muted-foreground mt-0.5" >
AI - suggested jury - to - project assignments based on expertise and workload
< / p >
< / div >
< div className = "flex items-center gap-2" >
{ aiAssignmentMutation . data && ! aiAssignmentMutation . isPending && (
< Button
size = "sm"
variant = "outline"
onClick = { ( ) = > setPreviewSheetOpen ( true ) }
>
Review Assignments
< / Button >
) }
< Button
size = "sm"
onClick = { ( ) = > {
aiAssignmentMutation . mutate ( {
roundId ,
requiredReviews : ( config . requiredReviewsPerProject as number ) || 3 ,
} )
} }
disabled = { projectCount === 0 || ! juryGroup || aiAssignmentMutation . isPending }
>
{ aiAssignmentMutation . isPending ? (
< >
< Loader2 className = "h-4 w-4 mr-1.5 animate-spin" / >
Generating . . .
< / >
) : (
< >
< Zap className = "h-4 w-4 mr-1.5" / >
{ aiAssignmentMutation . data ? 'Regenerate' : 'Generate with AI' }
< / >
) }
< / Button >
< / div >
< / div >
{ projectCount === 0 && (
< div className = "flex items-center gap-2 p-3 rounded-lg bg-amber-50 border border-amber-200 text-sm text-amber-800" >
< AlertTriangle className = "h-4 w-4 shrink-0" / >
Add projects to this round first .
< / div >
) }
{ juryGroup && projectCount > 0 && ! aiAssignmentMutation . isPending && ! aiAssignmentMutation . data && (
< p className = "text-sm text-muted-foreground" >
Click & quot ; Generate with AI & quot ; to create assignments using GPT analysis of juror expertise , project descriptions , and documents . Or open the preview to use the algorithm instead .
< / p >
) }
{ aiAssignmentMutation . isPending && (
< div className = "flex items-center gap-3 p-3 rounded-lg bg-violet-50 border border-violet-200 dark:bg-violet-950/20 dark:border-violet-800" >
< div className = "relative" >
< div className = "h-8 w-8 rounded-full border-2 border-violet-300 border-t-violet-600 animate-spin" / >
< / div >
< div >
< p className = "text-sm font-medium text-violet-800 dark:text-violet-200" > AI is analyzing projects and jurors . . . < / p >
< p className = "text-xs text-violet-600 dark:text-violet-400" >
Matching expertise , reviewing bios , and balancing workloads
< / p >
< / div >
< / div >
) }
{ aiAssignmentMutation . error && ! aiAssignmentMutation . isPending && (
< div className = "flex items-center gap-3 p-3 rounded-lg bg-red-50 border border-red-200 dark:bg-red-950/20 dark:border-red-800" >
< AlertTriangle className = "h-5 w-5 text-red-600 shrink-0" / >
< div className = "flex-1" >
< p className = "text-sm font-medium text-red-800 dark:text-red-200" >
AI generation failed
< / p >
< p className = "text-xs text-red-600 dark:text-red-400" >
{ aiAssignmentMutation . error . message }
< / p >
< / div >
< / div >
) }
{ aiAssignmentMutation . data && ! aiAssignmentMutation . isPending && (
< div className = "flex items-center gap-3 p-3 rounded-lg bg-emerald-50 border border-emerald-200 dark:bg-emerald-950/20 dark:border-emerald-800" >
< CheckCircle2 className = "h-5 w-5 text-emerald-600 shrink-0" / >
< div className = "flex-1" >
< p className = "text-sm font-medium text-emerald-800 dark:text-emerald-200" >
{ aiAssignmentMutation . data . stats . assignmentsGenerated } assignments generated
< / p >
< p className = "text-xs text-emerald-600 dark:text-emerald-400" >
{ aiAssignmentMutation . data . stats . totalJurors } jurors , { aiAssignmentMutation . data . stats . totalProjects } projects
{ aiAssignmentMutation . data . fallbackUsed && ' (algorithm fallback)' }
< / p >
< / div >
< Button size = "sm" variant = "outline" onClick = { ( ) = > setPreviewSheetOpen ( true ) } >
Review & amp ; Execute
< / Button >
< / div >
) }
< / div >
< / CardContent >
) }
< / Card >
{ /* 7. Jury Group (at bottom) */ }
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< Card >
< CardHeader >
< div className = "flex items-center justify-between" >
< div >
< CardTitle className = "text-base" > Jury Group < / CardTitle >
< CardDescription >
Select or create a jury group for this round
< / CardDescription >
2026-02-19 12:59:35 +01:00
< / div >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< div className = "flex items-center gap-2" >
< Button size = "sm" variant = "outline" onClick = { ( ) = > setCreateJuryOpen ( true ) } >
< Plus className = "h-4 w-4 mr-1.5" / >
New Jury
< / Button >
< / div >
< / div >
< / CardHeader >
< CardContent >
{ juryGroups && juryGroups . length > 0 ? (
< div className = "space-y-4" >
< Select
value = { round . juryGroupId ? ? '__none__' }
onValueChange = { ( value ) = > {
assignJuryMutation . mutate ( {
id : roundId ,
juryGroupId : value === '__none__' ? null : value ,
} )
} }
disabled = { assignJuryMutation . isPending }
>
< SelectTrigger className = "w-full sm:w-80" >
< SelectValue placeholder = "Select jury group..." / >
< / SelectTrigger >
< SelectContent >
< SelectItem value = "__none__" > No jury assigned < / SelectItem >
{ juryGroups . map ( ( jg : any ) = > (
< SelectItem key = { jg . id } value = { jg . id } >
{ jg . name } ( { jg . _count ? . members ? ? 0 } members )
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
{ round . juryGroupId && (
< AlertDialog >
< AlertDialogTrigger asChild >
< Button size = "sm" variant = "ghost" className = "text-destructive hover:text-destructive" >
< Trash2 className = "h-4 w-4 mr-1.5" / >
Delete & quot ; { juryGroup ? . name } & quot ;
< / Button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Delete jury group ? < / AlertDialogTitle >
< AlertDialogDescription >
This will permanently delete & quot ; { juryGroup ? . name } & quot ; and remove all its members .
Rounds using this jury group will be unlinked . This action cannot be undone .
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< AlertDialogAction
onClick = { ( ) = > deleteJuryMutation . mutate ( { id : round.juryGroupId ! } ) }
className = "bg-destructive text-destructive-foreground hover:bg-destructive/90"
disabled = { deleteJuryMutation . isPending }
>
{ deleteJuryMutation . isPending && < Loader2 className = "h-4 w-4 mr-1.5 animate-spin" / > }
Delete Jury
< / AlertDialogAction >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
) }
< / div >
) : (
< div className = "flex flex-col items-center justify-center py-10 text-center" >
< div className = "rounded-full bg-purple-50 p-4 mb-4" >
< Users className = "h-8 w-8 text-purple-400" / >
< / div >
< p className = "text-sm font-medium" > No Jury Groups < / p >
< p className = "text-xs text-muted-foreground mt-1 max-w-sm" >
Create a jury group to assign members who will evaluate projects in this round .
< / p >
< Button size = "sm" className = "mt-4" onClick = { ( ) = > setCreateJuryOpen ( true ) } >
< Plus className = "h-4 w-4 mr-1.5" / >
Create First Jury
< / Button >
< / div >
) }
< / CardContent >
< / Card >
2026-02-16 09:20:02 +01:00
{ /* Assignment Preview Sheet */ }
< AssignmentPreviewSheet
roundId = { roundId }
open = { previewSheetOpen }
onOpenChange = { setPreviewSheetOpen }
2026-02-17 12:33:20 +01:00
requiredReviews = { ( config . requiredReviewsPerProject as number ) || 3 }
2026-02-17 14:45:57 +01:00
aiResult = { aiAssignmentMutation . data ? ? null }
isAIGenerating = { aiAssignmentMutation . isPending }
onGenerateAI = { ( ) = > aiAssignmentMutation . mutate ( {
roundId ,
requiredReviews : ( config . requiredReviewsPerProject as number ) || 3 ,
} ) }
onResetAI = { ( ) = > aiAssignmentMutation . reset ( ) }
2026-02-16 09:20:02 +01:00
/ >
{ /* CSV Export Dialog */ }
< ExportEvaluationsDialog roundId = { roundId } open = { exportOpen } onOpenChange = { setExportOpen } / >
2026-02-19 12:59:35 +01:00
< / >
) }
2026-02-16 09:20:02 +01:00
< / TabsContent >
) }
2026-02-27 09:41:59 +01:00
{ /* ═══════════ RANKING TAB (EVALUATION rounds) ═══════════ */ }
{ isEvaluation && (
< TabsContent value = "ranking" className = "space-y-4" >
< RankingDashboard competitionId = { competitionId } roundId = { roundId } / >
< / TabsContent >
) }
2026-02-16 09:20:02 +01:00
{ /* ═══════════ CONFIG TAB ═══════════ */ }
< TabsContent value = "config" className = "space-y-6" >
2026-02-16 16:43:23 +01:00
{ /* Round Dates */ }
< Card >
< CardHeader className = "border-b" >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< ConfigSectionHeader
title = "Round Dates"
description = "When this round starts and ends. Defines the active period for document uploads and evaluations."
status = { round . windowOpenAt && round . windowCloseAt ? 'complete' : 'error' }
summary = {
round . windowOpenAt && round . windowCloseAt
? ` ${ new Date ( round . windowOpenAt ) . toLocaleDateString ( ) } — ${ new Date ( round . windowCloseAt ) . toLocaleDateString ( ) } `
: undefined
}
/ >
2026-02-16 16:43:23 +01:00
< / CardHeader >
< CardContent className = "pt-4" >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
{ ! round . windowOpenAt && ! round . windowCloseAt && (
< div className = "mb-3 rounded-md border border-amber-200 bg-amber-50 px-3 py-2" >
< p className = "text-xs text-amber-700" > Dates not set — this round cannot be activated without dates . < / p >
< / div >
) }
2026-02-16 16:43:23 +01:00
< div className = "grid gap-4 sm:grid-cols-2" >
< div className = "space-y-2" >
< Label > Start Date < / Label >
< DateTimePicker
value = { round . windowOpenAt ? new Date ( round . windowOpenAt ) : null }
2026-02-19 12:59:35 +01:00
onChange = { ( date ) = > updateMutation . mutate ( { id : roundId , windowOpenAt : date } , { onSuccess : ( ) = > toast . success ( 'Dates saved' ) } ) }
2026-02-16 16:43:23 +01:00
placeholder = "Select start date & time"
clearable
/ >
< / div >
< div className = "space-y-2" >
< Label > End Date < / Label >
< DateTimePicker
value = { round . windowCloseAt ? new Date ( round . windowCloseAt ) : null }
2026-02-19 12:59:35 +01:00
onChange = { ( date ) = > updateMutation . mutate ( { id : roundId , windowCloseAt : date } , { onSuccess : ( ) = > toast . success ( 'Dates saved' ) } ) }
2026-02-16 16:43:23 +01:00
placeholder = "Select end date & time"
clearable
/ >
< / div >
< / div >
< / CardContent >
< / Card >
2026-02-16 09:20:02 +01:00
{ /* General Round Settings */ }
< Card >
2026-02-16 12:38:28 +01:00
< CardHeader className = "border-b" >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< ConfigSectionHeader
title = "General Settings"
description = "Settings that apply to this round regardless of type"
status = {
isEvaluation && ! ( config . startupAdvanceCount as number ) && ! ( config . conceptAdvanceCount as number )
? 'warning'
: 'complete'
}
summary = {
( config . startupAdvanceCount as number ) || ( config . conceptAdvanceCount as number )
? ` Target: ${ config . startupAdvanceCount ? ? 0 } startup, ${ config . conceptAdvanceCount ? ? 0 } concept `
: undefined
}
/ >
2026-02-16 09:20:02 +01:00
< / CardHeader >
2026-02-16 12:38:28 +01:00
< CardContent className = "space-y-0 pt-0" >
< div className = "flex items-center justify-between p-4 rounded-md" >
2026-02-16 09:20:02 +01:00
< div className = "space-y-0.5" >
< Label htmlFor = "notify-on-entry" className = "text-sm font-medium" >
Notify on round entry
< / Label >
< p className = "text-xs text-muted-foreground" >
Send an automated email to project applicants when their project enters this round
< / p >
< / div >
< Switch
id = "notify-on-entry"
checked = { ! ! config . notifyOnEntry }
onCheckedChange = { ( checked ) = > {
handleConfigChange ( { . . . config , notifyOnEntry : checked } )
} }
/ >
< / div >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
2026-02-16 12:38:28 +01:00
< div className = "flex items-center justify-between p-4 rounded-md bg-muted/30" >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< div className = "space-y-0.5" >
< Label htmlFor = "notify-on-advance" className = "text-sm font-medium" >
Notify on advance
< / Label >
< p className = "text-xs text-muted-foreground" >
Send an email to project applicants when their project advances from this round to the next
< / p >
< / div >
< Switch
id = "notify-on-advance"
checked = { ! ! config . notifyOnAdvance }
onCheckedChange = { ( checked ) = > {
handleConfigChange ( { . . . config , notifyOnAdvance : checked } )
} }
/ >
< / div >
2026-03-03 09:47:20 +01:00
{ ( isEvaluation || isFiltering ) && (
2026-02-16 12:38:28 +01:00
< div className = "border-t mt-2 pt-4 px-4 pb-2 bg-[#053d57]/[0.03] rounded-b-lg -mx-6 -mb-6 p-6" >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< Label className = "text-sm font-medium" > Advancement Targets < / Label >
< p className = "text-xs text-muted-foreground mb-3" >
2026-03-02 12:48:08 +01:00
How to determine which projects advance from this round
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< / p >
2026-03-02 12:48:08 +01:00
{ /* Mode toggle */ }
< div className = "flex gap-2 mb-4" >
< Button
type = "button"
size = "sm"
variant = { ( config . advanceMode as string ) === 'threshold' ? 'outline' : 'default' }
className = "h-8 text-xs"
onClick = { ( ) = > handleConfigChange ( { . . . config , advanceMode : 'count' } ) }
>
Fixed Count
< / Button >
< Button
type = "button"
size = "sm"
variant = { ( config . advanceMode as string ) === 'threshold' ? 'default' : 'outline' }
className = "h-8 text-xs"
onClick = { ( ) = > handleConfigChange ( { . . . config , advanceMode : 'threshold' } ) }
>
Score Threshold
< / Button >
< / div >
{ ( config . advanceMode as string ) === 'threshold' ? (
< div className = "space-y-2" >
< Label htmlFor = "advance-threshold" className = "text-xs text-muted-foreground" >
Minimum Average Score to Advance
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< / Label >
2026-03-02 12:48:08 +01:00
< p className = "text-xs text-muted-foreground" >
All projects scoring at or above this threshold will advance ( both categories )
< / p >
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< Input
2026-03-02 12:48:08 +01:00
id = "advance-threshold"
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
type = "number"
min = { 0 }
2026-03-02 12:48:08 +01:00
max = { 10 }
step = { 0.1 }
className = "h-9 w-32"
placeholder = "e.g. 6.5"
value = { ( config . advanceScoreThreshold as number ) ? ? '' }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
onChange = { ( e ) = > {
2026-03-02 12:48:08 +01:00
const val = e . target . value ? parseFloat ( e . target . value ) : undefined
handleConfigChange ( { . . . config , advanceScoreThreshold : val } )
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
} }
/ >
< / div >
2026-03-02 12:48:08 +01:00
) : (
< >
{ isEvaluation && ! ( config . startupAdvanceCount as number ) && ! ( config . conceptAdvanceCount as number ) && (
< div className = "mb-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2" >
< p className = "text-xs text-amber-700" > Advancement targets not configured — all passed projects will be eligible to advance . < / p >
< / div >
) }
< p className = "text-xs text-muted-foreground mb-2" >
Target number of projects per category to advance
< / p >
< div className = "grid grid-cols-2 gap-4" >
< div className = "space-y-1.5" >
< Label htmlFor = "startup-advance-count" className = "text-xs text-muted-foreground" >
Startup Projects
< / Label >
< Input
id = "startup-advance-count"
type = "number"
min = { 0 }
className = "h-9"
placeholder = "No limit"
value = { ( config . startupAdvanceCount as number ) ? ? '' }
onChange = { ( e ) = > {
const val = e . target . value ? parseInt ( e . target . value , 10 ) : undefined
handleConfigChange ( { . . . config , startupAdvanceCount : val } )
} }
/ >
< / div >
< div className = "space-y-1.5" >
< Label htmlFor = "concept-advance-count" className = "text-xs text-muted-foreground" >
Concept Projects
< / Label >
< Input
id = "concept-advance-count"
type = "number"
min = { 0 }
className = "h-9"
placeholder = "No limit"
value = { ( config . conceptAdvanceCount as number ) ? ? '' }
onChange = { ( e ) = > {
const val = e . target . value ? parseInt ( e . target . value , 10 ) : undefined
handleConfigChange ( { . . . config , conceptAdvanceCount : val } )
} }
/ >
< / div >
< / div >
< / >
) }
AI category-aware evaluation: per-round config, file parsing, shortlist, advance flow
- Per-juror cap mode (HARD/SOFT/NONE) in add-member dialog and members table
- Jury invite flow: create user + add to group + send invitation from dialog
- Per-round config: notifyOnAdvance, aiParseFiles, startupAdvanceCount, conceptAdvanceCount
- Moved notify-on-advance from competition-level to per-round setting
- AI filtering: round-tagged files with newest-first sorting, optional file content extraction
- File content extractor service (pdf-parse for PDF, utf-8 for text files)
- AI shortlist runs independently per category (STARTUP / BUSINESS_CONCEPT)
- generateAIRecommendations tRPC endpoint with per-round config integration
- AI recommendations UI: trigger button, confirmation dialog, per-category results display
- Category-aware advance dialog: select/deselect projects by category with target caps
- STAGE_ACTIVE bug fix in assignment router
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 10:09:52 +01:00
< / div >
2026-03-03 09:47:20 +01:00
) }
2026-02-16 09:20:02 +01:00
< / CardContent >
< / Card >
{ /* Round-type-specific config */ }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< RoundConfigForm
roundType = { round . roundType }
config = { config }
onChange = { handleConfigChange }
2026-02-16 09:20:02 +01:00
juryGroups = { juryGroups ? . map ( ( jg : any ) = > ( { id : jg.id , name : jg.name } ) ) }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
/ >
2026-02-16 09:20:02 +01:00
{ /* Evaluation Criteria Editor (EVALUATION rounds only) */ }
{ isEvaluation && < EvaluationCriteriaEditor roundId = { roundId } / > }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
2026-02-17 14:13:25 +01:00
{ /* Document Requirements — hidden for EVALUATION rounds unless requireDocumentUpload is on */ }
{ ( ! isEvaluation || ! ! ( config . requireDocumentUpload as boolean ) ) && (
< Card >
< CardHeader >
Admin dashboard & round management UX overhaul
- Extract round detail monolith (2900→600 lines) into 13 standalone components
- Add shared round/status config (round-config.ts) replacing 4 local copies
- Delete 12 legacy competition-scoped pages, merge project pool into projects page
- Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary)
- Add contextual header quick actions based on active round type
- Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix
- Add config tab completion dots (green/amber/red) and inline validation warnings
- Enhance juries page with round assignments, member avatars, and cap mode badges
- Add context-aware project list (recent submissions vs active evaluations)
- Move competition settings into Manage Editions page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:14:00 +01:00
< ConfigSectionHeader
title = "Document Requirements"
description = {
` Files applicants must submit for this round ` +
( round . windowCloseAt ? ` — due by ${ new Date ( round . windowCloseAt ) . toLocaleDateString ( ) } ` : '' )
}
status = { ( fileRequirements ? . length ? ? 0 ) > 0 ? 'complete' : 'warning' }
summary = {
( fileRequirements ? . length ? ? 0 ) > 0
? ` ${ fileRequirements ? . length } requirement(s) `
: undefined
}
/ >
2026-02-17 14:13:25 +01:00
< / CardHeader >
< CardContent >
< FileRequirementsEditor
roundId = { roundId }
windowOpenAt = { round . windowOpenAt }
windowCloseAt = { round . windowCloseAt }
/ >
< / CardContent >
< / Card >
) }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / TabsContent >
2026-02-16 09:20:02 +01:00
{ /* ═══════════ AWARDS TAB ═══════════ */ }
2026-02-16 16:43:23 +01:00
{ hasAwards && (
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< TabsContent value = "awards" className = "space-y-4" >
< Card >
< CardContent className = "p-6" >
{ roundAwards . length === 0 ? (
2026-02-16 12:38:28 +01:00
< div className = "text-center py-12 text-muted-foreground" >
< div className = "rounded-full bg-[#de0f1e]/10 p-4 w-fit mx-auto mb-4" >
< Trophy className = "h-8 w-8 text-[#de0f1e]/60" / >
< / div >
< p className = "text-sm font-medium text-foreground" > No Awards Linked < / p >
< p className = "text-xs mt-1 max-w-sm mx-auto" >
2026-02-16 09:20:02 +01:00
Create an award and set this round as its evaluation round to see it here
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / p >
< / div >
) : (
< div className = "space-y-3" >
{ roundAwards . map ( ( award ) = > {
const eligibleCount = award . _count ? . eligibilities || 0
return (
< Link
key = { award . id }
href = { ` /admin/awards/ ${ award . id } ` as Route }
className = "block"
>
2026-02-16 12:38:28 +01:00
< div className = "flex items-start justify-between gap-4 rounded-lg border border-l-4 border-l-[#de0f1e] p-4 transition-all hover:shadow-md hover:-translate-y-0.5" >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< div className = "flex-1 min-w-0" >
< div className = "flex items-center gap-2 mb-1" >
< h3 className = "font-medium truncate" > { award . name } < / h3 >
< Badge
variant = {
award . eligibilityMode === 'SEPARATE_POOL'
? 'default'
: 'secondary'
}
className = "shrink-0"
>
{ award . eligibilityMode === 'SEPARATE_POOL'
? 'Separate Pool'
: 'Stay in Main' }
< / Badge >
< / div >
{ award . description && (
< p className = "text-sm text-muted-foreground line-clamp-1" >
{ award . description }
< / p >
) }
< / div >
< div className = "flex items-center gap-4 text-sm text-muted-foreground shrink-0" >
< div className = "text-right" >
2026-02-16 09:20:02 +01:00
< div className = "font-medium text-foreground" > { eligibleCount } < / div >
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< div className = "text-xs" > eligible < / div >
< / div >
< / div >
< / div >
< / Link >
)
} ) }
< / div >
) }
< / CardContent >
< / Card >
< / TabsContent >
2026-02-16 16:43:23 +01:00
) }
Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION
- Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards)
- Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review)
- Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone
- Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields
- Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish
- Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors
- Bulk upload page for admin project imports
- File router enhanced with admin upload and submission window procedures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 01:16:55 +01:00
< / Tabs >
2026-02-17 16:43:47 +01:00
2026-02-24 17:44:55 +01:00
{ /* ── Page-level dialogs (shared between jury/assignments tabs) ── */ }
< Dialog open = { createJuryOpen } onOpenChange = { setCreateJuryOpen } >
< DialogContent >
< DialogHeader >
< DialogTitle > Create Jury Group < / DialogTitle >
< DialogDescription >
Create a new jury group for this competition . It will be automatically assigned to this round .
< / DialogDescription >
< / DialogHeader >
< div className = "space-y-4" >
< div className = "space-y-2" >
< label className = "text-sm font-medium" > Name < / label >
< Input
placeholder = "e.g. Round 1 Jury, Expert Panel, Final Jury"
value = { newJuryName }
onChange = { ( e ) = > setNewJuryName ( e . target . value ) }
onKeyDown = { ( e ) = > {
if ( e . key === 'Enter' && newJuryName . trim ( ) ) {
createJuryMutation . mutate ( {
competitionId ,
name : newJuryName.trim ( ) ,
slug : newJuryName.trim ( ) . toLowerCase ( ) . replace ( /[^a-z0-9]+/g , '-' ) . replace ( /(^-|-$)/g , '' ) ,
} )
}
} }
/ >
< / div >
< / div >
< DialogFooter >
< Button variant = "outline" onClick = { ( ) = > setCreateJuryOpen ( false ) } > Cancel < / Button >
< Button
onClick = { ( ) = > {
createJuryMutation . mutate ( {
competitionId ,
name : newJuryName.trim ( ) ,
slug : newJuryName.trim ( ) . toLowerCase ( ) . replace ( /[^a-z0-9]+/g , '-' ) . replace ( /(^-|-$)/g , '' ) ,
} )
} }
disabled = { createJuryMutation . isPending || ! newJuryName . trim ( ) }
>
{ createJuryMutation . isPending && < Loader2 className = "h-4 w-4 mr-1.5 animate-spin" / > }
Create
< / Button >
< / DialogFooter >
< / DialogContent >
< / Dialog >
{ juryGroupId && (
< AddMemberDialog
juryGroupId = { juryGroupId }
open = { addMemberOpen }
onOpenChange = { ( open ) = > {
setAddMemberOpen ( open )
if ( ! open ) utils . juryGroup . getById . invalidate ( { id : juryGroupId } )
} }
/ >
) }
2026-02-18 14:59:23 +01:00
{ /* Autosave error bar — only shows when save fails */ }
{ autosaveStatus === 'error' && (
< div className = "fixed bottom-0 left-0 right-0 z-50 border-t bg-red-50 dark:bg-red-950/50 shadow-[0_-4px_12px_rgba(0,0,0,0.1)]" >
2026-02-17 16:43:47 +01:00
< div className = "container flex items-center justify-between py-3 px-4 max-w-5xl mx-auto" >
2026-02-18 14:59:23 +01:00
< div className = "flex items-center gap-2 text-sm text-red-700 dark:text-red-300" >
< AlertTriangle className = "h-4 w-4" / >
< span > Auto - save failed < / span >
2026-02-17 16:43:47 +01:00
< / div >
2026-02-18 14:59:23 +01:00
< Button
size = "sm"
onClick = { saveConfig }
disabled = { updateMutation . isPending }
className = "bg-[#de0f1e] hover:bg-[#c00d1a] text-white"
>
{ updateMutation . isPending ? (
< > < Loader2 className = "h-3.5 w-3.5 mr-1.5 animate-spin" / > Retrying . . . < / >
) : (
< > < Save className = "h-3.5 w-3.5 mr-1.5" / > Retry Save < / >
2026-02-17 16:43:47 +01:00
) }
2026-02-18 14:59:23 +01:00
< / Button >
2026-02-17 16:43:47 +01:00
< / div >
< / div >
) }
2026-02-23 16:08:46 +01:00
{ memberTransferJuror && (
< TransferAssignmentsDialog
roundId = { roundId }
sourceJuror = { memberTransferJuror }
open = { ! ! memberTransferJuror }
onClose = { ( ) = > setMemberTransferJuror ( null ) }
/ >
) }
2026-02-16 09:20:02 +01:00
< / div >
)
}