'use client' import Link from 'next/link' import type { Route } from 'next' import { cn } from '@/lib/utils' import { motion } from 'motion/react' import { roundTypeConfig as sharedRoundTypeConfig, roundStatusConfig as sharedRoundStatusConfig, } from '@/lib/round-config' type PipelineRound = { id: string name: string slug: string roundType: | 'INTAKE' | 'FILTERING' | 'EVALUATION' | 'SUBMISSION' | 'MENTORING' | 'LIVE_FINAL' | 'DELIBERATION' status: | 'ROUND_DRAFT' | 'ROUND_ACTIVE' | 'ROUND_CLOSED' | 'ROUND_ARCHIVED' sortOrder: number windowOpenAt: Date | null windowCloseAt: Date | null projectStates: { PENDING: number IN_PROGRESS: number PASSED: number REJECTED: number COMPLETED: number WITHDRAWN: number total: number } assignmentCount: number evalSubmitted: number evalDraft: number evalTotal: number filteringPassed: number filteringRejected: number filteringFlagged: number filteringTotal: number liveSessionStatus: string | null deliberationCount: number } const roundTypeConfig = sharedRoundTypeConfig const statusStyles: Record = Object.fromEntries( Object.entries(sharedRoundStatusConfig).map(([k, v]) => [k, { container: v.pipelineContainer, label: v.label }]) ) function getMetric(round: PipelineRound): string { const { roundType, projectStates, filteringTotal, filteringPassed, evalTotal, evalSubmitted, assignmentCount, liveSessionStatus, deliberationCount } = round switch (roundType) { case 'INTAKE': return `${projectStates.total} submitted` case 'FILTERING': return filteringTotal > 0 ? `${filteringPassed}/${filteringTotal} passed` : `${projectStates.total} to filter` case 'EVALUATION': return evalTotal > 0 ? `${evalSubmitted}/${evalTotal} evaluated` : `${assignmentCount} assignments` case 'SUBMISSION': { const active = projectStates.IN_PROGRESS + projectStates.COMPLETED return active > 0 ? `${projectStates.COMPLETED}/${projectStates.total} submitted${projectStates.IN_PROGRESS > 0 ? ` (${projectStates.IN_PROGRESS} in progress)` : ''}` : `${projectStates.total} awaiting` } case 'MENTORING': return `${projectStates.COMPLETED ?? 0} mentored` case 'LIVE_FINAL': { const status = liveSessionStatus return status ? status.charAt(0) + status.slice(1).toLowerCase() : `${projectStates.total} finalists` } case 'DELIBERATION': return deliberationCount > 0 ? `${deliberationCount} sessions` : 'Not started' default: return `${projectStates.total} projects` } } function getProgressPct(round: PipelineRound): number | null { if (round.status !== 'ROUND_ACTIVE') return null switch (round.roundType) { case 'FILTERING': { const processed = round.filteringPassed + round.filteringRejected + round.filteringFlagged const total = round.projectStates.total || round.filteringTotal return total > 0 ? Math.round((processed / total) * 100) : 0 } case 'EVALUATION': return round.evalTotal > 0 ? Math.round((round.evalSubmitted / round.evalTotal) * 100) : 0 case 'SUBMISSION': { const total = round.projectStates.total const active = round.projectStates.IN_PROGRESS + round.projectStates.COMPLETED return total > 0 ? Math.round((active / total) * 100) : 0 } case 'MENTORING': { const total = round.projectStates.total return total > 0 ? Math.round((round.projectStates.COMPLETED / total) * 100) : 0 } default: return null } } export function PipelineRoundNode({ round, index, }: { round: PipelineRound index: number }) { const typeConfig = roundTypeConfig[round.roundType] ?? roundTypeConfig.INTAKE const Icon = typeConfig.icon const status = statusStyles[round.status] ?? statusStyles.ROUND_DRAFT const isActive = round.status === 'ROUND_ACTIVE' const isCompleted = round.status === 'ROUND_CLOSED' || round.status === 'ROUND_ARCHIVED' const metric = getMetric(round) const progressPct = getProgressPct(round) return (
{/* Active ping indicator */} {isActive && ( )} {/* Completed check */} {isCompleted && ( )} {/* Icon */}
{/* Name */}

{round.name}

{/* Type label */} {typeConfig.label} {/* Status label */} {status.label} {/* Progress bar for active rounds */} {progressPct !== null && (

{progressPct}%

)} {/* Metric */} {progressPct === null && (

{metric}

)}
) } export type { PipelineRound }