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>
265 lines
8.0 KiB
TypeScript
265 lines
8.0 KiB
TypeScript
/**
|
|
* Shared round type, status, and project state configuration.
|
|
*
|
|
* Single source of truth for colors, labels, icons, and descriptions
|
|
* used across the admin dashboard, round detail, pipeline, and timeline.
|
|
*/
|
|
|
|
import type { RoundType, RoundStatus, ProjectRoundStateValue } from '@prisma/client'
|
|
import {
|
|
Upload,
|
|
Filter,
|
|
ClipboardCheck,
|
|
FileUp,
|
|
GraduationCap,
|
|
Radio,
|
|
Scale,
|
|
Inbox,
|
|
Users,
|
|
Circle,
|
|
Clock,
|
|
CheckCircle2,
|
|
} from 'lucide-react'
|
|
import type { LucideIcon } from 'lucide-react'
|
|
|
|
// ── Round Type Configuration ─────────────────────────────────────────────────
|
|
|
|
export type RoundTypeConfig = {
|
|
label: string
|
|
description: string
|
|
/** Badge classes: bg + text color */
|
|
badgeClass: string
|
|
/** Hex dot color for pipeline nodes */
|
|
dotColor: string
|
|
/** Lighter variant: bg, text, border classes for cards/filters */
|
|
cardBg: string
|
|
cardText: string
|
|
cardBorder: string
|
|
/** Icon for pipeline nodes and round detail */
|
|
icon: LucideIcon
|
|
iconColor: string
|
|
iconBg: string
|
|
}
|
|
|
|
export const roundTypeConfig: Record<RoundType, RoundTypeConfig> = {
|
|
INTAKE: {
|
|
label: 'Intake',
|
|
description: 'Collecting applications',
|
|
badgeClass: 'bg-gray-100 text-gray-700',
|
|
dotColor: '#9ca3af',
|
|
cardBg: 'bg-gray-50',
|
|
cardText: 'text-gray-600',
|
|
cardBorder: 'border-gray-300',
|
|
icon: Inbox,
|
|
iconColor: 'text-sky-600',
|
|
iconBg: 'bg-sky-100',
|
|
},
|
|
FILTERING: {
|
|
label: 'Filtering',
|
|
description: 'AI + manual screening',
|
|
badgeClass: 'bg-amber-100 text-amber-700',
|
|
dotColor: '#f59e0b',
|
|
cardBg: 'bg-amber-50',
|
|
cardText: 'text-amber-700',
|
|
cardBorder: 'border-amber-300',
|
|
icon: Filter,
|
|
iconColor: 'text-amber-600',
|
|
iconBg: 'bg-amber-100',
|
|
},
|
|
EVALUATION: {
|
|
label: 'Evaluation',
|
|
description: 'Jury evaluation & scoring',
|
|
badgeClass: 'bg-blue-100 text-blue-700',
|
|
dotColor: '#3b82f6',
|
|
cardBg: 'bg-blue-50',
|
|
cardText: 'text-blue-700',
|
|
cardBorder: 'border-blue-300',
|
|
icon: ClipboardCheck,
|
|
iconColor: 'text-violet-600',
|
|
iconBg: 'bg-violet-100',
|
|
},
|
|
SUBMISSION: {
|
|
label: 'Submission',
|
|
description: 'Document submission',
|
|
badgeClass: 'bg-purple-100 text-purple-700',
|
|
dotColor: '#8b5cf6',
|
|
cardBg: 'bg-purple-50',
|
|
cardText: 'text-purple-700',
|
|
cardBorder: 'border-purple-300',
|
|
icon: FileUp,
|
|
iconColor: 'text-blue-600',
|
|
iconBg: 'bg-blue-100',
|
|
},
|
|
MENTORING: {
|
|
label: 'Mentoring',
|
|
description: 'Mentor-guided development',
|
|
badgeClass: 'bg-teal-100 text-teal-700',
|
|
dotColor: '#557f8c',
|
|
cardBg: 'bg-teal-50',
|
|
cardText: 'text-teal-700',
|
|
cardBorder: 'border-teal-300',
|
|
icon: GraduationCap,
|
|
iconColor: 'text-teal-600',
|
|
iconBg: 'bg-teal-100',
|
|
},
|
|
LIVE_FINAL: {
|
|
label: 'Live Final',
|
|
description: 'Live presentations & voting',
|
|
badgeClass: 'bg-red-100 text-red-700',
|
|
dotColor: '#de0f1e',
|
|
cardBg: 'bg-red-50',
|
|
cardText: 'text-red-700',
|
|
cardBorder: 'border-red-300',
|
|
icon: Radio,
|
|
iconColor: 'text-red-600',
|
|
iconBg: 'bg-red-100',
|
|
},
|
|
DELIBERATION: {
|
|
label: 'Deliberation',
|
|
description: 'Final jury deliberation',
|
|
badgeClass: 'bg-indigo-100 text-indigo-700',
|
|
dotColor: '#6366f1',
|
|
cardBg: 'bg-indigo-50',
|
|
cardText: 'text-indigo-700',
|
|
cardBorder: 'border-indigo-300',
|
|
icon: Scale,
|
|
iconColor: 'text-indigo-600',
|
|
iconBg: 'bg-indigo-100',
|
|
},
|
|
}
|
|
|
|
// ── Round Status Configuration ───────────────────────────────────────────────
|
|
|
|
export type RoundStatusConfig = {
|
|
label: string
|
|
description: string
|
|
/** Badge classes for status badges */
|
|
bgClass: string
|
|
/** Dot color class (with optional animation) */
|
|
dotClass: string
|
|
/** Hex color for pipeline dot */
|
|
dotColor: string
|
|
/** Whether the dot should pulse (active round) */
|
|
pulse: boolean
|
|
/** Icon for timeline displays */
|
|
timelineIcon: LucideIcon
|
|
timelineIconColor: string
|
|
/** Container classes for pipeline nodes */
|
|
pipelineContainer: string
|
|
}
|
|
|
|
export const roundStatusConfig: Record<RoundStatus, RoundStatusConfig> = {
|
|
ROUND_DRAFT: {
|
|
label: 'Draft',
|
|
description: 'Not yet active. Configure before launching.',
|
|
bgClass: 'bg-gray-100 text-gray-700',
|
|
dotClass: 'bg-gray-500',
|
|
dotColor: '#9ca3af',
|
|
pulse: false,
|
|
timelineIcon: Circle,
|
|
timelineIconColor: 'text-gray-400',
|
|
pipelineContainer: 'bg-slate-50 border-slate-200 text-slate-400 border-dashed',
|
|
},
|
|
ROUND_ACTIVE: {
|
|
label: 'Active',
|
|
description: 'Round is live. Projects can be processed.',
|
|
bgClass: 'bg-emerald-100 text-emerald-700',
|
|
dotClass: 'bg-emerald-500 animate-pulse',
|
|
dotColor: '#10b981',
|
|
pulse: true,
|
|
timelineIcon: Clock,
|
|
timelineIconColor: 'text-emerald-500',
|
|
pipelineContainer: 'bg-blue-50 border-blue-300 text-blue-700 ring-2 ring-blue-400/30 shadow-lg shadow-blue-500/10',
|
|
},
|
|
ROUND_CLOSED: {
|
|
label: 'Closed',
|
|
description: 'No longer accepting changes. Results are final.',
|
|
bgClass: 'bg-blue-100 text-blue-700',
|
|
dotClass: 'bg-blue-500',
|
|
dotColor: '#3b82f6',
|
|
pulse: false,
|
|
timelineIcon: CheckCircle2,
|
|
timelineIconColor: 'text-blue-500',
|
|
pipelineContainer: 'bg-emerald-50 border-emerald-200 text-emerald-600',
|
|
},
|
|
ROUND_ARCHIVED: {
|
|
label: 'Archived',
|
|
description: 'Historical record only.',
|
|
bgClass: 'bg-muted text-muted-foreground',
|
|
dotClass: 'bg-muted-foreground',
|
|
dotColor: '#6b7280',
|
|
pulse: false,
|
|
timelineIcon: CheckCircle2,
|
|
timelineIconColor: 'text-gray-400',
|
|
pipelineContainer: 'bg-gray-50 border-gray-200 text-gray-400 opacity-60',
|
|
},
|
|
}
|
|
|
|
// ── Project Round State Colors ───────────────────────────────────────────────
|
|
|
|
export type ProjectStateConfig = {
|
|
label: string
|
|
/** Background color class for bars and dots */
|
|
bg: string
|
|
/** Text color class */
|
|
text: string
|
|
/** Badge variant classes */
|
|
badgeClass: string
|
|
}
|
|
|
|
export const projectStateConfig: Record<ProjectRoundStateValue, ProjectStateConfig> = {
|
|
PENDING: {
|
|
label: 'Pending',
|
|
bg: 'bg-slate-300',
|
|
text: 'text-slate-700',
|
|
badgeClass: 'bg-gray-100 text-gray-700',
|
|
},
|
|
IN_PROGRESS: {
|
|
label: 'In Progress',
|
|
bg: 'bg-blue-400',
|
|
text: 'text-blue-700',
|
|
badgeClass: 'bg-blue-100 text-blue-700',
|
|
},
|
|
PASSED: {
|
|
label: 'Passed',
|
|
bg: 'bg-emerald-500',
|
|
text: 'text-emerald-700',
|
|
badgeClass: 'bg-emerald-100 text-emerald-700',
|
|
},
|
|
REJECTED: {
|
|
label: 'Rejected',
|
|
bg: 'bg-red-400',
|
|
text: 'text-red-700',
|
|
badgeClass: 'bg-red-100 text-red-700',
|
|
},
|
|
COMPLETED: {
|
|
label: 'Completed',
|
|
bg: 'bg-[#557f8c]',
|
|
text: 'text-teal-700',
|
|
badgeClass: 'bg-teal-100 text-teal-700',
|
|
},
|
|
WITHDRAWN: {
|
|
label: 'Withdrawn',
|
|
bg: 'bg-slate-400',
|
|
text: 'text-slate-600',
|
|
badgeClass: 'bg-orange-100 text-orange-700',
|
|
},
|
|
}
|
|
|
|
// ── Award Status Configuration ───────────────────────────────────────────────
|
|
|
|
export const awardStatusConfig = {
|
|
DRAFT: { label: 'Draft', color: 'text-gray-500', bgClass: 'bg-gray-100 text-gray-700' },
|
|
NOMINATIONS_OPEN: { label: 'Nominations Open', color: 'text-amber-600', bgClass: 'bg-amber-100 text-amber-700' },
|
|
VOTING_OPEN: { label: 'Voting Open', color: 'text-emerald-600', bgClass: 'bg-emerald-100 text-emerald-700' },
|
|
CLOSED: { label: 'Closed', color: 'text-blue-500', bgClass: 'bg-blue-100 text-blue-700' },
|
|
ARCHIVED: { label: 'Archived', color: 'text-gray-400', bgClass: 'bg-gray-100 text-gray-600' },
|
|
} as const
|
|
|
|
// ── Round type option list (for selects/filters) ────────────────────────────
|
|
|
|
export const ROUND_TYPE_OPTIONS = Object.entries(roundTypeConfig).map(([value, cfg]) => ({
|
|
value: value as RoundType,
|
|
label: cfg.label,
|
|
}))
|