diff --git a/src/app/(auth)/onboarding/page.tsx b/src/app/(auth)/onboarding/page.tsx index c2dc95e..403d955 100644 --- a/src/app/(auth)/onboarding/page.tsx +++ b/src/app/(auth)/onboarding/page.tsx @@ -238,7 +238,7 @@ export default function OnboardingPage() { return (
- +
{/* Progress indicator */}
diff --git a/src/app/(jury)/jury/competitions/page.tsx b/src/app/(jury)/jury/competitions/page.tsx index 85ba86e..fe4654a 100644 --- a/src/app/(jury)/jury/competitions/page.tsx +++ b/src/app/(jury)/jury/competitions/page.tsx @@ -3,38 +3,59 @@ import Link from 'next/link' import type { Route } from 'next' import { trpc } from '@/lib/trpc/client' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { Button } from '@/components/ui/button' -import { ArrowLeft, ArrowRight, ClipboardList, Target } from 'lucide-react' -import { toast } from 'sonner' +import { StatusBadge } from '@/components/shared/status-badge' +import { ProjectLogo } from '@/components/shared/project-logo' +import { + ArrowLeft, + ArrowRight, + ClipboardList, + CheckCircle2, + Clock, + FileEdit, +} from 'lucide-react' +import { formatDateOnly, formatEnumLabel } from '@/lib/utils' -export default function JuryCompetitionsPage() { - const { data: competitions, isLoading } = trpc.competition.getMyCompetitions.useQuery() +export default function JuryAssignmentsPage() { + const { data: assignments, isLoading } = trpc.assignment.myAssignments.useQuery({}) if (isLoading) { return (
-
- {[1, 2, 3].map((i) => ( - +
+ {[1, 2, 3, 4].map((i) => ( + ))}
) } + // Group assignments by round + const byRound = new Map() + for (const a of assignments ?? []) { + if (!a.round) continue + if (!byRound.has(a.round.id)) { + byRound.set(a.round.id, { round: a.round, items: [] }) + } + byRound.get(a.round.id)!.items!.push(a) + } + + const roundGroups = Array.from(byRound.values()) + return (

- My Competitions + My Assignments

- View competitions and rounds you're assigned to + Projects assigned to you for evaluation

- {!competitions || competitions.length === 0 ? ( + {roundGroups.length === 0 ? (
-

No Competitions

+

No Assignments

- You don't have any active competition assignments yet. + You don't have any active assignments yet.

) : ( -
- {competitions.map((competition) => { - const activeRounds = competition.rounds?.filter(r => r.status !== 'ROUND_ARCHIVED') || [] - const totalRounds = competition.rounds?.length || 0 +
+ {roundGroups.map(({ round, items }) => { + const completed = (items ?? []).filter( + (a) => a.evaluation?.status === 'SUBMITTED' + ).length + const total = items?.length ?? 0 return ( - - -
-
- {competition.name} + + +
+
+ {round.name} + + {formatEnumLabel(round.roundType)} + +
+
+ + {completed}/{total} completed + + {round.windowCloseAt && ( + + + Due {formatDateOnly(round.windowCloseAt)} + + )}
- - {totalRounds} round{totalRounds !== 1 ? 's' : ''} -
- + +
+ {(items ?? []).map((assignment) => { + const project = assignment.project + const evalStatus = assignment.evaluation?.status + const isSubmitted = evalStatus === 'SUBMITTED' + const isDraft = evalStatus === 'DRAFT' -
- -
- {activeRounds.length > 0 ? ( - activeRounds.slice(0, 2).map((round) => ( + return ( -
- - {round.name} +
+ +
+

+ {project.title} +

+

+ {[project.teamName, project.country].filter(Boolean).join(' \u00b7 ')} +

+
+
+ {isSubmitted ? ( + + + Submitted + + ) : isDraft ? ( + + + Draft + + ) : ( + + + Pending + + )} + +
- - )) - ) : ( -

- No active rounds -

- )} - {activeRounds.length > 2 && ( -

- +{activeRounds.length - 2} more round{activeRounds.length - 2 !== 1 ? 's' : ''} -

- )} + ) + })}
diff --git a/src/components/layouts/jury-nav.tsx b/src/components/layouts/jury-nav.tsx index ab680f6..56f5597 100644 --- a/src/components/layouts/jury-nav.tsx +++ b/src/components/layouts/jury-nav.tsx @@ -1,6 +1,6 @@ 'use client' -import { BookOpen, Home, Trophy, Layers } from 'lucide-react' +import { BookOpen, Home, Trophy, ClipboardList } from 'lucide-react' import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav' import { trpc } from '@/lib/trpc/client' import { Badge } from '@/components/ui/badge' @@ -42,6 +42,11 @@ function RemainingBadge() { } export function JuryNav({ user }: JuryNavProps) { + const { data: myAwards } = trpc.specialAward.getMyAwards.useQuery( + undefined, + { refetchInterval: 60000 } + ) + const navigation: NavItem[] = [ { name: 'Dashboard', @@ -49,15 +54,19 @@ export function JuryNav({ user }: JuryNavProps) { icon: Home, }, { - name: 'Competitions', + name: 'Assignments', href: '/jury/competitions', - icon: Layers, - }, - { - name: 'Awards', - href: '/jury/awards', - icon: Trophy, + icon: ClipboardList, }, + ...(myAwards && myAwards.length > 0 + ? [ + { + name: 'Awards', + href: '/jury/awards', + icon: Trophy, + }, + ] + : []), { name: 'Learning Hub', href: '/jury/learning', diff --git a/src/components/shared/expertise-select.tsx b/src/components/shared/expertise-select.tsx index 6c9166b..8354100 100644 --- a/src/components/shared/expertise-select.tsx +++ b/src/components/shared/expertise-select.tsx @@ -182,7 +182,7 @@ export function ExpertiseSelect({ )} {/* Categories with expandable tag lists */} -
+
{Object.entries(filteredTagsByCategory) .sort(([a], [b]) => a.localeCompare(b)) .map(([category, categoryTags]) => { @@ -261,6 +261,10 @@ export function ExpertiseSelect({ ))}
+ {tag.name} )