'use client' import Link from 'next/link' import type { Route } from 'next' import { cn } from '@/lib/utils' import { motion } from 'motion/react' import { Upload, Filter, ClipboardCheck, FileUp, GraduationCap, Radio, Scale, } from 'lucide-react' 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: Record< string, { icon: typeof Upload; iconColor: string; iconBg: string } > = { INTAKE: { icon: Upload, iconColor: 'text-sky-600', iconBg: 'bg-sky-100' }, FILTERING: { icon: Filter, iconColor: 'text-amber-600', iconBg: 'bg-amber-100', }, EVALUATION: { icon: ClipboardCheck, iconColor: 'text-violet-600', iconBg: 'bg-violet-100', }, SUBMISSION: { icon: FileUp, iconColor: 'text-blue-600', iconBg: 'bg-blue-100', }, MENTORING: { icon: GraduationCap, iconColor: 'text-teal-600', iconBg: 'bg-teal-100', }, LIVE_FINAL: { icon: Radio, iconColor: 'text-red-600', iconBg: 'bg-red-100', }, DELIBERATION: { icon: Scale, iconColor: 'text-indigo-600', iconBg: 'bg-indigo-100', }, } const statusStyles: Record< string, { container: string; label: string } > = { ROUND_DRAFT: { container: 'bg-slate-50 border-slate-200 text-slate-400 border-dashed', label: 'Draft', }, ROUND_ACTIVE: { container: 'bg-blue-50 border-blue-300 text-blue-700 ring-2 ring-blue-400/30 shadow-lg shadow-blue-500/10', label: 'Active', }, ROUND_CLOSED: { container: 'bg-emerald-50 border-emerald-200 text-emerald-600', label: 'Closed', }, ROUND_ARCHIVED: { container: 'bg-slate-50/50 border-slate-100 text-slate-300', label: 'Archived', }, } 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': return `${projectStates.COMPLETED} submitted` 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` } } 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 metric = getMetric(round) return (
{/* Active ping indicator */} {isActive && ( )} {/* Icon */}
{/* Name */}

{round.name}

{/* Status label */} {status.label} {/* Metric */}

{metric}

) } export type { PipelineRound }