Jury dashboard compact layout, assignment redesign, auth fixes
- Jury dashboard: collapse zero-assignment state into single welcome card with inline quick actions; merge completion bar into stats row; tighten spacing - Manual assignment: replace tiny Dialog modal with inline collapsible section featuring searchable juror combobox and multi-select project list with bulk assign - Fix applicant invite URL path (/auth/accept-invite -> /accept-invite) - Add APPLICANT role redirect to /my-submission from root page - Add Applicant label to accept-invite role display - Fix a/an grammar in invitation emails and accept-invite page - Set-password page: use MOPC logo instead of lock icon - Notification bell: remove filter tabs, always show all notifications Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -215,6 +215,54 @@ async function JuryDashboardContent() {
|
||||
},
|
||||
]
|
||||
|
||||
// Zero-assignment state: compact welcome card
|
||||
if (totalAssignments === 0) {
|
||||
return (
|
||||
<AnimatedCard index={0}>
|
||||
<Card className="overflow-hidden">
|
||||
<div className="h-1 w-full bg-gradient-to-r from-brand-teal/40 via-brand-blue/40 to-brand-teal/40" />
|
||||
<CardContent className="py-8 px-6">
|
||||
<div className="flex flex-col items-center text-center mb-6">
|
||||
<div className="rounded-2xl bg-gradient-to-br from-brand-teal/10 to-brand-blue/10 p-4 mb-3 dark:from-brand-teal/20 dark:to-brand-blue/20">
|
||||
<ClipboardList className="h-8 w-8 text-brand-teal/60" />
|
||||
</div>
|
||||
<p className="text-lg font-semibold">No assignments yet</p>
|
||||
<p className="text-sm text-muted-foreground mt-1 max-w-sm">
|
||||
Your project assignments will appear here once an administrator assigns them to you.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2 max-w-md mx-auto">
|
||||
<Link
|
||||
href="/jury/assignments"
|
||||
className="group flex items-center gap-3 rounded-xl border border-border/60 p-3 transition-all duration-200 hover:border-brand-blue/30 hover:bg-brand-blue/5 hover:-translate-y-0.5 hover:shadow-md dark:hover:border-brand-teal/30 dark:hover:bg-brand-teal/5"
|
||||
>
|
||||
<div className="rounded-lg bg-blue-50 p-2 transition-colors group-hover:bg-blue-100 dark:bg-blue-950/40">
|
||||
<ClipboardList className="h-4 w-4 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="font-semibold text-sm group-hover:text-brand-blue dark:group-hover:text-brand-teal transition-colors">All Assignments</p>
|
||||
<p className="text-xs text-muted-foreground">View evaluations</p>
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
href="/jury/compare"
|
||||
className="group flex items-center gap-3 rounded-xl border border-border/60 p-3 transition-all duration-200 hover:border-brand-teal/30 hover:bg-brand-teal/5 hover:-translate-y-0.5 hover:shadow-md"
|
||||
>
|
||||
<div className="rounded-lg bg-teal-50 p-2 transition-colors group-hover:bg-teal-100 dark:bg-teal-950/40">
|
||||
<GitCompare className="h-4 w-4 text-brand-teal" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="font-semibold text-sm group-hover:text-brand-teal transition-colors">Compare Projects</p>
|
||||
<p className="text-xs text-muted-foreground">Side-by-side view</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero CTA - Jump to next evaluation */}
|
||||
@@ -248,8 +296,8 @@ async function JuryDashboardContent() {
|
||||
</AnimatedCard>
|
||||
)}
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{/* Stats + Overall Completion in one row */}
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-5">
|
||||
{stats.map((stat, i) => (
|
||||
<AnimatedCard key={stat.label} index={i + 1}>
|
||||
<Card className={cn(
|
||||
@@ -268,43 +316,33 @@ async function JuryDashboardContent() {
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
))}
|
||||
{/* Overall completion as 5th stat card */}
|
||||
<AnimatedCard index={5}>
|
||||
<Card className="border-l-4 border-l-brand-teal transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md">
|
||||
<CardContent className="flex items-center gap-4 py-5 px-5">
|
||||
<div className="rounded-xl p-3 bg-brand-blue/10 dark:bg-brand-blue/20">
|
||||
<BarChart3 className="h-5 w-5 text-brand-blue dark:text-brand-teal" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-2xl font-bold tabular-nums tracking-tight text-brand-blue dark:text-brand-teal">
|
||||
{completionRate.toFixed(0)}%
|
||||
</p>
|
||||
<div className="relative h-1.5 w-full overflow-hidden rounded-full bg-muted/60 mt-1">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-brand-teal to-brand-blue transition-all duration-500 ease-out"
|
||||
style={{ width: `${completionRate}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
</div>
|
||||
|
||||
{/* Overall Progress */}
|
||||
<AnimatedCard index={5}>
|
||||
<Card className="overflow-hidden">
|
||||
<div className="h-1 w-full bg-gradient-to-r from-brand-teal via-brand-blue to-brand-teal" />
|
||||
<CardContent className="py-5 px-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-brand-blue/10 p-2 dark:bg-brand-blue/20">
|
||||
<BarChart3 className="h-4 w-4 text-brand-blue dark:text-brand-teal" />
|
||||
</div>
|
||||
<span className="text-sm font-semibold">Overall Completion</span>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-2xl font-bold tabular-nums text-brand-blue dark:text-brand-teal">
|
||||
{completionRate.toFixed(0)}%
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground ml-1">
|
||||
({completedAssignments}/{totalAssignments})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative h-3 w-full overflow-hidden rounded-full bg-muted/60">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-brand-teal to-brand-blue transition-all duration-500 ease-out"
|
||||
style={{ width: `${completionRate}%` }}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
|
||||
{/* Main content -- two column layout */}
|
||||
<div className="grid gap-6 lg:grid-cols-12">
|
||||
<div className="grid gap-4 lg:grid-cols-12">
|
||||
{/* Left column */}
|
||||
<div className="lg:col-span-7 space-y-6">
|
||||
<div className="lg:col-span-7 space-y-4">
|
||||
{/* Recent Assignments */}
|
||||
<AnimatedCard index={6}>
|
||||
<Card>
|
||||
@@ -402,11 +440,11 @@ async function JuryDashboardContent() {
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-10 text-center">
|
||||
<div className="rounded-2xl bg-brand-teal/10 p-4 mb-3">
|
||||
<ClipboardList className="h-8 w-8 text-brand-teal/60" />
|
||||
<div className="flex flex-col items-center justify-center py-6 text-center">
|
||||
<div className="rounded-2xl bg-brand-teal/10 p-3 mb-2">
|
||||
<ClipboardList className="h-6 w-6 text-brand-teal/60" />
|
||||
</div>
|
||||
<p className="font-medium text-muted-foreground">
|
||||
<p className="font-medium text-sm text-muted-foreground">
|
||||
No assignments yet
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground/70 mt-1 max-w-[240px]">
|
||||
@@ -462,7 +500,7 @@ async function JuryDashboardContent() {
|
||||
</div>
|
||||
|
||||
{/* Right column */}
|
||||
<div className="lg:col-span-5 space-y-6">
|
||||
<div className="lg:col-span-5 space-y-4">
|
||||
{/* Active Rounds */}
|
||||
{activeRounds.length > 0 && (
|
||||
<AnimatedCard index={8}>
|
||||
@@ -561,12 +599,12 @@ async function JuryDashboardContent() {
|
||||
)}
|
||||
|
||||
{/* No active rounds */}
|
||||
{activeRounds.length === 0 && totalAssignments > 0 && (
|
||||
{activeRounds.length === 0 && (
|
||||
<AnimatedCard index={8}>
|
||||
<Card>
|
||||
<CardContent className="flex flex-col items-center justify-center py-10 text-center">
|
||||
<div className="rounded-2xl bg-brand-teal/10 p-4 mb-3 dark:bg-brand-teal/20">
|
||||
<Clock className="h-7 w-7 text-brand-teal/70" />
|
||||
<CardContent className="flex flex-col items-center justify-center py-6 text-center">
|
||||
<div className="rounded-2xl bg-brand-teal/10 p-3 mb-2 dark:bg-brand-teal/20">
|
||||
<Clock className="h-6 w-6 text-brand-teal/70" />
|
||||
</div>
|
||||
<p className="font-semibold text-sm">No active voting rounds</p>
|
||||
<p className="text-xs text-muted-foreground mt-1 max-w-[220px]">
|
||||
@@ -618,24 +656,6 @@ async function JuryDashboardContent() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* No assignments at all */}
|
||||
{totalAssignments === 0 && (
|
||||
<AnimatedCard index={1}>
|
||||
<Card className="overflow-hidden">
|
||||
<div className="h-1 w-full bg-gradient-to-r from-brand-teal/40 via-brand-blue/40 to-brand-teal/40" />
|
||||
<CardContent className="flex flex-col items-center justify-center py-14 text-center">
|
||||
<div className="rounded-2xl bg-gradient-to-br from-brand-teal/10 to-brand-blue/10 p-5 mb-4 dark:from-brand-teal/20 dark:to-brand-blue/20">
|
||||
<ClipboardList className="h-10 w-10 text-brand-teal/60" />
|
||||
</div>
|
||||
<p className="text-lg font-semibold">No assignments yet</p>
|
||||
<p className="text-sm text-muted-foreground mt-1.5 max-w-sm">
|
||||
You'll see your project assignments here once they're assigned to you by an administrator.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</AnimatedCard>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -721,7 +741,7 @@ export default async function JuryDashboardPage() {
|
||||
const session = await auth()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
{/* Header */}
|
||||
<div className="relative">
|
||||
<div className="absolute -top-6 -left-6 -right-6 h-32 bg-gradient-to-b from-brand-blue/[0.03] to-transparent dark:from-brand-blue/[0.06] pointer-events-none rounded-xl" />
|
||||
|
||||
Reference in New Issue
Block a user