'use client' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { CheckCircle2, Circle, Clock, XCircle, Trophy } from 'lucide-react' const roundStatusDisplay: Record = { ROUND_DRAFT: { label: 'Upcoming', variant: 'secondary' }, ROUND_ACTIVE: { label: 'In Progress', variant: 'default' }, ROUND_CLOSED: { label: 'Completed', variant: 'default' }, ROUND_ARCHIVED: { label: 'Completed', variant: 'default' }, } export function ApplicantCompetitionTimeline() { const { data, isLoading } = trpc.applicant.getMyCompetitionTimeline.useQuery() if (isLoading) { return (
{[1, 2, 3].map((i) => ( ))}
) } if (!data || data.entries.length === 0) { return ( Competition Timeline

No rounds available yet

) } return ( Competition Timeline {data.competitionName && (

{data.competitionName}

)}
{/* Vertical connecting line */}
{data.entries.map((entry) => { const isCompleted = entry.status === 'ROUND_CLOSED' || entry.status === 'ROUND_ARCHIVED' const isActive = entry.status === 'ROUND_ACTIVE' const isRejected = entry.projectState === 'REJECTED' const isGrandFinale = entry.roundType === 'GRAND_FINALE' // Determine icon let Icon = Circle let iconBg = 'bg-muted' let iconColor = 'text-muted-foreground' if (isRejected) { Icon = XCircle iconBg = 'bg-red-50' iconColor = 'text-red-600' } else if (isGrandFinale && isCompleted) { Icon = Trophy iconBg = 'bg-yellow-50' iconColor = 'text-yellow-600' } else if (isCompleted) { Icon = CheckCircle2 iconBg = 'bg-emerald-50' iconColor = 'text-emerald-600' } else if (isActive) { Icon = Clock iconBg = 'bg-brand-blue/10' iconColor = 'text-brand-blue' } // Project state display let stateLabel: string | null = null if (entry.projectState === 'REJECTED') { stateLabel = 'Not Selected' } else if (entry.projectState === 'PASSED' || entry.projectState === 'COMPLETED') { stateLabel = 'Advanced' } else if (entry.projectState === 'IN_PROGRESS') { stateLabel = 'Under Review' } else if (entry.projectState === 'PENDING') { stateLabel = 'Pending' } const statusInfo = roundStatusDisplay[entry.status] ?? { label: 'Upcoming', variant: 'secondary' as const } return (
{/* Icon */}
{/* Content */}

{entry.label}

{stateLabel && ( {stateLabel} )} {statusInfo.label}
{entry.windowOpenAt && entry.windowCloseAt && (

Opens: {new Date(entry.windowOpenAt).toLocaleDateString()}

Closes: {new Date(entry.windowCloseAt).toLocaleDateString()}

)}
) })}
) } /** * Compact sidebar variant for the dashboard. * Shows dots + labels, no date details. */ export function CompetitionTimelineSidebar() { const { data, isLoading } = trpc.applicant.getMyCompetitionTimeline.useQuery() if (isLoading) { return (
{[1, 2, 3].map((i) => ( ))}
) } if (!data || data.entries.length === 0) { return

No rounds available

} return (
{data.entries.map((entry, index) => { const isCompleted = entry.status === 'ROUND_CLOSED' || entry.status === 'ROUND_ARCHIVED' const isActive = entry.status === 'ROUND_ACTIVE' const isRejected = entry.projectState === 'REJECTED' const isGrandFinale = entry.roundType === 'GRAND_FINALE' const isLast = index === data.entries.length - 1 let dotColor = 'border-2 border-muted bg-background' if (isRejected) dotColor = 'bg-destructive' else if (isGrandFinale && isCompleted) dotColor = 'bg-yellow-500' else if (isCompleted) dotColor = 'bg-primary' else if (isActive) dotColor = 'bg-primary ring-2 ring-primary/30' return (
{/* Connecting line */} {!isLast && (
)} {/* Dot */}
{/* Label */}

{entry.label}

{isRejected && (

Not Selected

)} {isActive && (

In Progress

)}
) })}
) }