Admin dashboard & round management UX overhaul
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>
This commit is contained in:
2026-02-22 17:14:00 +01:00
parent f7bc3b4dd2
commit f26ee3f076
51 changed files with 4530 additions and 6276 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -46,43 +46,28 @@ import {
ArrowRight,
} from 'lucide-react'
import { useEdition } from '@/contexts/edition-context'
import {
roundTypeConfig,
roundStatusConfig,
awardStatusConfig,
ROUND_TYPE_OPTIONS,
} from '@/lib/round-config'
// ─── Constants ───────────────────────────────────────────────────────────────
// ─── Constants (derived from shared config) ──────────────────────────────────
const ROUND_TYPES = [
{ value: 'INTAKE', label: 'Intake' },
{ value: 'FILTERING', label: 'Filtering' },
{ value: 'EVALUATION', label: 'Evaluation' },
{ value: 'SUBMISSION', label: 'Submission' },
{ value: 'MENTORING', label: 'Mentoring' },
{ value: 'LIVE_FINAL', label: 'Live Final' },
{ value: 'DELIBERATION', label: 'Deliberation' },
] as const
const ROUND_TYPES = ROUND_TYPE_OPTIONS
const ROUND_TYPE_COLORS: Record<string, { dot: string; bg: string; text: string; border: string }> = {
INTAKE: { dot: '#9ca3af', bg: 'bg-gray-50', text: 'text-gray-600', border: 'border-gray-300' },
FILTERING: { dot: '#f59e0b', bg: 'bg-amber-50', text: 'text-amber-700', border: 'border-amber-300' },
EVALUATION: { dot: '#3b82f6', bg: 'bg-blue-50', text: 'text-blue-700', border: 'border-blue-300' },
SUBMISSION: { dot: '#8b5cf6', bg: 'bg-purple-50', text: 'text-purple-700', border: 'border-purple-300' },
MENTORING: { dot: '#557f8c', bg: 'bg-teal-50', text: 'text-teal-700', border: 'border-teal-300' },
LIVE_FINAL: { dot: '#de0f1e', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-300' },
DELIBERATION: { dot: '#6366f1', bg: 'bg-indigo-50', text: 'text-indigo-700', border: 'border-indigo-300' },
}
const ROUND_TYPE_COLORS: Record<string, { dot: string; bg: string; text: string; border: string }> = Object.fromEntries(
Object.entries(roundTypeConfig).map(([k, v]) => [k, { dot: v.dotColor, bg: v.cardBg, text: v.cardText, border: v.cardBorder }])
)
const ROUND_STATUS_STYLES: Record<string, { color: string; label: string; pulse?: boolean }> = {
ROUND_DRAFT: { color: '#9ca3af', label: 'Draft' },
ROUND_ACTIVE: { color: '#10b981', label: 'Active', pulse: true },
ROUND_CLOSED: { color: '#3b82f6', label: 'Closed' },
ROUND_ARCHIVED: { color: '#6b7280', label: 'Archived' },
}
const ROUND_STATUS_STYLES: Record<string, { color: string; label: string; pulse?: boolean }> = Object.fromEntries(
Object.entries(roundStatusConfig).map(([k, v]) => [k, { color: v.dotColor, label: v.label, pulse: v.pulse }])
)
const AWARD_STATUS_COLORS: Record<string, string> = {
DRAFT: 'text-gray-500',
NOMINATIONS_OPEN: 'text-amber-600',
VOTING_OPEN: 'text-emerald-600',
CLOSED: 'text-blue-600',
ARCHIVED: 'text-gray-400',
}
const AWARD_STATUS_COLORS: Record<string, string> = Object.fromEntries(
Object.entries(awardStatusConfig).map(([k, v]) => [k, v.color])
)
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -268,12 +253,12 @@ export default function RoundsPage() {
</div>
<h3 className="text-lg font-semibold text-[#053d57] mb-1">No Competition Configured</h3>
<p className="text-sm text-muted-foreground max-w-sm mb-5">
Create a competition to start building the evaluation pipeline.
Create a program edition to start building the evaluation pipeline.
</p>
<Link href={`/admin/competitions/new?programId=${programId}` as Route}>
<Link href={'/admin/programs' as Route}>
<Button className="bg-[#de0f1e] hover:bg-[#de0f1e]/90">
<Plus className="h-4 w-4 mr-2" />
Create Competition
Manage Editions
</Button>
</Link>
</div>