'use client' import { trpc } from '@/lib/trpc/client' import { toast } from 'sonner' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog' import { ListOrdered, Loader2 } from 'lucide-react' import { formatEnumLabel } from '@/lib/utils' import type { CompetitionCategory } from '@prisma/client' interface Props { programId: string } const STATUS_LABEL: Record = { WAITING: { label: 'Waiting', variant: 'outline' }, PROMOTED: { label: 'Promoted', variant: 'default' }, USED: { label: 'Used', variant: 'secondary' }, } export function WaitlistCard({ programId }: Props) { const utils = trpc.useUtils() const { data, isLoading } = trpc.finalist.listWaitlist.useQuery({ programId }) const promoteMutation = trpc.finalist.manualPromote.useMutation({ onSuccess: () => { toast.success('Waitlist entry promoted — confirmation email sent') utils.finalist.listWaitlist.invalidate({ programId }) utils.finalist.listCategoryCounts.invalidate({ programId }) }, onError: (err) => toast.error(err.message), }) if (isLoading) return const byCategory = new Map() for (const entry of data ?? []) { const list = byCategory.get(entry.category) ?? [] list.push(entry) byCategory.set(entry.category, list) } if (!data || data.length === 0) { return (
Waitlist
Per-category ranked waitlist. Auto-cascades when a finalist declines or expires.

No waitlist entries yet.

) } return (
Waitlist
Per-category ranked waitlist. Auto-cascades when a finalist declines or expires. You can manually promote out of order — overrides are audit-logged.
{Array.from(byCategory.entries()).map(([category, entries]) => (
{formatEnumLabel(category)}
{(entries ?? []).map((entry) => { const badge = STATUS_LABEL[entry.status] ?? { label: entry.status, variant: 'outline' as const } const canPromote = entry.status === 'WAITING' const isPending = promoteMutation.isPending && promoteMutation.variables?.waitlistEntryId === entry.id return (
{entry.rank}
{entry.project.title}
{entry.project.country ?? '—'}
{badge.label} {canPromote && ( Promote this team out of order? {entry.project.title} (rank #{entry.rank}) will be promoted into a finalist slot. A confirmation email will be sent to the team lead with a 24-hour window. This override is audit-logged. Cancel promoteMutation.mutate({ waitlistEntryId: entry.id, windowHours: 24, }) } > Promote )}
) })}
))}
) }