refactor(ui): strip all dark: Tailwind classes (single-theme product)
All checks were successful
Build and Push Docker Image / build (push) Successful in 12m17s
All checks were successful
Build and Push Docker Image / build (push) Successful in 12m17s
Mechanical sweep of 41 files via `perl -i -pe 's{\s+dark:[\w:/\[\]\.\-]+}{}g'`.
All dark: variants were paired with light-mode counterparts already; no
elements relied on a dark:-only style.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -335,20 +335,20 @@ function RoundsDndGrid({
|
||||
function ConfidenceBadge({ confidence }: { confidence: number }) {
|
||||
if (confidence > 0.8) {
|
||||
return (
|
||||
<Badge variant="outline" className="border-emerald-300 bg-emerald-50 text-emerald-700 dark:border-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-400 text-xs tabular-nums">
|
||||
<Badge variant="outline" className="border-emerald-300 bg-emerald-50 text-emerald-700 text-xs tabular-nums">
|
||||
{Math.round(confidence * 100)}%
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
if (confidence >= 0.5) {
|
||||
return (
|
||||
<Badge variant="outline" className="border-amber-300 bg-amber-50 text-amber-700 dark:border-amber-700 dark:bg-amber-950/30 dark:text-amber-400 text-xs tabular-nums">
|
||||
<Badge variant="outline" className="border-amber-300 bg-amber-50 text-amber-700 text-xs tabular-nums">
|
||||
{Math.round(confidence * 100)}%
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Badge variant="outline" className="border-red-300 bg-red-50 text-red-700 dark:border-red-700 dark:bg-red-950/30 dark:text-red-400 text-xs tabular-nums">
|
||||
<Badge variant="outline" className="border-red-300 bg-red-50 text-red-700 text-xs tabular-nums">
|
||||
{Math.round(confidence * 100)}%
|
||||
</Badge>
|
||||
)
|
||||
@@ -897,8 +897,8 @@ export default function AwardDetailPage({
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Eligible</p>
|
||||
<p className="text-2xl font-bold tabular-nums">{award.eligibleCount}</p>
|
||||
</div>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-950/40">
|
||||
<CheckCircle2 className="h-5 w-5 text-emerald-600 dark:text-emerald-400" />
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-emerald-100">
|
||||
<CheckCircle2 className="h-5 w-5 text-emerald-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -910,8 +910,8 @@ export default function AwardDetailPage({
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Evaluated</p>
|
||||
<p className="text-2xl font-bold tabular-nums">{(award as any).totalAssessed ?? award._count.eligibilities}</p>
|
||||
</div>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-950/40">
|
||||
<ListChecks className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-100">
|
||||
<ListChecks className="h-5 w-5 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -923,8 +923,8 @@ export default function AwardDetailPage({
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Jurors</p>
|
||||
<p className="text-2xl font-bold tabular-nums">{award._count.jurors}</p>
|
||||
</div>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-violet-100 dark:bg-violet-950/40">
|
||||
<Users className="h-5 w-5 text-violet-600 dark:text-violet-400" />
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-violet-100">
|
||||
<Users className="h-5 w-5 text-violet-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -936,8 +936,8 @@ export default function AwardDetailPage({
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Votes</p>
|
||||
<p className="text-2xl font-bold tabular-nums">{award._count.votes}</p>
|
||||
</div>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-amber-100 dark:bg-amber-950/40">
|
||||
<Vote className="h-5 w-5 text-amber-600 dark:text-amber-400" />
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-amber-100">
|
||||
<Vote className="h-5 w-5 text-amber-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -1612,7 +1612,7 @@ export default function AwardDetailPage({
|
||||
{/* Rounds Tab */}
|
||||
<TabsContent value="rounds" className="space-y-4">
|
||||
{award.eligibilityMode !== 'SEPARATE_POOL' && (
|
||||
<div className="flex items-start gap-2 rounded-md border border-blue-200 bg-blue-50 p-3 text-blue-800 dark:border-blue-800 dark:bg-blue-950/30 dark:text-blue-300">
|
||||
<div className="flex items-start gap-2 rounded-md border border-blue-200 bg-blue-50 p-3 text-blue-800">
|
||||
<Info className="h-4 w-4 mt-0.5 shrink-0" />
|
||||
<p className="text-sm">
|
||||
Rounds are used in <strong>Separate Pool</strong> mode to create a dedicated evaluation track for shortlisted projects.
|
||||
@@ -1620,7 +1620,7 @@ export default function AwardDetailPage({
|
||||
</div>
|
||||
)}
|
||||
{!award.competitionId && (
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-800 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-300">
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-800">
|
||||
<AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
|
||||
<p className="text-sm">
|
||||
Link this award to a competition first before creating rounds.
|
||||
@@ -1750,16 +1750,16 @@ export default function AwardDetailPage({
|
||||
return (
|
||||
<TableRow
|
||||
key={r.project.id}
|
||||
className={isWinner ? 'bg-amber-50/80 dark:bg-amber-950/20' : ''}
|
||||
className={isWinner ? 'bg-amber-50/80' : ''}
|
||||
>
|
||||
<TableCell>
|
||||
<span className={`inline-flex h-7 w-7 items-center justify-center rounded-full text-xs font-bold ${
|
||||
i === 0
|
||||
? 'bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300'
|
||||
? 'bg-amber-100 text-amber-800'
|
||||
: i === 1
|
||||
? 'bg-slate-200 text-slate-700 dark:bg-slate-700 dark:text-slate-300'
|
||||
? 'bg-slate-200 text-slate-700'
|
||||
: i === 2
|
||||
? 'bg-orange-100 text-orange-800 dark:bg-orange-900/40 dark:text-orange-300'
|
||||
? 'bg-orange-100 text-orange-800'
|
||||
: 'text-muted-foreground'
|
||||
}`}>
|
||||
{i + 1}
|
||||
|
||||
@@ -1047,7 +1047,7 @@ export default function MemberDetailPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/30 dark:text-amber-100">
|
||||
<div className="flex items-center justify-between gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-900">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="h-3.5 w-3.5 shrink-0" />
|
||||
<span>
|
||||
|
||||
@@ -907,7 +907,7 @@ export default function MemberInvitePage() {
|
||||
</div>
|
||||
|
||||
{!sendInvitation && (
|
||||
<div className="flex items-start gap-3 rounded-lg bg-blue-500/10 p-4 text-blue-700 dark:text-blue-400">
|
||||
<div className="flex items-start gap-3 rounded-lg bg-blue-500/10 p-4 text-blue-700">
|
||||
<MailX className="h-5 w-5 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-medium">No invitations will be sent</p>
|
||||
|
||||
@@ -473,7 +473,7 @@ function MentorAssignmentContent({ projectId }: { projectId: string }) {
|
||||
|
||||
<TabsContent value="ai" className="space-y-4">
|
||||
{aiSource === 'fallback' && (
|
||||
<div className="flex items-start gap-3 rounded-md border border-amber-300 bg-amber-50 p-3 text-sm dark:border-amber-700 dark:bg-amber-950/40">
|
||||
<div className="flex items-start gap-3 rounded-md border border-amber-300 bg-amber-50 p-3 text-sm">
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-amber-600" />
|
||||
<div>
|
||||
<p className="font-medium">AI matching unavailable</p>
|
||||
@@ -693,7 +693,7 @@ function PendingChangeRequestsPanel({ projectId }: { projectId: string }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="border-amber-300 dark:border-amber-700">
|
||||
<Card className="border-amber-300">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg flex items-center gap-2">
|
||||
<Inbox className="h-5 w-5 text-amber-600" />
|
||||
|
||||
@@ -462,7 +462,7 @@ export default function BulkUploadPage() {
|
||||
return (
|
||||
<TableRow
|
||||
key={row.project.id}
|
||||
className={row.isComplete ? 'bg-green-50/50 dark:bg-green-950/10' : ''}
|
||||
className={row.isComplete ? 'bg-green-50/50' : ''}
|
||||
>
|
||||
<TableCell>
|
||||
<Link
|
||||
|
||||
@@ -53,15 +53,15 @@ type TeamMemberEntry = {
|
||||
}
|
||||
|
||||
const ROLE_COLORS: Record<string, string> = {
|
||||
LEAD: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',
|
||||
MEMBER: 'bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-400',
|
||||
ADVISOR: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
|
||||
LEAD: 'bg-red-100 text-red-700',
|
||||
MEMBER: 'bg-teal-100 text-teal-700',
|
||||
ADVISOR: 'bg-blue-100 text-blue-700',
|
||||
}
|
||||
|
||||
const ROLE_AVATAR_COLORS: Record<string, string> = {
|
||||
LEAD: 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300',
|
||||
MEMBER: 'bg-teal-100 text-teal-700 dark:bg-teal-900/40 dark:text-teal-300',
|
||||
ADVISOR: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
|
||||
LEAD: 'bg-red-100 text-red-700',
|
||||
MEMBER: 'bg-teal-100 text-teal-700',
|
||||
ADVISOR: 'bg-blue-100 text-blue-700',
|
||||
}
|
||||
|
||||
const ROLE_LABELS: Record<string, string> = {
|
||||
|
||||
@@ -679,7 +679,7 @@ export default function ProjectsPage() {
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setAiTagDialogOpen(true)}
|
||||
className={taggingInProgress ? 'border-amber-400 bg-amber-50 dark:bg-amber-950/20' : ''}
|
||||
className={taggingInProgress ? 'border-amber-400 bg-amber-50' : ''}
|
||||
>
|
||||
{taggingInProgress ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin text-amber-600" />
|
||||
@@ -1716,15 +1716,15 @@ export default function ProjectsPage() {
|
||||
<div className="space-y-6 py-4">
|
||||
{/* Progress Indicator (when running) */}
|
||||
{taggingInProgress && (
|
||||
<div className="p-4 rounded-lg bg-blue-50 dark:bg-blue-950/20 border border-blue-200 dark:border-blue-900">
|
||||
<div className="p-4 rounded-lg bg-blue-50 border border-blue-200">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Loader2 className="h-5 w-5 animate-spin text-blue-600" />
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-blue-900 dark:text-blue-100">
|
||||
<p className="font-medium text-blue-900">
|
||||
AI Tagging in Progress
|
||||
</p>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||
<p className="text-sm text-blue-700">
|
||||
{jobStatus?.status === 'PENDING'
|
||||
? 'Initializing...'
|
||||
: `Processing ${jobStatus?.totalProjects || 0} projects with AI...`}
|
||||
@@ -1739,12 +1739,12 @@ export default function ProjectsPage() {
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-blue-700 dark:text-blue-300">
|
||||
<span className="text-blue-700">
|
||||
{jobStatus?.processedCount || 0} of {jobStatus?.totalProjects || '?'} projects processed
|
||||
{jobStatus?.taggedCount ? ` (${jobStatus.taggedCount} tagged)` : ''}
|
||||
</span>
|
||||
{jobStatus && jobStatus.totalProjects > 0 && (
|
||||
<span className="font-medium text-blue-900 dark:text-blue-100">
|
||||
<span className="font-medium text-blue-900">
|
||||
{taggingProgressPercent}%
|
||||
</span>
|
||||
)}
|
||||
@@ -1767,9 +1767,9 @@ export default function ProjectsPage() {
|
||||
{taggingResult && !taggingInProgress && (
|
||||
<div className={`p-4 rounded-lg border ${
|
||||
taggingResult.failed > 0
|
||||
? 'bg-amber-50 dark:bg-amber-950/20 border-amber-200 dark:border-amber-900'
|
||||
? 'bg-amber-50 border-amber-200'
|
||||
: taggingResult.processed > 0
|
||||
? 'bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-900'
|
||||
? 'bg-green-50 border-green-200'
|
||||
: 'bg-muted border-border'
|
||||
}`}>
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
@@ -1804,12 +1804,12 @@ export default function ProjectsPage() {
|
||||
</div>
|
||||
{taggingResult.errors.length > 0 && (
|
||||
<div className="mt-3 space-y-2">
|
||||
<p className="text-sm font-medium text-amber-700 dark:text-amber-300">
|
||||
<p className="text-sm font-medium text-amber-700">
|
||||
{taggingResult.errors.length} project{taggingResult.errors.length > 1 ? 's' : ''} failed:
|
||||
</p>
|
||||
<div className="max-h-32 overflow-y-auto rounded bg-background/50 p-2 text-xs space-y-1">
|
||||
{taggingResult.errors.map((error, i) => (
|
||||
<p key={i} className="text-amber-700 dark:text-amber-300">
|
||||
<p key={i} className="text-amber-700">
|
||||
• {error}
|
||||
</p>
|
||||
))}
|
||||
|
||||
@@ -394,7 +394,7 @@ export default function AdminProxyEvaluatePage({ params: paramsPromise }: PagePr
|
||||
</Link>
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
{isReadOnly ? 'Submitted Evaluation' : 'Fill In Evaluation'}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 mt-1 flex-wrap">
|
||||
@@ -404,8 +404,8 @@ export default function AdminProxyEvaluatePage({ params: paramsPromise }: PagePr
|
||||
variant="secondary"
|
||||
className={
|
||||
project.competitionCategory === 'STARTUP'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200 dark:bg-violet-950 dark:text-violet-300'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200 dark:bg-sky-950 dark:text-sky-300'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200'
|
||||
}
|
||||
>
|
||||
{project.competitionCategory === 'STARTUP' ? 'Startup' : 'Business Concept'}
|
||||
@@ -415,7 +415,7 @@ export default function AdminProxyEvaluatePage({ params: paramsPromise }: PagePr
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="border-l-4 border-l-amber-500 bg-amber-50/40 dark:bg-amber-950/10">
|
||||
<Card className="border-l-4 border-l-amber-500 bg-amber-50/40">
|
||||
<CardContent className="flex items-start gap-3 p-4">
|
||||
<UserCheck className="h-5 w-5 text-amber-600 shrink-0 mt-0.5" />
|
||||
<div className="flex-1 text-sm">
|
||||
@@ -431,7 +431,7 @@ export default function AdminProxyEvaluatePage({ params: paramsPromise }: PagePr
|
||||
</Card>
|
||||
|
||||
{isReadOnly && (
|
||||
<Card className="border-l-4 border-l-blue-500 bg-blue-50/50 dark:bg-blue-950/20">
|
||||
<Card className="border-l-4 border-l-blue-500 bg-blue-50/50">
|
||||
<CardContent className="flex items-start gap-3 p-4">
|
||||
<Lock className="h-5 w-5 text-blue-600 shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
@@ -446,7 +446,7 @@ export default function AdminProxyEvaluatePage({ params: paramsPromise }: PagePr
|
||||
)}
|
||||
|
||||
{hasCOI && !isReadOnly && (
|
||||
<Card className="border-l-4 border-l-red-500 bg-red-50/40 dark:bg-red-950/10">
|
||||
<Card className="border-l-4 border-l-red-500 bg-red-50/40">
|
||||
<CardContent className="flex items-start gap-3 p-4">
|
||||
<Lock className="h-5 w-5 text-red-600 shrink-0 mt-0.5" />
|
||||
<div className="flex-1 text-sm">
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function AdminJurorProxyEvaluatePage({ params: paramsPromise }: P
|
||||
</Link>
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
Proxy Evaluations
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
@@ -205,8 +205,8 @@ function AssignmentRow({ roundId, userId, assignment, mode }: AssignmentRowProps
|
||||
className={cn(
|
||||
'shrink-0',
|
||||
project.competitionCategory === 'STARTUP'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200 dark:bg-violet-950 dark:text-violet-300'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200 dark:bg-sky-950 dark:text-sky-300',
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200',
|
||||
)}
|
||||
>
|
||||
{project.competitionCategory === 'STARTUP' ? 'Startup' : 'Business Concept'}
|
||||
|
||||
@@ -2074,39 +2074,39 @@ export default function RoundDetailPage() {
|
||||
</p>
|
||||
)}
|
||||
{aiAssignmentMutation.isPending && (
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-violet-50 border border-violet-200 dark:bg-violet-950/20 dark:border-violet-800">
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-violet-50 border border-violet-200">
|
||||
<div className="relative">
|
||||
<div className="h-8 w-8 rounded-full border-2 border-violet-300 border-t-violet-600 animate-spin" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-violet-800 dark:text-violet-200">AI is analyzing projects and jurors...</p>
|
||||
<p className="text-xs text-violet-600 dark:text-violet-400">
|
||||
<p className="text-sm font-medium text-violet-800">AI is analyzing projects and jurors...</p>
|
||||
<p className="text-xs text-violet-600">
|
||||
Matching expertise, reviewing bios, and balancing workloads
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{aiAssignmentMutation.error && !aiAssignmentMutation.isPending && (
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-red-50 border border-red-200 dark:bg-red-950/20 dark:border-red-800">
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-red-50 border border-red-200">
|
||||
<AlertTriangle className="h-5 w-5 text-red-600 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-red-800 dark:text-red-200">
|
||||
<p className="text-sm font-medium text-red-800">
|
||||
AI generation failed
|
||||
</p>
|
||||
<p className="text-xs text-red-600 dark:text-red-400">
|
||||
<p className="text-xs text-red-600">
|
||||
{aiAssignmentMutation.error.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{aiAssignmentMutation.data && !aiAssignmentMutation.isPending && (
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-emerald-50 border border-emerald-200 dark:bg-emerald-950/20 dark:border-emerald-800">
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-emerald-50 border border-emerald-200">
|
||||
<CheckCircle2 className="h-5 w-5 text-emerald-600 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-emerald-800 dark:text-emerald-200">
|
||||
<p className="text-sm font-medium text-emerald-800">
|
||||
{aiAssignmentMutation.data.stats.assignmentsGenerated} assignments generated
|
||||
</p>
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400">
|
||||
<p className="text-xs text-emerald-600">
|
||||
{aiAssignmentMutation.data.stats.totalJurors} jurors, {aiAssignmentMutation.data.stats.totalProjects} projects
|
||||
{aiAssignmentMutation.data.fallbackUsed && ' (algorithm fallback)'}
|
||||
</p>
|
||||
@@ -2588,9 +2588,9 @@ export default function RoundDetailPage() {
|
||||
|
||||
{/* Autosave error bar — only shows when save fails */}
|
||||
{autosaveStatus === 'error' && (
|
||||
<div className="fixed bottom-0 left-0 right-0 z-50 border-t bg-red-50 dark:bg-red-950/50 shadow-[0_-4px_12px_rgba(0,0,0,0.1)]">
|
||||
<div className="fixed bottom-0 left-0 right-0 z-50 border-t bg-red-50 shadow-[0_-4px_12px_rgba(0,0,0,0.1)]">
|
||||
<div className="container flex items-center justify-between py-3 px-4 max-w-5xl mx-auto">
|
||||
<div className="flex items-center gap-2 text-sm text-red-700 dark:text-red-300">
|
||||
<div className="flex items-center gap-2 text-sm text-red-700">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<span>Auto-save failed</span>
|
||||
</div>
|
||||
|
||||
@@ -219,12 +219,12 @@ export default function ApplicantDashboardPage() {
|
||||
key={round.id}
|
||||
className={`flex flex-col sm:flex-row items-start sm:items-center gap-3 rounded-lg border px-4 py-3 ${
|
||||
isUrgent
|
||||
? 'border-amber-500/50 bg-amber-50 dark:bg-amber-950/20'
|
||||
? 'border-amber-500/50 bg-amber-50'
|
||||
: 'border-primary/20 bg-primary/5'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<Clock className={`h-4 w-4 shrink-0 ${isUrgent ? 'text-amber-600 dark:text-amber-400' : 'text-primary'}`} />
|
||||
<Clock className={`h-4 w-4 shrink-0 ${isUrgent ? 'text-amber-600' : 'text-primary'}`} />
|
||||
<span className="font-medium text-sm truncate">{round.name}</span>
|
||||
<Badge variant={isUrgent ? 'warning' : 'default'} className="shrink-0">
|
||||
{remaining > 0 ? formatCountdown(remaining) + ' left' : 'Closed'}
|
||||
|
||||
@@ -427,10 +427,10 @@ function ProjectDetails({ project }: { project: ProjectData }) {
|
||||
return (
|
||||
<div className="px-4 pb-4 pt-2 space-y-4 border-t mt-2">
|
||||
{project.evaluationScore && (
|
||||
<div className="flex items-center gap-2 rounded-md bg-blue-50/50 dark:bg-blue-950/20 px-3 py-2">
|
||||
<Star className="h-4 w-4 text-blue-600 dark:text-blue-400 shrink-0" />
|
||||
<div className="flex items-center gap-2 rounded-md bg-blue-50/50 px-3 py-2">
|
||||
<Star className="h-4 w-4 text-blue-600 shrink-0" />
|
||||
<div className="text-sm">
|
||||
<span className="font-semibold text-blue-700 dark:text-blue-300">
|
||||
<span className="font-semibold text-blue-700">
|
||||
{project.evaluationScore.avg.toFixed(1)} / 10
|
||||
</span>
|
||||
<span className="text-muted-foreground ml-2">
|
||||
@@ -518,7 +518,7 @@ function ProjectCard({
|
||||
isExpanded && 'rotate-180'
|
||||
)} />
|
||||
<div className="min-w-0">
|
||||
<h3 className="font-semibold text-sm group-hover:text-brand-blue dark:group-hover:text-brand-teal transition-colors">
|
||||
<h3 className="font-semibold text-sm group-hover:text-brand-blue transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
@@ -587,7 +587,7 @@ function ChairPanel({
|
||||
const isClosed = award.status === 'CLOSED'
|
||||
|
||||
return (
|
||||
<Card className="border-amber-200 dark:border-amber-900">
|
||||
<Card className="border-amber-200">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<Gavel className="h-5 w-5 text-amber-600" />
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function JuryRoundDetailPage() {
|
||||
Back
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
{round?.name || 'Round Details'}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
|
||||
@@ -460,7 +460,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</div>
|
||||
<Card className="border-l-4 border-l-amber-500">
|
||||
<CardContent className="flex items-start gap-4 p-6">
|
||||
<div className="rounded-xl bg-amber-50 dark:bg-amber-950/40 p-3">
|
||||
<div className="rounded-xl bg-amber-50 p-3">
|
||||
<Clock className="h-6 w-6 text-amber-600" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -495,7 +495,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</Link>
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
Evaluate Project
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">{project.title}</p>
|
||||
@@ -526,7 +526,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</Link>
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
Evaluate Project
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">{project.title}</p>
|
||||
@@ -534,7 +534,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</div>
|
||||
<Card className="border-l-4 border-l-amber-500">
|
||||
<CardContent className="flex items-start gap-4 p-6">
|
||||
<div className="rounded-xl bg-amber-50 dark:bg-amber-950/40 p-3">
|
||||
<div className="rounded-xl bg-amber-50 p-3">
|
||||
<ShieldAlert className="h-6 w-6 text-amber-600" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -573,7 +573,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</Button>
|
||||
)}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
{isReadOnly ? 'Submitted Evaluation' : 'Evaluate Project'}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
@@ -583,8 +583,8 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
variant="secondary"
|
||||
className={
|
||||
project.competitionCategory === 'STARTUP'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200 dark:bg-violet-950 dark:text-violet-300'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200 dark:bg-sky-950 dark:text-sky-300'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200'
|
||||
}
|
||||
>
|
||||
{project.competitionCategory === 'STARTUP' ? 'Startup' : 'Business Concept'}
|
||||
@@ -595,7 +595,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</div>
|
||||
|
||||
{isReadOnly && (
|
||||
<Card className="border-l-4 border-l-blue-500 bg-blue-50/50 dark:bg-blue-950/20">
|
||||
<Card className="border-l-4 border-l-blue-500 bg-blue-50/50">
|
||||
<CardContent className="flex items-start gap-3 p-4">
|
||||
<Lock className="h-5 w-5 text-blue-600 shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -98,8 +98,8 @@ export default function JuryProjectDetailPage() {
|
||||
variant="secondary"
|
||||
className={
|
||||
project.competitionCategory === 'STARTUP'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200 dark:bg-violet-950 dark:text-violet-300'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200 dark:bg-sky-950 dark:text-sky-300'
|
||||
? 'bg-violet-100 text-violet-700 border-violet-200'
|
||||
: 'bg-sky-100 text-sky-700 border-sky-200'
|
||||
}
|
||||
>
|
||||
{project.competitionCategory === 'STARTUP' ? 'Startup' : 'Business Concept'}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function JuryAssignmentsPage() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
My Assignments
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
|
||||
@@ -262,7 +262,7 @@ async function JuryDashboardContent() {
|
||||
<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">
|
||||
<div className="rounded-2xl bg-gradient-to-br from-brand-teal/10 to-brand-blue/10 p-4 mb-3">
|
||||
<ClipboardList className="h-8 w-8 text-brand-teal/60" />
|
||||
</div>
|
||||
<p className="text-lg font-semibold">No assignments yet</p>
|
||||
@@ -273,13 +273,13 @@ async function JuryDashboardContent() {
|
||||
<div className={`grid gap-3 max-w-md mx-auto ${juryCompareEnabled ? 'sm:grid-cols-2' : ''}`}>
|
||||
<Link
|
||||
href="/jury/competitions"
|
||||
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"
|
||||
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"
|
||||
>
|
||||
<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 className="rounded-lg bg-blue-50 p-2 transition-colors group-hover:bg-blue-100">
|
||||
<ClipboardList className="h-4 w-4 text-blue-600" />
|
||||
</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="font-semibold text-sm group-hover:text-brand-blue transition-colors">All Assignments</p>
|
||||
<p className="text-xs text-muted-foreground">View evaluations</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -288,7 +288,7 @@ async function JuryDashboardContent() {
|
||||
href="/jury/competitions"
|
||||
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">
|
||||
<div className="rounded-lg bg-teal-50 p-2 transition-colors group-hover:bg-teal-100">
|
||||
<GitCompare className="h-4 w-4 text-brand-teal" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
@@ -314,8 +314,8 @@ async function JuryDashboardContent() {
|
||||
<div className="rounded-[7px] bg-background">
|
||||
<CardHeader className="pb-2 pt-4 px-5">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-amber-100 p-1.5 dark:bg-amber-900/40">
|
||||
<Trophy className="h-4 w-4 text-amber-600 dark:text-amber-400" />
|
||||
<div className="rounded-lg bg-amber-100 p-1.5">
|
||||
<Trophy className="h-4 w-4 text-amber-600" />
|
||||
</div>
|
||||
<CardTitle className="text-lg">Special Awards — Voting Open</CardTitle>
|
||||
</div>
|
||||
@@ -333,27 +333,27 @@ async function JuryDashboardContent() {
|
||||
className={cn(
|
||||
'rounded-xl border p-4 space-y-3 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',
|
||||
hasVoted
|
||||
? 'border-green-200/60 bg-green-50/30 dark:border-green-800/40 dark:bg-green-950/10'
|
||||
? 'border-green-200/60 bg-green-50/30'
|
||||
: isUrgent
|
||||
? 'border-red-200 bg-red-50/50 dark:border-red-900 dark:bg-red-950/20'
|
||||
: 'border-amber-200/60 bg-amber-50/30 dark:border-amber-800/40 dark:bg-amber-950/10'
|
||||
? 'border-red-200 bg-red-50/50'
|
||||
: 'border-amber-200/60 bg-amber-50/30'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className={cn('font-semibold', hasVoted ? 'text-green-700 dark:text-green-400' : 'text-amber-700 dark:text-amber-400')}>{award.name}</h3>
|
||||
<h3 className={cn('font-semibold', hasVoted ? 'text-green-700' : 'text-amber-700')}>{award.name}</h3>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
{award._count.eligibilities} project{award._count.eligibilities !== 1 ? 's' : ''} to review
|
||||
{record.isChair && ' · You are the Chair'}
|
||||
</p>
|
||||
</div>
|
||||
{hasVoted ? (
|
||||
<Badge className="bg-green-100 text-green-800 border-green-300 dark:bg-green-950 dark:text-green-300 dark:border-green-700">
|
||||
<Badge className="bg-green-100 text-green-800 border-green-300">
|
||||
<CheckCircle2 className="mr-1 h-3 w-3" />
|
||||
Submitted
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge className="bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-700">
|
||||
<Badge className="bg-amber-100 text-amber-800 border-amber-300">
|
||||
Vote Now
|
||||
</Badge>
|
||||
)}
|
||||
@@ -452,8 +452,8 @@ async function JuryDashboardContent() {
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-brand-blue/10 p-1.5 dark:bg-brand-blue/20">
|
||||
<ClipboardList className="h-4 w-4 text-brand-blue dark:text-brand-teal" />
|
||||
<div className="rounded-lg bg-brand-blue/10 p-1.5">
|
||||
<ClipboardList className="h-4 w-4 text-brand-blue" />
|
||||
</div>
|
||||
<CardTitle className="text-lg">My Assignments</CardTitle>
|
||||
</div>
|
||||
@@ -487,14 +487,14 @@ async function JuryDashboardContent() {
|
||||
href={`/jury/competitions/${assignment.round.id}/projects/${assignment.project.id}`}
|
||||
className="flex-1 min-w-0 group"
|
||||
>
|
||||
<p className="text-sm font-medium truncate group-hover:text-brand-blue dark:group-hover:text-brand-teal transition-colors">
|
||||
<p className="text-sm font-medium truncate group-hover:text-brand-blue transition-colors">
|
||||
{assignment.project.title}
|
||||
</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs text-muted-foreground truncate">
|
||||
{assignment.project.teamName}
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 bg-brand-blue/5 text-brand-blue/80 dark:bg-brand-teal/10 dark:text-brand-teal/80 border-0">
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 bg-brand-blue/5 text-brand-blue/80 border-0">
|
||||
{assignment.round.name}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -506,7 +506,7 @@ async function JuryDashboardContent() {
|
||||
Done
|
||||
</Badge>
|
||||
) : isDraft && isVotingOpen ? (
|
||||
<Badge className="text-xs bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-950 dark:text-amber-300 dark:border-amber-700 animate-pulse">
|
||||
<Badge className="text-xs bg-amber-100 text-amber-800 border-amber-300 animate-pulse">
|
||||
<Send className="mr-1 h-3 w-3" />
|
||||
Ready to submit
|
||||
</Badge>
|
||||
@@ -571,7 +571,7 @@ async function JuryDashboardContent() {
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-brand-teal/10 p-1.5 dark:bg-brand-teal/20">
|
||||
<div className="rounded-lg bg-brand-teal/10 p-1.5">
|
||||
<Zap className="h-4 w-4 text-brand-teal" />
|
||||
</div>
|
||||
<CardTitle className="text-lg">Quick Actions</CardTitle>
|
||||
@@ -581,13 +581,13 @@ async function JuryDashboardContent() {
|
||||
<div className={`grid gap-3 ${juryCompareEnabled ? 'sm:grid-cols-2' : ''}`}>
|
||||
<Link
|
||||
href="/jury/competitions"
|
||||
className="group flex items-center gap-4 rounded-xl border border-border/60 p-4 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"
|
||||
className="group flex items-center gap-4 rounded-xl border border-border/60 p-4 transition-all duration-200 hover:border-brand-blue/30 hover:bg-brand-blue/5 hover:-translate-y-0.5 hover:shadow-md"
|
||||
>
|
||||
<div className="rounded-xl bg-blue-50 p-3 transition-colors group-hover:bg-blue-100 dark:bg-blue-950/40 dark:group-hover:bg-blue-950/60">
|
||||
<ClipboardList className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
<div className="rounded-xl bg-blue-50 p-3 transition-colors group-hover:bg-blue-100">
|
||||
<ClipboardList className="h-5 w-5 text-blue-600" />
|
||||
</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="font-semibold text-sm group-hover:text-brand-blue transition-colors">All Assignments</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">View and manage evaluations</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -596,7 +596,7 @@ async function JuryDashboardContent() {
|
||||
href="/jury/competitions"
|
||||
className="group flex items-center gap-4 rounded-xl border border-border/60 p-4 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-xl bg-teal-50 p-3 transition-colors group-hover:bg-teal-100 dark:bg-teal-950/40 dark:group-hover:bg-teal-950/60">
|
||||
<div className="rounded-xl bg-teal-50 p-3 transition-colors group-hover:bg-teal-100">
|
||||
<GitCompare className="h-5 w-5 text-brand-teal" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
@@ -620,8 +620,8 @@ async function JuryDashboardContent() {
|
||||
<div className="h-1 w-full bg-gradient-to-r from-brand-blue via-brand-teal to-brand-blue" />
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-brand-blue/10 p-1.5 dark:bg-brand-blue/20">
|
||||
<Waves className="h-4 w-4 text-brand-blue dark:text-brand-teal" />
|
||||
<div className="rounded-lg bg-brand-blue/10 p-1.5">
|
||||
<Waves className="h-4 w-4 text-brand-blue" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-lg">Active Voting Stages</CardTitle>
|
||||
@@ -650,13 +650,13 @@ async function JuryDashboardContent() {
|
||||
className={cn(
|
||||
'rounded-xl border p-4 space-y-3 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',
|
||||
isUrgent
|
||||
? 'border-red-200 bg-red-50/50 dark:border-red-900 dark:bg-red-950/20'
|
||||
: 'border-border/60 bg-muted/20 dark:bg-muted/10'
|
||||
? 'border-red-200 bg-red-50/50'
|
||||
: 'border-border/60 bg-muted/20'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-brand-blue dark:text-brand-teal">{round.name}</h3>
|
||||
<h3 className="font-semibold text-brand-blue">{round.name}</h3>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
{program.name} · {program.year}
|
||||
</p>
|
||||
@@ -716,7 +716,7 @@ async function JuryDashboardContent() {
|
||||
<AnimatedCard index={8}>
|
||||
<Card>
|
||||
<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">
|
||||
<div className="rounded-2xl bg-brand-teal/10 p-3 mb-2">
|
||||
<Clock className="h-6 w-6 text-brand-teal/70" />
|
||||
</div>
|
||||
<p className="font-semibold text-sm">No active voting stages</p>
|
||||
@@ -734,7 +734,7 @@ async function JuryDashboardContent() {
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="rounded-lg bg-brand-teal/10 p-1.5 dark:bg-brand-teal/20">
|
||||
<div className="rounded-lg bg-brand-teal/10 p-1.5">
|
||||
<BarChart3 className="h-4 w-4 text-brand-teal" />
|
||||
</div>
|
||||
<CardTitle className="text-lg">Round Summary</CardTitle>
|
||||
@@ -750,7 +750,7 @@ async function JuryDashboardContent() {
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="font-medium truncate">{round.name}</span>
|
||||
<div className="flex items-baseline gap-1 shrink-0 ml-2">
|
||||
<span className="font-bold tabular-nums text-brand-blue dark:text-brand-teal">{pct}%</span>
|
||||
<span className="font-bold tabular-nums text-brand-blue">{pct}%</span>
|
||||
<span className="text-xs text-muted-foreground">({done}/{total})</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -852,7 +852,7 @@ export default async function JuryDashboardPage() {
|
||||
<div className="space-y-4">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue dark:text-foreground">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||||
{getGreeting()}, {session?.user?.name || 'Juror'}
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-0.5">
|
||||
|
||||
@@ -596,7 +596,7 @@ function MilestonesSection({
|
||||
<div
|
||||
key={milestone.id}
|
||||
className={`flex items-start gap-3 p-3 rounded-lg border transition-all duration-200 hover:-translate-y-0.5 hover:shadow-sm ${
|
||||
isCompleted ? 'bg-green-50/50 border-green-200 dark:bg-green-950/20 dark:border-green-900' : ''
|
||||
isCompleted ? 'bg-green-50/50 border-green-200' : ''
|
||||
}`}
|
||||
>
|
||||
<Checkbox
|
||||
|
||||
@@ -280,7 +280,7 @@ function FinalistConfirmContent({ token }: { token: string }) {
|
||||
Your project <strong>{data.project.title}</strong> is a finalist for the Monaco Ocean
|
||||
Protection Challenge grand finale.
|
||||
</p>
|
||||
<div className="bg-background border-amber-300 mt-3 rounded-md border-l-4 p-3 dark:border-amber-700">
|
||||
<div className="bg-background border-amber-300 mt-3 rounded-md border-l-4 p-3">
|
||||
<p className="text-sm">
|
||||
<strong>Confirm by {formatDeadline(deadline)}.</strong>
|
||||
</p>
|
||||
|
||||
@@ -437,7 +437,7 @@ export function AssignmentPreviewSheet({
|
||||
{mode === 'ai' && !aiResult && !isAIGenerating && (
|
||||
<Card className="border-dashed">
|
||||
<CardContent className="flex flex-col items-center justify-center py-8 gap-3">
|
||||
<div className="h-12 w-12 rounded-full bg-violet-100 dark:bg-violet-950 flex items-center justify-center">
|
||||
<div className="h-12 w-12 rounded-full bg-violet-100 flex items-center justify-center">
|
||||
<Sparkles className="h-6 w-6 text-violet-600" />
|
||||
</div>
|
||||
<div className="text-center space-y-1">
|
||||
@@ -463,7 +463,7 @@ export function AssignmentPreviewSheet({
|
||||
{isLoading ? (
|
||||
<div className="space-y-3">
|
||||
{mode === 'ai' && (
|
||||
<Card className="border-violet-200 bg-violet-50/50 dark:bg-violet-950/20">
|
||||
<Card className="border-violet-200 bg-violet-50/50">
|
||||
<CardContent className="flex items-center gap-3 py-4">
|
||||
<div className="relative">
|
||||
<div className="h-6 w-6 rounded-full border-2 border-violet-300 border-t-violet-600 animate-spin" />
|
||||
@@ -567,13 +567,13 @@ export function AssignmentPreviewSheet({
|
||||
|
||||
{/* ── Warnings ── */}
|
||||
{preview.warnings && preview.warnings.length > 0 && (
|
||||
<Card className="border-amber-300 bg-amber-50/50 dark:bg-amber-950/20">
|
||||
<Card className="border-amber-300 bg-amber-50/50">
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertTriangle className="h-4 w-4 text-amber-600 mt-0.5 shrink-0" />
|
||||
<div className="space-y-1">
|
||||
{preview.warnings.map((w: string, idx: number) => (
|
||||
<p key={idx} className="text-xs text-amber-800 dark:text-amber-200">
|
||||
<p key={idx} className="text-xs text-amber-800">
|
||||
{w}
|
||||
</p>
|
||||
))}
|
||||
|
||||
@@ -259,7 +259,7 @@ export function AwardShortlist({
|
||||
}
|
||||
</p>
|
||||
{eligibilityMode === 'SEPARATE_POOL' && !hasAwardRounds && (
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-800 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-300">
|
||||
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-amber-800">
|
||||
<AlertTriangle className="h-4 w-4 mt-0.5 shrink-0" />
|
||||
<p className="text-sm">
|
||||
No award rounds have been created yet. Projects will be confirmed but <strong>not routed</strong> to an evaluation track. Create rounds on the award page first.
|
||||
|
||||
@@ -328,13 +328,13 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
<div className="space-y-6">
|
||||
{/* Grace Period Banner */}
|
||||
{summary.isGracePeriodActive && (
|
||||
<Card className="border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/20">
|
||||
<Card className="border-amber-200 bg-amber-50">
|
||||
<CardContent className="flex items-center justify-between py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Clock className="h-5 w-5 text-amber-600" />
|
||||
<div>
|
||||
<p className="font-medium text-amber-800 dark:text-amber-200">Grace Period Active</p>
|
||||
<p className="text-sm text-amber-600 dark:text-amber-400">
|
||||
<p className="font-medium text-amber-800">Grace Period Active</p>
|
||||
<p className="text-sm text-amber-600">
|
||||
Applicants can still submit until{' '}
|
||||
{summary.gracePeriodEndsAt
|
||||
? new Date(summary.gracePeriodEndsAt).toLocaleString()
|
||||
@@ -358,12 +358,12 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
|
||||
{/* Finalized Banner */}
|
||||
{summary.isFinalized && (
|
||||
<Card className="border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/20">
|
||||
<Card className="border-green-200 bg-green-50">
|
||||
<CardContent className="flex items-center gap-3 py-4">
|
||||
<CheckCircle2 className="h-5 w-5 text-green-600" />
|
||||
<div>
|
||||
<p className="font-medium text-green-800 dark:text-green-200">Round Finalized</p>
|
||||
<p className="text-sm text-green-600 dark:text-green-400">
|
||||
<p className="font-medium text-green-800">Round Finalized</p>
|
||||
<p className="text-sm text-green-600">
|
||||
Finalized on{' '}
|
||||
{summary.finalizedAt
|
||||
? new Date(summary.finalizedAt).toLocaleString()
|
||||
@@ -376,13 +376,13 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
|
||||
{/* Needs Processing Banner */}
|
||||
{!summary.isFinalized && !summary.isGracePeriodActive && summary.projects.length > 0 && summary.projects.every((p) => !p.proposedOutcome) && (
|
||||
<Card className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/20">
|
||||
<Card className="border-blue-200 bg-blue-50">
|
||||
<CardContent className="flex items-center justify-between py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertTriangle className="h-5 w-5 text-blue-600" />
|
||||
<div>
|
||||
<p className="font-medium text-blue-800 dark:text-blue-200">Projects Need Processing</p>
|
||||
<p className="text-sm text-blue-600 dark:text-blue-400">
|
||||
<p className="font-medium text-blue-800">Projects Need Processing</p>
|
||||
<p className="text-sm text-blue-600">
|
||||
{summary.projects.length} project{summary.projects.length !== 1 ? 's' : ''} in this round have no proposed outcome.
|
||||
Click "Process" to auto-assign outcomes based on round type and project activity.
|
||||
</p>
|
||||
|
||||
@@ -124,7 +124,7 @@ export function MentoringRoundOverview({ roundId }: Props) {
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
{assignedPct}% of round{' '}
|
||||
{stats.awaitingAssignment > 0 && (
|
||||
<span className="text-amber-700 dark:text-amber-400">
|
||||
<span className="text-amber-700">
|
||||
· {stats.awaitingAssignment} awaiting
|
||||
</span>
|
||||
)}
|
||||
@@ -189,7 +189,7 @@ export function MentoringRoundOverview({ roundId }: Props) {
|
||||
|
||||
<Card
|
||||
className={`md:col-span-2 xl:col-span-4 ${
|
||||
pendingCount > 0 ? 'border-amber-300 dark:border-amber-700' : ''
|
||||
pendingCount > 0 ? 'border-amber-300' : ''
|
||||
}`}
|
||||
>
|
||||
<CardContent className="flex items-center justify-between py-4">
|
||||
|
||||
@@ -290,8 +290,8 @@ export function ProjectStatesTable({ competitionId, roundId, roundStatus, compet
|
||||
<div className="space-y-4">
|
||||
{/* Finalization hint for closed rounds */}
|
||||
{(roundStatus === 'ROUND_CLOSED' || roundStatus === 'ROUND_ARCHIVED') && (
|
||||
<div className="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/20 px-4 py-3 text-sm">
|
||||
<span className="text-blue-700 dark:text-blue-300">
|
||||
<div className="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-4 py-3 text-sm">
|
||||
<span className="text-blue-700">
|
||||
This round is closed. Use the <strong>Finalization</strong> tab to review proposed outcomes and confirm advancement.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -699,7 +699,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
This may take a minute. You can continue working — results will appear automatically.
|
||||
</p>
|
||||
</div>
|
||||
<div className="h-2 w-48 rounded-full bg-blue-100 dark:bg-blue-900 overflow-hidden">
|
||||
<div className="h-2 w-48 rounded-full bg-blue-100 overflow-hidden">
|
||||
<div className="h-full w-full rounded-full bg-blue-500 animate-pulse" />
|
||||
</div>
|
||||
</>
|
||||
@@ -962,18 +962,18 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
|
||||
{/* Ranking in-progress banner */}
|
||||
{rankingInProgress && (
|
||||
<Card className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
|
||||
<Card className="border-blue-200 bg-blue-50">
|
||||
<CardContent className="flex items-center gap-3 py-4">
|
||||
<Loader2 className="h-5 w-5 animate-spin text-blue-600 dark:text-blue-400 flex-shrink-0" />
|
||||
<Loader2 className="h-5 w-5 animate-spin text-blue-600 flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-blue-900 dark:text-blue-200">
|
||||
<p className="text-sm font-medium text-blue-900">
|
||||
Ranking in progress…
|
||||
</p>
|
||||
<p className="text-xs text-blue-700 dark:text-blue-400">
|
||||
<p className="text-xs text-blue-700">
|
||||
This may take a minute. You can continue working — results will appear automatically.
|
||||
</p>
|
||||
</div>
|
||||
<div className="h-1.5 w-32 rounded-full bg-blue-200 dark:bg-blue-800 overflow-hidden flex-shrink-0">
|
||||
<div className="h-1.5 w-32 rounded-full bg-blue-200 overflow-hidden flex-shrink-0">
|
||||
<div className="h-full w-full rounded-full bg-blue-500 animate-pulse" />
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -1097,7 +1097,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className={isAdvancing
|
||||
? 'rounded-lg bg-emerald-50 border-l-4 border-emerald-400 dark:bg-emerald-950/20 dark:border-emerald-600'
|
||||
? 'rounded-lg bg-emerald-50 border-l-4 border-emerald-400'
|
||||
: ''}
|
||||
>
|
||||
<SortableProjectRow
|
||||
@@ -1120,7 +1120,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
{isCutoffRow && (
|
||||
<div className="flex items-center gap-2 py-1">
|
||||
<div className="flex-1 border-t-2 border-dashed border-emerald-400/60" />
|
||||
<span className="text-xs font-medium text-emerald-600 dark:text-emerald-400 whitespace-nowrap">
|
||||
<span className="text-xs font-medium text-emerald-600 whitespace-nowrap">
|
||||
Advancement cutoff — {isThresholdMode ? `Score ≥ ${threshold}` : `Top ${advanceCount}`}
|
||||
</span>
|
||||
<div className="flex-1 border-t-2 border-dashed border-emerald-400/60" />
|
||||
|
||||
@@ -33,19 +33,19 @@ const severityConfig = {
|
||||
critical: {
|
||||
icon: AlertTriangle,
|
||||
iconClass: 'text-red-600',
|
||||
bgClass: 'bg-red-50 dark:bg-red-950/30',
|
||||
bgClass: 'bg-red-50',
|
||||
borderClass: 'border-l-red-500',
|
||||
},
|
||||
warning: {
|
||||
icon: AlertCircle,
|
||||
iconClass: 'text-amber-600',
|
||||
bgClass: 'bg-amber-50 dark:bg-amber-950/30',
|
||||
bgClass: 'bg-amber-50',
|
||||
borderClass: 'border-l-amber-500',
|
||||
},
|
||||
info: {
|
||||
icon: Info,
|
||||
iconClass: 'text-blue-600',
|
||||
bgClass: 'bg-blue-50 dark:bg-blue-950/30',
|
||||
bgClass: 'bg-blue-50',
|
||||
borderClass: 'border-l-blue-500',
|
||||
},
|
||||
}
|
||||
@@ -54,8 +54,8 @@ export function SmartActions({ actions }: SmartActionsProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center gap-3 space-y-0 pb-4">
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-amber-100 dark:bg-amber-900/40">
|
||||
<Zap className="h-5 w-5 text-amber-600 dark:text-amber-400" />
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-amber-100">
|
||||
<Zap className="h-5 w-5 text-amber-600" />
|
||||
</div>
|
||||
<CardTitle className="flex-1">Action Required</CardTitle>
|
||||
{actions.length > 0 && (
|
||||
@@ -65,8 +65,8 @@ export function SmartActions({ actions }: SmartActionsProps) {
|
||||
<CardContent>
|
||||
{actions.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-center">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-900/40">
|
||||
<CheckCircle2 className="h-6 w-6 text-emerald-600 dark:text-emerald-400" />
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-emerald-100">
|
||||
<CheckCircle2 className="h-6 w-6 text-emerald-600" />
|
||||
</div>
|
||||
<p className="mt-3 text-sm font-medium text-muted-foreground">
|
||||
All caught up!
|
||||
|
||||
@@ -207,7 +207,7 @@ export function EvaluationFormFields({
|
||||
className={cn(
|
||||
'flex-1 h-12 rounded-lg border-2 flex items-center justify-center text-sm font-medium transition-all',
|
||||
currentValue === true
|
||||
? 'border-emerald-500 bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-400'
|
||||
? 'border-emerald-500 bg-emerald-50 text-emerald-700'
|
||||
: 'border-border hover:border-emerald-300 hover:bg-emerald-50/50',
|
||||
isReadOnly && 'opacity-60 cursor-default',
|
||||
)}
|
||||
@@ -222,7 +222,7 @@ export function EvaluationFormFields({
|
||||
className={cn(
|
||||
'flex-1 h-12 rounded-lg border-2 flex items-center justify-center text-sm font-medium transition-all',
|
||||
currentValue === false
|
||||
? 'border-red-500 bg-red-50 text-red-700 dark:bg-red-950/40 dark:text-red-400'
|
||||
? 'border-red-500 bg-red-50 text-red-700'
|
||||
: 'border-border hover:border-red-300 hover:bg-red-50/50',
|
||||
isReadOnly && 'opacity-60 cursor-default',
|
||||
)}
|
||||
|
||||
@@ -83,10 +83,10 @@ export function EvaluationFormWithCOI({
|
||||
<CardContent className="flex items-center gap-3 py-6">
|
||||
<ShieldAlert className="h-6 w-6 text-amber-600 shrink-0" />
|
||||
<div>
|
||||
<p className="font-medium text-amber-800 dark:text-amber-200">
|
||||
<p className="font-medium text-amber-800">
|
||||
Conflict of Interest Declared
|
||||
</p>
|
||||
<p className="text-sm text-amber-700 dark:text-amber-300 mt-1">
|
||||
<p className="text-sm text-amber-700 mt-1">
|
||||
You declared a conflict of interest for this project. An admin will
|
||||
review your declaration. You cannot evaluate this project while the
|
||||
conflict is under review.
|
||||
|
||||
@@ -62,7 +62,7 @@ export function JuryPreferencesBanner() {
|
||||
if (isLoading || unconfirmed.length === 0) return null
|
||||
|
||||
return (
|
||||
<Card className="border-amber-300 bg-amber-50/50 dark:border-amber-800 dark:bg-amber-950/20">
|
||||
<Card className="border-amber-300 bg-amber-50/50">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Scale className="h-5 w-5 text-amber-600" />
|
||||
|
||||
@@ -9,10 +9,10 @@ import { Radio, Users, Trophy, Eye, EyeOff } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const SESSION_STATUS_CONFIG: Record<string, { label: string; color: string; bg: string; pulse?: boolean }> = {
|
||||
NOT_STARTED: { label: 'Not Started', color: 'text-slate-500', bg: 'bg-slate-100 dark:bg-slate-800' },
|
||||
IN_PROGRESS: { label: 'In Progress', color: 'text-emerald-600', bg: 'bg-emerald-50 dark:bg-emerald-900/20', pulse: true },
|
||||
PAUSED: { label: 'Paused', color: 'text-amber-600', bg: 'bg-amber-50 dark:bg-amber-900/20' },
|
||||
COMPLETED: { label: 'Completed', color: 'text-blue-600', bg: 'bg-blue-50 dark:bg-blue-900/20' },
|
||||
NOT_STARTED: { label: 'Not Started', color: 'text-slate-500', bg: 'bg-slate-100' },
|
||||
IN_PROGRESS: { label: 'In Progress', color: 'text-emerald-600', bg: 'bg-emerald-50', pulse: true },
|
||||
PAUSED: { label: 'Paused', color: 'text-amber-600', bg: 'bg-amber-50' },
|
||||
COMPLETED: { label: 'Completed', color: 'text-blue-600', bg: 'bg-blue-50' },
|
||||
}
|
||||
|
||||
export function LiveFinalPanel({ roundId }: { roundId: string }) {
|
||||
|
||||
@@ -52,7 +52,7 @@ export function PreviousRoundSection({ currentRoundId }: { currentRoundId: strin
|
||||
{!collapsed && (
|
||||
<CardContent className="space-y-4">
|
||||
{/* Headline Stat */}
|
||||
<div className="flex items-center gap-3 rounded-lg bg-rose-50 dark:bg-rose-950/20 p-4">
|
||||
<div className="flex items-center gap-3 rounded-lg bg-rose-50 p-4">
|
||||
<ArrowDown className="h-6 w-6 text-rose-500 shrink-0" />
|
||||
<div>
|
||||
<p className="text-lg font-semibold">
|
||||
@@ -85,7 +85,7 @@ export function PreviousRoundSection({ currentRoundId }: { currentRoundId: strin
|
||||
</div>
|
||||
<div className="relative h-2.5 rounded-full bg-muted overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-y-0 left-0 rounded-full bg-slate-300 dark:bg-slate-600 transition-all"
|
||||
className="absolute inset-y-0 left-0 rounded-full bg-slate-300 transition-all"
|
||||
style={{ width: `${prevPct}%` }}
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -37,9 +37,9 @@ type OutcomeFilter = 'ALL' | 'PASSED' | 'FILTERED_OUT' | 'FLAGGED'
|
||||
|
||||
function outcomeTextColor(outcome: string): string {
|
||||
switch (outcome) {
|
||||
case 'PASSED': return 'text-emerald-700 dark:text-emerald-400'
|
||||
case 'FILTERED_OUT': return 'text-rose-700 dark:text-rose-400'
|
||||
case 'FLAGGED': return 'text-amber-700 dark:text-amber-400'
|
||||
case 'PASSED': return 'text-emerald-700'
|
||||
case 'FILTERED_OUT': return 'text-rose-700'
|
||||
case 'FLAGGED': return 'text-amber-700'
|
||||
default: return 'text-primary'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,8 +339,8 @@ export function StorageSettingsForm({ settings }: StorageSettingsFormProps) {
|
||||
)}
|
||||
|
||||
{storageProvider === 'local' && (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-900 dark:bg-amber-950">
|
||||
<p className="text-sm text-amber-800 dark:text-amber-200">
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4">
|
||||
<p className="text-sm text-amber-800">
|
||||
<strong>Warning:</strong> Local storage is not recommended for production deployments
|
||||
with multiple servers, as files will only be accessible from the server that uploaded them.
|
||||
</p>
|
||||
|
||||
@@ -62,9 +62,9 @@ function getUrgency(totalMs: number): Urgency {
|
||||
|
||||
const urgencyStyles: Record<Urgency, string> = {
|
||||
expired: 'text-muted-foreground bg-muted',
|
||||
critical: 'text-red-700 bg-red-50 border-red-200 dark:text-red-400 dark:bg-red-950/50 dark:border-red-900',
|
||||
warning: 'text-amber-700 bg-amber-50 border-amber-200 dark:text-amber-400 dark:bg-amber-950/50 dark:border-amber-900',
|
||||
normal: 'text-green-700 bg-green-50 border-green-200 dark:text-green-400 dark:bg-green-950/50 dark:border-green-900',
|
||||
critical: 'text-red-700 bg-red-50 border-red-200',
|
||||
warning: 'text-amber-700 bg-amber-50 border-amber-200',
|
||||
normal: 'text-green-700 bg-green-50 border-green-200',
|
||||
}
|
||||
|
||||
export function CountdownTimer({ deadline, label, className }: CountdownTimerProps) {
|
||||
|
||||
@@ -19,18 +19,18 @@ import { useState } from 'react'
|
||||
|
||||
const statusConfig: Record<string, { bg: string; text: string; dot: string }> = {
|
||||
DRAFT: {
|
||||
bg: 'bg-amber-50 dark:bg-amber-950/50',
|
||||
text: 'text-amber-700 dark:text-amber-400',
|
||||
bg: 'bg-amber-50',
|
||||
text: 'text-amber-700',
|
||||
dot: 'bg-amber-500',
|
||||
},
|
||||
ACTIVE: {
|
||||
bg: 'bg-emerald-50 dark:bg-emerald-950/50',
|
||||
text: 'text-emerald-700 dark:text-emerald-400',
|
||||
bg: 'bg-emerald-50',
|
||||
text: 'text-emerald-700',
|
||||
dot: 'bg-emerald-500',
|
||||
},
|
||||
ARCHIVED: {
|
||||
bg: 'bg-slate-100 dark:bg-slate-800/50',
|
||||
text: 'text-slate-600 dark:text-slate-400',
|
||||
bg: 'bg-slate-100',
|
||||
text: 'text-slate-600',
|
||||
dot: 'bg-slate-400',
|
||||
},
|
||||
}
|
||||
@@ -95,10 +95,10 @@ export function EditionSelector() {
|
||||
|
||||
{/* Text */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold text-slate-900 dark:text-slate-100">
|
||||
<p className="truncate text-sm font-semibold text-slate-900">
|
||||
{currentEdition ? currentEdition.year : 'Select'}
|
||||
</p>
|
||||
<p className="truncate text-xs text-slate-500 dark:text-slate-400">
|
||||
<p className="truncate text-xs text-slate-500">
|
||||
{currentEdition?.status === 'ACTIVE' ? 'Current Edition' : currentEdition?.status?.toLowerCase()}
|
||||
</p>
|
||||
</div>
|
||||
@@ -136,7 +136,7 @@ export function EditionSelector() {
|
||||
}}
|
||||
className={cn(
|
||||
'group/item flex items-center gap-3 rounded-lg px-2.5 py-2.5 cursor-pointer transition-colors',
|
||||
isSelected ? 'bg-slate-100 dark:bg-slate-800' : 'hover:bg-slate-50 dark:hover:bg-slate-800/50'
|
||||
isSelected ? 'bg-slate-100' : 'hover:bg-slate-50'
|
||||
)}
|
||||
>
|
||||
{/* Year badge in dropdown */}
|
||||
@@ -144,19 +144,19 @@ export function EditionSelector() {
|
||||
'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg font-bold text-sm transition-colors',
|
||||
isSelected
|
||||
? 'bg-brand-blue text-white'
|
||||
: 'bg-slate-200 text-slate-600 dark:bg-slate-700 dark:text-slate-300'
|
||||
: 'bg-slate-200 text-slate-600'
|
||||
)}>
|
||||
{String(edition.year).slice(-2)}
|
||||
</div>
|
||||
|
||||
{/* Edition info */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-semibold text-slate-900 dark:text-slate-100">
|
||||
<p className="truncate text-sm font-semibold text-slate-900">
|
||||
{edition.year}
|
||||
</p>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className={cn('h-1.5 w-1.5 rounded-full', editionStatus.dot)} />
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 capitalize">
|
||||
<span className="text-xs text-slate-500 capitalize">
|
||||
{edition.status.toLowerCase()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -827,9 +827,9 @@ function RequirementChecklist({ roundId, files }: { roundId: string; files: Proj
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg border p-2.5 text-sm',
|
||||
isFulfilled
|
||||
? 'border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950'
|
||||
? 'border-green-200 bg-green-50'
|
||||
: req.isRequired
|
||||
? 'border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950'
|
||||
? 'border-red-200 bg-red-50'
|
||||
: 'border-muted'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -108,20 +108,20 @@ const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
// Priority styles
|
||||
const PRIORITY_STYLES = {
|
||||
low: {
|
||||
iconBg: 'bg-slate-100 dark:bg-slate-800',
|
||||
iconBg: 'bg-slate-100',
|
||||
iconColor: 'text-slate-500',
|
||||
},
|
||||
normal: {
|
||||
iconBg: 'bg-blue-100 dark:bg-blue-900/30',
|
||||
iconColor: 'text-blue-600 dark:text-blue-400',
|
||||
iconBg: 'bg-blue-100',
|
||||
iconColor: 'text-blue-600',
|
||||
},
|
||||
high: {
|
||||
iconBg: 'bg-amber-100 dark:bg-amber-900/30',
|
||||
iconColor: 'text-amber-600 dark:text-amber-400',
|
||||
iconBg: 'bg-amber-100',
|
||||
iconColor: 'text-amber-600',
|
||||
},
|
||||
urgent: {
|
||||
iconBg: 'bg-red-100 dark:bg-red-900/30',
|
||||
iconColor: 'text-red-600 dark:text-red-400',
|
||||
iconBg: 'bg-red-100',
|
||||
iconColor: 'text-red-600',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ function NotificationItem({
|
||||
data-notification-id={notification.id}
|
||||
className={cn(
|
||||
'flex gap-3 p-3 hover:bg-muted/50 transition-colors cursor-pointer',
|
||||
!notification.isRead && 'bg-blue-50/50 dark:bg-blue-950/20'
|
||||
!notification.isRead && 'bg-blue-50/50'
|
||||
)}
|
||||
onClick={onRead}
|
||||
>
|
||||
|
||||
@@ -263,9 +263,9 @@ export function RequirementUploadSlot({
|
||||
|
||||
const isFulfilled = !!existingFile
|
||||
const statusColor = isFulfilled
|
||||
? 'border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950'
|
||||
? 'border-green-200 bg-green-50'
|
||||
: requirement.isRequired
|
||||
? 'border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950'
|
||||
? 'border-red-200 bg-red-50'
|
||||
: 'border-muted'
|
||||
|
||||
// Build accept string for file input
|
||||
|
||||
@@ -4,39 +4,39 @@ import { cn } from '@/lib/utils'
|
||||
const STATUS_STYLES: Record<string, { variant: BadgeProps['variant']; className?: string }> = {
|
||||
// Round statuses
|
||||
DRAFT: { variant: 'secondary' },
|
||||
ACTIVE: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200 dark:text-blue-400' },
|
||||
EVALUATION: { variant: 'default', className: 'bg-violet-500/10 text-violet-700 border-violet-200 dark:text-violet-400' },
|
||||
ACTIVE: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200' },
|
||||
EVALUATION: { variant: 'default', className: 'bg-violet-500/10 text-violet-700 border-violet-200' },
|
||||
CLOSED: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200' },
|
||||
ROUND_DRAFT: { variant: 'secondary' },
|
||||
ROUND_ACTIVE: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200 dark:text-blue-400' },
|
||||
ROUND_ACTIVE: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200' },
|
||||
ROUND_CLOSED: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200' },
|
||||
ROUND_ARCHIVED: { variant: 'secondary', className: 'bg-slate-400/10 text-slate-400 border-slate-200' },
|
||||
|
||||
// Project statuses
|
||||
SUBMITTED: { variant: 'secondary', className: 'bg-indigo-500/10 text-indigo-700 border-indigo-200 dark:text-indigo-400' },
|
||||
ELIGIBLE: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200 dark:text-emerald-400' },
|
||||
ASSIGNED: { variant: 'default', className: 'bg-violet-500/10 text-violet-700 border-violet-200 dark:text-violet-400' },
|
||||
UNDER_REVIEW: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200 dark:text-blue-400' },
|
||||
SHORTLISTED: { variant: 'default', className: 'bg-amber-500/10 text-amber-700 border-amber-200 dark:text-amber-400' },
|
||||
SEMIFINALIST: { variant: 'default', className: 'bg-amber-500/10 text-amber-700 border-amber-200 dark:text-amber-400' },
|
||||
FINALIST: { variant: 'default', className: 'bg-orange-500/10 text-orange-700 border-orange-200 dark:text-orange-400' },
|
||||
WINNER: { variant: 'default', className: 'bg-yellow-500/10 text-yellow-800 border-yellow-300 dark:text-yellow-400' },
|
||||
SUBMITTED: { variant: 'secondary', className: 'bg-indigo-500/10 text-indigo-700 border-indigo-200' },
|
||||
ELIGIBLE: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200' },
|
||||
ASSIGNED: { variant: 'default', className: 'bg-violet-500/10 text-violet-700 border-violet-200' },
|
||||
UNDER_REVIEW: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200' },
|
||||
SHORTLISTED: { variant: 'default', className: 'bg-amber-500/10 text-amber-700 border-amber-200' },
|
||||
SEMIFINALIST: { variant: 'default', className: 'bg-amber-500/10 text-amber-700 border-amber-200' },
|
||||
FINALIST: { variant: 'default', className: 'bg-orange-500/10 text-orange-700 border-orange-200' },
|
||||
WINNER: { variant: 'default', className: 'bg-yellow-500/10 text-yellow-800 border-yellow-300' },
|
||||
REJECTED: { variant: 'destructive' },
|
||||
WITHDRAWN: { variant: 'secondary' },
|
||||
|
||||
// Observer-derived statuses
|
||||
NOT_REVIEWED: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200 dark:text-slate-400' },
|
||||
REVIEWED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200 dark:text-emerald-400' },
|
||||
NOT_REVIEWED: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200' },
|
||||
REVIEWED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200' },
|
||||
|
||||
// Round state statuses
|
||||
PENDING: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200 dark:text-slate-400' },
|
||||
IN_PROGRESS: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200 dark:text-blue-400' },
|
||||
COMPLETED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200 dark:text-emerald-400' },
|
||||
PASSED: { variant: 'default', className: 'bg-green-500/10 text-green-700 border-green-200 dark:text-green-400' },
|
||||
PENDING: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-600 border-slate-200' },
|
||||
IN_PROGRESS: { variant: 'default', className: 'bg-blue-500/10 text-blue-700 border-blue-200' },
|
||||
COMPLETED: { variant: 'default', className: 'bg-emerald-500/10 text-emerald-700 border-emerald-200' },
|
||||
PASSED: { variant: 'default', className: 'bg-green-500/10 text-green-700 border-green-200' },
|
||||
|
||||
// User statuses
|
||||
NONE: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-500 border-slate-200 dark:text-slate-400' },
|
||||
INVITED: { variant: 'secondary', className: 'bg-sky-500/10 text-sky-700 border-sky-200 dark:text-sky-400' },
|
||||
NONE: { variant: 'secondary', className: 'bg-slate-500/10 text-slate-500 border-slate-200' },
|
||||
INVITED: { variant: 'secondary', className: 'bg-sky-500/10 text-sky-700 border-sky-200' },
|
||||
INACTIVE: { variant: 'secondary' },
|
||||
SUSPENDED: { variant: 'destructive' },
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ const alertVariants = cva(
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
"border-destructive/50 text-destructive [&>svg]:text-destructive",
|
||||
success:
|
||||
"border-green-500/50 text-green-700 dark:border-green-500 [&>svg]:text-green-600",
|
||||
"border-green-500/50 text-green-700 [&>svg]:text-green-600",
|
||||
warning:
|
||||
"border-yellow-500/50 text-yellow-700 dark:border-yellow-500 [&>svg]:text-yellow-600",
|
||||
"border-yellow-500/50 text-yellow-700 [&>svg]:text-yellow-600",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -15,10 +15,10 @@ const badgeVariants = cva(
|
||||
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
||||
outline: 'text-foreground',
|
||||
success:
|
||||
'border-transparent bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100',
|
||||
'border-transparent bg-green-100 text-green-800',
|
||||
warning:
|
||||
'border-transparent bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-100',
|
||||
info: 'border-transparent bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-100',
|
||||
'border-transparent bg-amber-100 text-amber-800',
|
||||
info: 'border-transparent bg-blue-100 text-blue-800',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
Reference in New Issue
Block a user