fix: separate main pipeline from award tracks on rounds page
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m24s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m24s
Award-specific rounds (e.g. Spotlight on Africa) were mixed into the main pipeline as a flat list, making them look like sequential steps after Deliberation. Now they render in their own amber-tinted card sections below the main pipeline, each with a header showing award name, pool size, eligibility mode, and status. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -41,7 +41,6 @@ import {
|
|||||||
FileBox,
|
FileBox,
|
||||||
Save,
|
Save,
|
||||||
Loader2,
|
Loader2,
|
||||||
Award,
|
|
||||||
Trophy,
|
Trophy,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
@@ -151,27 +150,42 @@ export default function RoundsPage() {
|
|||||||
onError: (err) => toast.error(err.message),
|
onError: (err) => toast.error(err.message),
|
||||||
})
|
})
|
||||||
|
|
||||||
const rounds = useMemo(() => {
|
// Split rounds into main pipeline (no specialAwardId) and award tracks
|
||||||
|
const mainRounds = useMemo(() => {
|
||||||
const all = (compDetail?.rounds ?? []) as RoundWithStats[]
|
const all = (compDetail?.rounds ?? []) as RoundWithStats[]
|
||||||
return filterType === 'all' ? all : all.filter((r) => r.roundType === filterType)
|
const main = all.filter((r) => !r.specialAwardId)
|
||||||
|
return filterType === 'all' ? main : main.filter((r) => r.roundType === filterType)
|
||||||
}, [compDetail?.rounds, filterType])
|
}, [compDetail?.rounds, filterType])
|
||||||
|
|
||||||
// Group awards by their evaluationRoundId
|
// Group award-track rounds by their specialAwardId, paired with the award metadata
|
||||||
const awardsByRound = useMemo(() => {
|
const awardTrackGroups = useMemo(() => {
|
||||||
const map = new Map<string, SpecialAwardItem[]>()
|
const allRounds = (compDetail?.rounds ?? []) as RoundWithStats[]
|
||||||
for (const award of (awards ?? []) as SpecialAwardItem[]) {
|
const awardRounds = allRounds.filter((r) => r.specialAwardId)
|
||||||
if (award.evaluationRoundId) {
|
const groups = new Map<string, { award: SpecialAwardItem; rounds: RoundWithStats[] }>()
|
||||||
const existing = map.get(award.evaluationRoundId) ?? []
|
|
||||||
existing.push(award)
|
for (const round of awardRounds) {
|
||||||
map.set(award.evaluationRoundId, existing)
|
const awardId = round.specialAwardId!
|
||||||
|
if (!groups.has(awardId)) {
|
||||||
|
const award = ((awards ?? []) as SpecialAwardItem[]).find((a) => a.id === awardId)
|
||||||
|
if (!award) continue
|
||||||
|
groups.set(awardId, { award, rounds: [] })
|
||||||
}
|
}
|
||||||
|
groups.get(awardId)!.rounds.push(round)
|
||||||
}
|
}
|
||||||
return map
|
return Array.from(groups.values())
|
||||||
}, [awards])
|
}, [compDetail?.rounds, awards])
|
||||||
|
|
||||||
const floatingAwards = useMemo(() => {
|
const floatingAwards = useMemo(() => {
|
||||||
return ((awards ?? []) as SpecialAwardItem[]).filter((a) => !a.evaluationRoundId)
|
// Awards that have no evaluationRoundId AND no rounds linked via specialAwardId
|
||||||
}, [awards])
|
const awardIdsWithRounds = new Set(
|
||||||
|
((compDetail?.rounds ?? []) as RoundWithStats[])
|
||||||
|
.filter((r) => r.specialAwardId)
|
||||||
|
.map((r) => r.specialAwardId!)
|
||||||
|
)
|
||||||
|
return ((awards ?? []) as SpecialAwardItem[]).filter(
|
||||||
|
(a) => !a.evaluationRoundId && !awardIdsWithRounds.has(a.id)
|
||||||
|
)
|
||||||
|
}, [awards, compDetail?.rounds])
|
||||||
|
|
||||||
const handleCreateRound = () => {
|
const handleCreateRound = () => {
|
||||||
if (!roundForm.name.trim() || !roundForm.roundType || !comp) {
|
if (!roundForm.name.trim() || !roundForm.roundType || !comp) {
|
||||||
@@ -271,8 +285,10 @@ export default function RoundsPage() {
|
|||||||
const activeFilter = filterType !== 'all'
|
const activeFilter = filterType !== 'all'
|
||||||
const totalProjects = (compDetail as any)?.distinctProjectCount ?? 0
|
const totalProjects = (compDetail as any)?.distinctProjectCount ?? 0
|
||||||
const allRounds = (compDetail?.rounds ?? []) as RoundWithStats[]
|
const allRounds = (compDetail?.rounds ?? []) as RoundWithStats[]
|
||||||
|
const allMainRounds = allRounds.filter((r) => !r.specialAwardId)
|
||||||
|
const awardRoundCount = allRounds.length - allMainRounds.length
|
||||||
const totalAssignments = allRounds.reduce((s, r) => s + r._count.assignments, 0)
|
const totalAssignments = allRounds.reduce((s, r) => s + r._count.assignments, 0)
|
||||||
const activeRound = allRounds.find((r) => r.status === 'ROUND_ACTIVE')
|
const activeRound = allMainRounds.find((r) => r.status === 'ROUND_ACTIVE')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipProvider delayDuration={200}>
|
<TooltipProvider delayDuration={200}>
|
||||||
@@ -313,7 +329,7 @@ export default function RoundsPage() {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4 mt-1 text-sm text-muted-foreground">
|
<div className="flex items-center gap-4 mt-1 text-sm text-muted-foreground">
|
||||||
<span>{allRounds.filter((r) => !r.specialAwardId).length} rounds</span>
|
<span>{allMainRounds.length} rounds{awardRoundCount > 0 ? ` + ${awardRoundCount} award` : ''}</span>
|
||||||
<span className="text-muted-foreground/30">|</span>
|
<span className="text-muted-foreground/30">|</span>
|
||||||
<span>{totalProjects} projects</span>
|
<span>{totalProjects} projects</span>
|
||||||
<span className="text-muted-foreground/30">|</span>
|
<span className="text-muted-foreground/30">|</span>
|
||||||
@@ -330,12 +346,12 @@ export default function RoundsPage() {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{awards && awards.length > 0 && (
|
{awardTrackGroups.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<span className="text-muted-foreground/30">|</span>
|
<span className="text-muted-foreground/30">|</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Award className="h-3.5 w-3.5" />
|
<Trophy className="h-3.5 w-3.5" />
|
||||||
{awards.length} awards
|
{awardTrackGroups.length} award {awardTrackGroups.length === 1 ? 'track' : 'tracks'}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -389,7 +405,7 @@ export default function RoundsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Pipeline View ───────────────────────────────────────────── */}
|
{/* ── Pipeline View ───────────────────────────────────────────── */}
|
||||||
{rounds.length === 0 ? (
|
{mainRounds.length === 0 && awardTrackGroups.length === 0 ? (
|
||||||
<div className="py-16 text-center border-2 border-dashed rounded-lg">
|
<div className="py-16 text-center border-2 border-dashed rounded-lg">
|
||||||
<FileBox className="h-8 w-8 text-muted-foreground/40 mx-auto mb-2" />
|
<FileBox className="h-8 w-8 text-muted-foreground/40 mx-auto mb-2" />
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
@@ -397,142 +413,79 @@ export default function RoundsPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative">
|
<div className="space-y-6">
|
||||||
{/* Main pipeline track */}
|
{/* ── Main Competition Pipeline ───────────────────────── */}
|
||||||
{rounds.map((round, index) => {
|
{mainRounds.length > 0 && (
|
||||||
const isLast = index === rounds.length - 1
|
<div className="relative">
|
||||||
const typeColors = ROUND_TYPE_COLORS[round.roundType] ?? ROUND_TYPE_COLORS.INTAKE
|
{mainRounds.map((round, index) => (
|
||||||
const statusStyle = ROUND_STATUS_STYLES[round.status] ?? ROUND_STATUS_STYLES.ROUND_DRAFT
|
<RoundRow
|
||||||
const projectCount = round._count.projectRoundStates
|
key={round.id}
|
||||||
const assignmentCount = round._count.assignments
|
round={round}
|
||||||
const roundAwards = awardsByRound.get(round.id) ?? []
|
isLast={index === mainRounds.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ── Award Track Sections ────────────────────────────── */}
|
||||||
|
{awardTrackGroups.map(({ award, rounds: awardRounds }) => {
|
||||||
|
const isExclusive = award.eligibilityMode === 'SEPARATE_POOL'
|
||||||
|
const eligible = award._count.eligibilities
|
||||||
|
const statusColor = AWARD_STATUS_COLORS[award.status] ?? 'text-gray-500'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={round.id} className="relative">
|
<div
|
||||||
{/* Round row with pipeline connector */}
|
key={award.id}
|
||||||
<div className="flex">
|
className="rounded-lg border border-amber-200/80 bg-amber-50/30 overflow-hidden"
|
||||||
{/* Left: pipeline track */}
|
>
|
||||||
<div className="flex flex-col items-center shrink-0 w-10">
|
{/* Award track header */}
|
||||||
{/* Status dot */}
|
<Link href={`/admin/awards/${award.id}` as Route}>
|
||||||
<Tooltip>
|
<div className="group flex items-center gap-3 px-4 py-3 border-b border-amber-200/60 hover:bg-amber-50/60 transition-colors cursor-pointer">
|
||||||
<TooltipTrigger asChild>
|
<div className="flex items-center justify-center h-8 w-8 rounded-full bg-amber-100 shrink-0">
|
||||||
<div className="relative z-10 flex items-center justify-center">
|
<Trophy className="h-4 w-4 text-amber-600" />
|
||||||
<div
|
|
||||||
className="h-3.5 w-3.5 rounded-full border-2 border-white shadow-sm"
|
|
||||||
style={{ backgroundColor: statusStyle.color }}
|
|
||||||
/>
|
|
||||||
{statusStyle.pulse && (
|
|
||||||
<div
|
|
||||||
className="absolute h-3.5 w-3.5 rounded-full animate-ping opacity-40"
|
|
||||||
style={{ backgroundColor: statusStyle.color }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="left" className="text-xs">
|
|
||||||
{statusStyle.label}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
{/* Connector line */}
|
|
||||||
{!isLast && (
|
|
||||||
<div className="w-px flex-1 min-h-[8px] bg-border" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right: round content + awards */}
|
|
||||||
<div className="flex-1 min-w-0 pb-2">
|
|
||||||
<div className="flex items-stretch gap-3">
|
|
||||||
{/* Round row */}
|
|
||||||
<Link
|
|
||||||
href={`/admin/rounds/${round.id}` as Route}
|
|
||||||
className="flex-1 min-w-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'group flex items-center gap-3 px-3 py-2.5 rounded-md border-l-[3px] cursor-pointer transition-all',
|
|
||||||
'bg-white hover:bg-gray-50/80 hover:shadow-sm',
|
|
||||||
)}
|
|
||||||
style={{ borderLeftColor: typeColors.dot }}
|
|
||||||
>
|
|
||||||
{/* Round type indicator */}
|
|
||||||
<span className={cn(
|
|
||||||
'text-[10px] font-semibold uppercase tracking-wider shrink-0 w-[70px]',
|
|
||||||
typeColors.text
|
|
||||||
)}>
|
|
||||||
{round.roundType.replace('_', ' ')}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{/* Round name */}
|
|
||||||
<span className="text-sm font-semibold text-[#053d57] truncate group-hover:text-[#de0f1e] transition-colors min-w-0 flex-1">
|
|
||||||
{round.name}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{/* Stats cluster */}
|
|
||||||
<div className="hidden sm:flex items-center gap-3 text-xs text-muted-foreground shrink-0">
|
|
||||||
{round.juryGroup && (
|
|
||||||
<span className="flex items-center gap-1 max-w-[120px]">
|
|
||||||
<Users className="h-3 w-3 shrink-0" />
|
|
||||||
<span className="truncate">{round.juryGroup.name}</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className="flex items-center gap-1">
|
|
||||||
<FileBox className="h-3 w-3 shrink-0" />
|
|
||||||
{projectCount}
|
|
||||||
</span>
|
|
||||||
{assignmentCount > 0 && (
|
|
||||||
<span className="tabular-nums">{assignmentCount} asgn</span>
|
|
||||||
)}
|
|
||||||
{(round.windowOpenAt || round.windowCloseAt) && (
|
|
||||||
<span className="flex items-center gap-1 tabular-nums">
|
|
||||||
<Calendar className="h-3 w-3 shrink-0" />
|
|
||||||
{round.windowOpenAt
|
|
||||||
? new Date(round.windowOpenAt).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })
|
|
||||||
: ''}
|
|
||||||
{round.windowOpenAt && round.windowCloseAt ? ' \u2013 ' : ''}
|
|
||||||
{round.windowCloseAt
|
|
||||||
? new Date(round.windowCloseAt).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })
|
|
||||||
: ''}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status badge (compact) */}
|
|
||||||
<Badge
|
|
||||||
variant="outline"
|
|
||||||
className="text-[10px] px-1.5 py-0 h-5 font-medium shrink-0 hidden md:inline-flex"
|
|
||||||
style={{ color: statusStyle.color, borderColor: statusStyle.color + '40' }}
|
|
||||||
>
|
|
||||||
{statusStyle.label}
|
|
||||||
</Badge>
|
|
||||||
|
|
||||||
{/* Arrow */}
|
|
||||||
<ArrowRight className="h-3.5 w-3.5 text-muted-foreground/30 group-hover:text-[#de0f1e]/60 transition-colors shrink-0" />
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Awards branching off this round */}
|
|
||||||
{roundAwards.length > 0 && (
|
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
|
||||||
{/* Connector dash */}
|
|
||||||
<div className="w-4 h-px bg-amber-300" />
|
|
||||||
{/* Award nodes */}
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
{roundAwards.map((award) => (
|
|
||||||
<AwardNode key={award.id} award={award} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<h3 className="text-sm font-semibold text-[#053d57] group-hover:text-[#de0f1e] transition-colors truncate">
|
||||||
|
{award.name}
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
|
||||||
|
<span>{eligible} projects</span>
|
||||||
|
<span className="text-muted-foreground/30">·</span>
|
||||||
|
<span className={cn(
|
||||||
|
'text-[10px] font-semibold uppercase tracking-wide px-1.5 py-px rounded',
|
||||||
|
isExclusive
|
||||||
|
? 'bg-red-100 text-red-600'
|
||||||
|
: 'bg-blue-100 text-blue-600'
|
||||||
|
)}>
|
||||||
|
{isExclusive ? 'Exclusive pool' : 'Parallel'}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground/30">·</span>
|
||||||
|
<span className={statusColor}>
|
||||||
|
{award.status.replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRight className="h-4 w-4 text-muted-foreground/30 group-hover:text-[#de0f1e]/60 transition-colors shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Award track rounds */}
|
||||||
|
<div className="px-4 py-2">
|
||||||
|
{awardRounds.map((round, index) => (
|
||||||
|
<RoundRow
|
||||||
|
key={round.id}
|
||||||
|
round={round}
|
||||||
|
isLast={index === awardRounds.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* Floating awards (no evaluationRoundId) */}
|
{/* Floating awards (no linked rounds) */}
|
||||||
{floatingAwards.length > 0 && (
|
{floatingAwards.length > 0 && (
|
||||||
<div className="mt-4 pt-4 border-t border-dashed">
|
<div className="pt-2 border-t border-dashed">
|
||||||
<p className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground mb-2 pl-10">
|
<p className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground mb-2 pl-10">
|
||||||
Unlinked Awards
|
Unlinked Awards
|
||||||
</p>
|
</p>
|
||||||
@@ -685,6 +638,110 @@ export default function RoundsPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Round Row ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function RoundRow({ round, isLast }: { round: RoundWithStats; isLast: boolean }) {
|
||||||
|
const typeColors = ROUND_TYPE_COLORS[round.roundType] ?? ROUND_TYPE_COLORS.INTAKE
|
||||||
|
const statusStyle = ROUND_STATUS_STYLES[round.status] ?? ROUND_STATUS_STYLES.ROUND_DRAFT
|
||||||
|
const projectCount = round._count.projectRoundStates
|
||||||
|
const assignmentCount = round._count.assignments
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex">
|
||||||
|
{/* Left: pipeline track */}
|
||||||
|
<div className="flex flex-col items-center shrink-0 w-10">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="relative z-10 flex items-center justify-center">
|
||||||
|
<div
|
||||||
|
className="h-3.5 w-3.5 rounded-full border-2 border-white shadow-sm"
|
||||||
|
style={{ backgroundColor: statusStyle.color }}
|
||||||
|
/>
|
||||||
|
{statusStyle.pulse && (
|
||||||
|
<div
|
||||||
|
className="absolute h-3.5 w-3.5 rounded-full animate-ping opacity-40"
|
||||||
|
style={{ backgroundColor: statusStyle.color }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="left" className="text-xs">
|
||||||
|
{statusStyle.label}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
{!isLast && (
|
||||||
|
<div className="w-px flex-1 min-h-[8px] bg-border" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right: round content */}
|
||||||
|
<div className="flex-1 min-w-0 pb-2">
|
||||||
|
<Link
|
||||||
|
href={`/admin/rounds/${round.id}` as Route}
|
||||||
|
className="block"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center gap-3 px-3 py-2.5 rounded-md border-l-[3px] cursor-pointer transition-all',
|
||||||
|
'bg-white hover:bg-gray-50/80 hover:shadow-sm',
|
||||||
|
)}
|
||||||
|
style={{ borderLeftColor: typeColors.dot }}
|
||||||
|
>
|
||||||
|
<span className={cn(
|
||||||
|
'text-[10px] font-semibold uppercase tracking-wider shrink-0 w-[70px]',
|
||||||
|
typeColors.text
|
||||||
|
)}>
|
||||||
|
{round.roundType.replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-semibold text-[#053d57] truncate group-hover:text-[#de0f1e] transition-colors min-w-0 flex-1">
|
||||||
|
{round.name}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div className="hidden sm:flex items-center gap-3 text-xs text-muted-foreground shrink-0">
|
||||||
|
{round.juryGroup && (
|
||||||
|
<span className="flex items-center gap-1 max-w-[120px]">
|
||||||
|
<Users className="h-3 w-3 shrink-0" />
|
||||||
|
<span className="truncate">{round.juryGroup.name}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<FileBox className="h-3 w-3 shrink-0" />
|
||||||
|
{projectCount}
|
||||||
|
</span>
|
||||||
|
{assignmentCount > 0 && (
|
||||||
|
<span className="tabular-nums">{assignmentCount} asgn</span>
|
||||||
|
)}
|
||||||
|
{(round.windowOpenAt || round.windowCloseAt) && (
|
||||||
|
<span className="flex items-center gap-1 tabular-nums">
|
||||||
|
<Calendar className="h-3 w-3 shrink-0" />
|
||||||
|
{round.windowOpenAt
|
||||||
|
? new Date(round.windowOpenAt).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })
|
||||||
|
: ''}
|
||||||
|
{round.windowOpenAt && round.windowCloseAt ? ' \u2013 ' : ''}
|
||||||
|
{round.windowCloseAt
|
||||||
|
? new Date(round.windowCloseAt).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })
|
||||||
|
: ''}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-[10px] px-1.5 py-0 h-5 font-medium shrink-0 hidden md:inline-flex"
|
||||||
|
style={{ color: statusStyle.color, borderColor: statusStyle.color + '40' }}
|
||||||
|
>
|
||||||
|
{statusStyle.label}
|
||||||
|
</Badge>
|
||||||
|
|
||||||
|
<ArrowRight className="h-3.5 w-3.5 text-muted-foreground/30 group-hover:text-[#de0f1e]/60 transition-colors shrink-0" />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Award Node ──────────────────────────────────────────────────────────────
|
// ─── Award Node ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function AwardNode({ award }: { award: SpecialAwardItem }) {
|
function AwardNode({ award }: { award: SpecialAwardItem }) {
|
||||||
|
|||||||
Reference in New Issue
Block a user