'use client' import { useState } from 'react' import { useParams } from 'next/navigation' import Link from 'next/link' import type { Route } from 'next' import { trpc } from '@/lib/trpc/client' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Skeleton } from '@/components/ui/skeleton' import { Badge } from '@/components/ui/badge' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { cn } from '@/lib/utils' import { ArrowLeft, Save, Loader2, ChevronDown, Play, Square, Archive, } from 'lucide-react' import { RoundConfigForm } from '@/components/admin/competition/round-config-form' import { ProjectStatesTable } from '@/components/admin/round/project-states-table' import { SubmissionWindowManager } from '@/components/admin/round/submission-window-manager' import { FileRequirementsEditor } from '@/components/admin/rounds/config/file-requirements-editor' const roundTypeColors: Record = { INTAKE: 'bg-gray-100 text-gray-700', FILTERING: 'bg-amber-100 text-amber-700', EVALUATION: 'bg-blue-100 text-blue-700', SUBMISSION: 'bg-purple-100 text-purple-700', MENTORING: 'bg-teal-100 text-teal-700', LIVE_FINAL: 'bg-red-100 text-red-700', DELIBERATION: 'bg-indigo-100 text-indigo-700', } const roundStatusConfig: Record = { ROUND_DRAFT: { label: 'Draft', bgClass: 'bg-gray-100 text-gray-700' }, ROUND_ACTIVE: { label: 'Active', bgClass: 'bg-emerald-100 text-emerald-700' }, ROUND_CLOSED: { label: 'Closed', bgClass: 'bg-blue-100 text-blue-700' }, ROUND_ARCHIVED: { label: 'Archived', bgClass: 'bg-muted text-muted-foreground' }, } export default function RoundDetailPage() { const params = useParams() const roundId = params.roundId as string const [config, setConfig] = useState>({}) const [hasChanges, setHasChanges] = useState(false) const [confirmAction, setConfirmAction] = useState(null) const utils = trpc.useUtils() const { data: round, isLoading } = trpc.round.getById.useQuery({ id: roundId }) // Fetch competition for jury groups (DELIBERATION) and awards const competitionId = round?.competitionId const { data: competition } = trpc.competition.getById.useQuery( { id: competitionId! }, { enabled: !!competitionId } ) const juryGroups = competition?.juryGroups?.map((g: any) => ({ id: g.id, name: g.name })) // Fetch awards linked to this round const programId = competition?.programId const { data: awards } = trpc.specialAward.list.useQuery( { programId: programId! }, { enabled: !!programId } ) // Filter awards by this round const roundAwards = awards?.filter((a) => a.evaluationRoundId === roundId) || [] // Update local config when round data changes if (round && !hasChanges) { const roundConfig = (round.configJson as Record) ?? {} if (JSON.stringify(roundConfig) !== JSON.stringify(config)) { setConfig(roundConfig) } } const updateMutation = trpc.round.update.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round configuration saved') setHasChanges(false) }, onError: (err) => toast.error(err.message), }) // Round lifecycle mutations const activateMutation = trpc.roundEngine.activate.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round activated') setConfirmAction(null) }, onError: (err) => toast.error(err.message), }) const closeMutation = trpc.roundEngine.close.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round closed') setConfirmAction(null) }, onError: (err) => toast.error(err.message), }) const archiveMutation = trpc.roundEngine.archive.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round archived') setConfirmAction(null) }, onError: (err) => toast.error(err.message), }) const handleConfigChange = (newConfig: Record) => { setConfig(newConfig) setHasChanges(true) } const handleSave = () => { updateMutation.mutate({ id: roundId, configJson: config }) } const handleLifecycleAction = () => { if (confirmAction === 'activate') activateMutation.mutate({ roundId }) else if (confirmAction === 'close') closeMutation.mutate({ roundId }) else if (confirmAction === 'archive') archiveMutation.mutate({ roundId }) } const isLifecyclePending = activateMutation.isPending || closeMutation.isPending || archiveMutation.isPending if (isLoading) { return (
) } if (!round) { return (

Round Not Found

The requested round does not exist

) } const statusCfg = roundStatusConfig[round.status] ?? roundStatusConfig.ROUND_DRAFT const canActivate = round.status === 'ROUND_DRAFT' const canClose = round.status === 'ROUND_ACTIVE' const canArchive = round.status === 'ROUND_CLOSED' return (
{/* Header */}

{round.name}

{round.roundType.replace('_', ' ')} {/* Status Dropdown */} {canActivate && ( setConfirmAction('activate')}> Activate Round )} {canClose && ( setConfirmAction('close')}> Close Round )} {canArchive && ( <> setConfirmAction('archive')}> Archive Round )} {!canActivate && !canClose && !canArchive && ( No actions available )}

{round.slug}

{hasChanges && ( )}
{/* Tabs */} Configuration Projects Submission Windows Documents Awards {roundAwards.length > 0 && ( {roundAwards.length} )} {roundAwards.length === 0 ? (

No awards linked to this round

Create an award and set this round as its source round to see it here

) : (
{roundAwards.map((award) => { const eligibleCount = award._count?.eligibilities || 0 const autoTagRules = award.autoTagRulesJson as { rules?: unknown[] } | null const ruleCount = autoTagRules?.rules?.length || 0 return (

{award.name}

{award.eligibilityMode === 'SEPARATE_POOL' ? 'Separate Pool' : 'Stay in Main'}
{award.description && (

{award.description}

)}
{ruleCount}
{ruleCount === 1 ? 'rule' : 'rules'}
{eligibleCount}
eligible
) })}
)}
{/* Lifecycle Confirmation Dialog */} setConfirmAction(null)}> {confirmAction === 'activate' && 'Activate Round'} {confirmAction === 'close' && 'Close Round'} {confirmAction === 'archive' && 'Archive Round'} {confirmAction === 'activate' && 'This will open the round for submissions and evaluations. Projects will be able to enter this round.'} {confirmAction === 'close' && 'This will close the round. No more submissions or evaluations will be accepted.'} {confirmAction === 'archive' && 'This will archive the round. It will no longer appear in active views.'}
) }