All checks were successful
Build and Push Docker Image / build (push) Successful in 8m43s
- Extract round detail monolith (2900→600 lines) into 13 standalone components - Add shared round/status config (round-config.ts) replacing 4 local copies - Delete 12 legacy competition-scoped pages, merge project pool into projects page - Add round-type-specific dashboard stat panels (submission, mentoring, live final, deliberation, summary) - Add contextual header quick actions based on active round type - Improve pipeline visualization: progress bars, checkmarks, chevron connectors, overflow fix - Add config tab completion dots (green/amber/red) and inline validation warnings - Enhance juries page with round assignments, member avatars, and cap mode badges - Add context-aware project list (recent submissions vs active evaluations) - Move competition settings into Manage Editions page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { cn } from '@/lib/utils'
|
|
import { motion } from 'motion/react'
|
|
import { Workflow, ArrowRight, ChevronRight } from 'lucide-react'
|
|
import {
|
|
PipelineRoundNode,
|
|
type PipelineRound,
|
|
} from '@/components/dashboard/pipeline-round-node'
|
|
|
|
function Connector({
|
|
prevStatus,
|
|
nextStatus,
|
|
index,
|
|
}: {
|
|
prevStatus: string
|
|
nextStatus: string
|
|
index: number
|
|
}) {
|
|
const isCompleted =
|
|
prevStatus === 'ROUND_CLOSED' || prevStatus === 'ROUND_ARCHIVED'
|
|
const isNextActive = nextStatus === 'ROUND_ACTIVE'
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ scaleX: 0, opacity: 0 }}
|
|
animate={{ scaleX: 1, opacity: 1 }}
|
|
transition={{ duration: 0.3, delay: 0.15 + index * 0.06 }}
|
|
className="flex items-center self-center origin-left px-0.5"
|
|
>
|
|
<div className="flex items-center gap-0">
|
|
<div
|
|
className={cn(
|
|
'h-0.5 w-5 transition-colors',
|
|
isCompleted
|
|
? 'bg-emerald-400'
|
|
: isNextActive
|
|
? 'bg-blue-300'
|
|
: 'bg-slate-200'
|
|
)}
|
|
/>
|
|
<ChevronRight
|
|
className={cn(
|
|
'h-3.5 w-3.5 -ml-1',
|
|
isCompleted
|
|
? 'text-emerald-400'
|
|
: isNextActive
|
|
? 'text-blue-300'
|
|
: 'text-slate-200'
|
|
)}
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
export function CompetitionPipeline({
|
|
rounds,
|
|
}: {
|
|
rounds: PipelineRound[]
|
|
}) {
|
|
if (rounds.length === 0) {
|
|
return (
|
|
<Card className="border-dashed">
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-500/10">
|
|
<Workflow className="h-4 w-4 text-slate-500" />
|
|
</div>
|
|
<CardTitle className="text-base">Competition Pipeline</CardTitle>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex flex-col items-center justify-center py-10 text-center">
|
|
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-muted">
|
|
<Workflow className="h-7 w-7 text-muted-foreground/40" />
|
|
</div>
|
|
<p className="mt-3 text-sm font-medium text-muted-foreground">
|
|
No rounds configured yet
|
|
</p>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
Create rounds to visualize the competition pipeline.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-brand-teal/10">
|
|
<Workflow className="h-4 w-4 text-brand-teal" />
|
|
</div>
|
|
<CardTitle className="text-base">Competition Pipeline</CardTitle>
|
|
</div>
|
|
<Link
|
|
href="/admin/rounds"
|
|
className="flex items-center gap-1 text-xs font-semibold uppercase tracking-wider text-brand-teal hover:text-brand-teal-light transition-colors"
|
|
>
|
|
All rounds <ArrowRight className="h-3 w-3" />
|
|
</Link>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="px-4 pb-4">
|
|
{/* Scrollable container with padding to prevent cutoff */}
|
|
<div className="overflow-x-auto -mx-1 px-1 pt-2 pb-3">
|
|
<div className="flex items-center gap-0 min-w-max">
|
|
{rounds.map((round, index) => (
|
|
<div key={round.id} className="flex items-center">
|
|
<PipelineRoundNode round={round} index={index} />
|
|
{index < rounds.length - 1 && (
|
|
<Connector
|
|
prevStatus={round.status}
|
|
nextStatus={rounds[index + 1].status}
|
|
index={index}
|
|
/>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|