Admin system overhaul: full round config UI, flattened navigation, juries, awards integration, evaluation rewrite
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m23s
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m23s
- Phase 1: 7 round config sub-components covering all ~65 Zod schema fields across INTAKE, FILTERING, EVALUATION, SUBMISSION, MENTORING, LIVE_FINAL, DELIBERATION - Phase 2: Replace Competitions nav with Rounds + add Juries; new /admin/rounds and /admin/rounds/[roundId] pages with tabbed detail (Config, Projects, Windows, Documents, Awards) - Phase 3: Top-level /admin/juries with list + detail pages (members table, settings panel, self-service review) - Phase 4: File requirements editor in round config; project detail per-requirement upload slots replacing generic drop zone - Phase 5: Awards edit page with source round dropdown, eligibility mode, auto-tag rules builder; round detail Awards tab; specialAward router enhanced with evaluationRoundId/eligibilityMode fields - Phase 6: Evaluation page rewrite supporting all 3 scoring modes (criteria/global/binary) with config-driven behavior; live voting UI polish - Phase 7: UI design polish across admin pages — consistent headers, cards, hover transitions, empty states, brand colors - Bulk upload page for admin project imports - File router enhanced with admin upload and submission window procedures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
108
src/components/admin/rounds/config/submission-config.tsx
Normal file
108
src/components/admin/rounds/config/submission-config.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
'use client'
|
||||
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
type SubmissionConfigProps = {
|
||||
config: Record<string, unknown>
|
||||
onChange: (config: Record<string, unknown>) => void
|
||||
}
|
||||
|
||||
const STATUSES = [
|
||||
{ value: 'PENDING', label: 'Pending', color: 'bg-gray-100 text-gray-700' },
|
||||
{ value: 'IN_PROGRESS', label: 'In Progress', color: 'bg-blue-100 text-blue-700' },
|
||||
{ value: 'PASSED', label: 'Passed', color: 'bg-emerald-100 text-emerald-700' },
|
||||
{ value: 'REJECTED', label: 'Rejected', color: 'bg-red-100 text-red-700' },
|
||||
{ value: 'COMPLETED', label: 'Completed', color: 'bg-purple-100 text-purple-700' },
|
||||
{ value: 'WITHDRAWN', label: 'Withdrawn', color: 'bg-amber-100 text-amber-700' },
|
||||
]
|
||||
|
||||
export function SubmissionConfig({ config, onChange }: SubmissionConfigProps) {
|
||||
const update = (key: string, value: unknown) => {
|
||||
onChange({ ...config, [key]: value })
|
||||
}
|
||||
|
||||
const eligible = (config.eligibleStatuses as string[]) ?? ['PASSED']
|
||||
|
||||
const toggleStatus = (status: string) => {
|
||||
const current = [...eligible]
|
||||
const idx = current.indexOf(status)
|
||||
if (idx >= 0) {
|
||||
current.splice(idx, 1)
|
||||
} else {
|
||||
current.push(status)
|
||||
}
|
||||
update('eligibleStatuses', current)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Submission Eligibility</CardTitle>
|
||||
<CardDescription>
|
||||
Which project states from the previous round are eligible to submit documents in this round
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Eligible Project Statuses</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Projects with these statuses from the previous round can submit
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{STATUSES.map((s) => (
|
||||
<Badge
|
||||
key={s.value}
|
||||
variant={eligible.includes(s.value) ? 'default' : 'outline'}
|
||||
className="cursor-pointer select-none"
|
||||
onClick={() => toggleStatus(s.value)}
|
||||
>
|
||||
{s.label}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Notifications & Locking</CardTitle>
|
||||
<CardDescription>Behavior when the submission round activates</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label htmlFor="notifyEligibleTeams">Notify Eligible Teams</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Send email notification to teams when submission window opens
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="notifyEligibleTeams"
|
||||
checked={(config.notifyEligibleTeams as boolean) ?? true}
|
||||
onCheckedChange={(v) => update('notifyEligibleTeams', v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label htmlFor="lockPreviousWindows">Lock Previous Windows</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Prevent uploads to earlier submission windows when this round activates
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="lockPreviousWindows"
|
||||
checked={(config.lockPreviousWindows as boolean) ?? true}
|
||||
onCheckedChange={(v) => update('lockPreviousWindows', v)}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user