'use client' import { useState } from 'react' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Progress } from '@/components/ui/progress' import { Skeleton } from '@/components/ui/skeleton' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { FileSpreadsheet, BarChart3, Users, ClipboardList, CheckCircle2, TrendingUp, GitCompare, UserCheck, Globe, } from 'lucide-react' import { formatDateOnly } from '@/lib/utils' import { ScoreDistributionChart, EvaluationTimelineChart, StatusBreakdownChart, JurorWorkloadChart, ProjectRankingsChart, CriteriaScoresChart, CrossStageComparisonChart, JurorConsistencyChart, DiversityMetricsChart, } from '@/components/charts' import { ExportPdfButton } from '@/components/shared/export-pdf-button' import { AnimatedCard } from '@/components/shared/animated-container' // Parse selection value: "all:programId" for edition-wide, or stageId function parseSelection(value: string | null): { stageId?: string; programId?: string } { if (!value) return {} if (value.startsWith('all:')) return { programId: value.slice(4) } return { stageId: value } } function OverviewTab({ selectedValue }: { selectedValue: string | null }) { const { data: programs, isLoading } = trpc.program.list.useQuery({ includeStages: true }) const stages = programs?.flatMap(p => (p.stages as { id: string; name: string; status: string; windowCloseAt: Date | null; _count: { projects: number; assignments: number } }[]).map(s => ({ ...s, programName: `${p.year} Edition`, })) ) || [] const queryInput = parseSelection(selectedValue) const hasSelection = !!queryInput.stageId || !!queryInput.programId const { data: overviewStats, isLoading: statsLoading } = trpc.analytics.getOverviewStats.useQuery( queryInput, { enabled: hasSelection } ) if (isLoading) { return (
{[...Array(4)].map((_, i) => ( ))}
) } const totalProjects = stages.reduce((acc, s) => acc + (s._count?.projects || 0), 0) const activeStages = stages.filter((s) => s.status === 'STAGE_ACTIVE').length const totalPrograms = programs?.length || 0 return (
{/* Quick Stats */}

Total Stages

{stages.length}

{activeStages} active

Total Projects

{totalProjects}

Across all stages

Active Stages

{activeStages}

Currently active

Programs

{totalPrograms}

Total programs

{/* Round/edition-specific overview stats */} {hasSelection && ( <> {statsLoading ? (
{[...Array(4)].map((_, i) => ( ))}
) : overviewStats ? (

{queryInput.programId ? 'Edition Overview' : 'Selected Round Details'}

Projects
{overviewStats.projectCount}

In this round

Assignments
{overviewStats.assignmentCount}

{overviewStats.jurorCount} jurors

Evaluations
{overviewStats.evaluationCount}

Submitted

Completion
{overviewStats.completionRate}%
) : null} )} {/* Stages Table - Desktop */} Stage Reports Progress overview for each stage Stage Program Projects Status {stages.map((stage) => (

{stage.name}

{stage.windowCloseAt && (

Ends: {formatDateOnly(stage.windowCloseAt)}

)}
{stage.programName} {stage._count?.projects || '-'} {stage.status === 'STAGE_ACTIVE' ? 'Active' : stage.status === 'STAGE_CLOSED' ? 'Closed' : stage.status}
))}
{/* Stages Cards - Mobile */}

Stage Reports

{stages.map((stage) => (

{stage.name}

{stage.status === 'STAGE_ACTIVE' ? 'Active' : stage.status === 'STAGE_CLOSED' ? 'Closed' : stage.status}

{stage.programName}

{stage.windowCloseAt && (

Ends: {formatDateOnly(stage.windowCloseAt)}

)}
{stage._count?.projects || 0} projects
))}
) } function AnalyticsTab({ selectedValue }: { selectedValue: string }) { const queryInput = parseSelection(selectedValue) const hasSelection = !!queryInput.stageId || !!queryInput.programId const { data: scoreDistribution, isLoading: scoreLoading } = trpc.analytics.getScoreDistribution.useQuery( queryInput, { enabled: hasSelection } ) const { data: timeline, isLoading: timelineLoading } = trpc.analytics.getEvaluationTimeline.useQuery( queryInput, { enabled: hasSelection } ) const { data: statusBreakdown, isLoading: statusLoading } = trpc.analytics.getStatusBreakdown.useQuery( queryInput, { enabled: hasSelection } ) const { data: jurorWorkload, isLoading: workloadLoading } = trpc.analytics.getJurorWorkload.useQuery( queryInput, { enabled: hasSelection } ) const { data: projectRankings, isLoading: rankingsLoading } = trpc.analytics.getProjectRankings.useQuery( { ...queryInput, limit: 15 }, { enabled: hasSelection } ) const { data: criteriaScores, isLoading: criteriaLoading } = trpc.analytics.getCriteriaScores.useQuery( queryInput, { enabled: hasSelection } ) return (
{/* Row 1: Score Distribution & Status Breakdown */}
{scoreLoading ? ( ) : scoreDistribution ? ( ) : null} {statusLoading ? ( ) : statusBreakdown ? ( ) : null}
{/* Row 2: Evaluation Timeline */} {timelineLoading ? ( ) : timeline?.length ? ( ) : (

No evaluation data available yet

)} {/* Row 3: Criteria Scores */} {criteriaLoading ? ( ) : criteriaScores?.length ? ( ) : null} {/* Row 4: Juror Workload */} {workloadLoading ? ( ) : jurorWorkload?.length ? ( ) : (

No juror assignments yet

)} {/* Row 5: Project Rankings */} {rankingsLoading ? ( ) : projectRankings?.length ? ( ) : (

No project scores available yet

)}
) } function CrossStageTab() { const { data: programs, isLoading: programsLoading } = trpc.program.list.useQuery({ includeStages: true }) const stages = programs?.flatMap(p => ((p.stages || []) as Array<{ id: string; name: string }>).map(s => ({ id: s.id, name: s.name, programName: `${p.year} Edition` })) ) || [] const [selectedStageIds, setSelectedStageIds] = useState([]) const { data: comparison, isLoading: comparisonLoading } = trpc.analytics.getCrossStageComparison.useQuery( { stageIds: selectedStageIds }, { enabled: selectedStageIds.length >= 2 } ) const toggleStage = (stageId: string) => { setSelectedStageIds((prev) => prev.includes(stageId) ? prev.filter((id) => id !== stageId) : [...prev, stageId] ) } if (programsLoading) return return (
Select Stages to Compare Choose at least 2 stages
{stages.map((stage) => ( toggleStage(stage.id)} > {stage.programName} - {stage.name} ))}
{selectedStageIds.length < 2 && (

Select at least 2 stages to enable comparison

)}
{comparisonLoading && selectedStageIds.length >= 2 && } {comparison && ( } /> )}
) } function JurorConsistencyTab({ selectedValue }: { selectedValue: string }) { const queryInput = parseSelection(selectedValue) const hasSelection = !!queryInput.stageId || !!queryInput.programId const { data: consistency, isLoading } = trpc.analytics.getJurorConsistency.useQuery( queryInput, { enabled: hasSelection } ) if (isLoading) return if (!consistency) return null return ( }} /> ) } function DiversityTab({ selectedValue }: { selectedValue: string }) { const queryInput = parseSelection(selectedValue) const hasSelection = !!queryInput.stageId || !!queryInput.programId const { data: diversity, isLoading } = trpc.analytics.getDiversityMetrics.useQuery( queryInput, { enabled: hasSelection } ) if (isLoading) return if (!diversity) return null return ( ) } export default function ObserverReportsPage() { const [selectedValue, setSelectedValue] = useState(null) const { data: programs, isLoading: stagesLoading } = trpc.program.list.useQuery({ includeStages: true }) const stages = programs?.flatMap(p => (p.stages as { id: string; name: string; status: string; windowCloseAt: Date | null; _count: { projects: number; assignments: number } }[]).map(s => ({ ...s, programId: p.id, programName: `${p.year} Edition`, })) ) || [] // Set default selected stage if (stages.length && !selectedValue) { setSelectedValue(stages[0].id) } const hasSelection = !!selectedValue const selectedStage = stages.find((s) => s.id === selectedValue) return (
{/* Header */}

Reports

View evaluation progress and statistics

{/* Stage Selector */}
{stagesLoading ? ( ) : stages.length > 0 ? ( ) : (

No stages available

)}
{/* Tabs */}
Overview Analytics Cross-Round Juror Consistency Diversity {selectedValue && !selectedValue.startsWith('all:') && ( )}
{hasSelection ? ( ) : (

Select a round

Choose a round or edition from the dropdown above to view analytics

)}
{hasSelection ? ( ) : (

Select a round

Choose a round or edition above to view juror consistency metrics

)}
{hasSelection ? ( ) : (

Select a round

Choose a round or edition above to view diversity metrics

)}
) }