'use client' import { use, useState, useEffect } from 'react' import Link from 'next/link' import { useRouter } from 'next/navigation' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Switch } from '@/components/ui/switch' import { Skeleton } from '@/components/ui/skeleton' import { toast } from 'sonner' import { ArrowLeft, Save, Loader2 } from 'lucide-react' export default function EditAwardPage({ params, }: { params: Promise<{ id: string }> }) { const { id: awardId } = use(params) const router = useRouter() const utils = trpc.useUtils() const { data: award, isLoading } = trpc.specialAward.get.useQuery({ id: awardId }) // Rounds come from the award's included competition relation const competitionRounds = award?.competition?.rounds ?? [] const updateAward = trpc.specialAward.update.useMutation({ onSuccess: () => { utils.specialAward.get.invalidate({ id: awardId }) utils.specialAward.list.invalidate() }, }) const [name, setName] = useState('') const [description, setDescription] = useState('') const [criteriaText, setCriteriaText] = useState('') const [scoringMode, setScoringMode] = useState<'PICK_WINNER' | 'RANKED' | 'SCORED'>('PICK_WINNER') const [useAiEligibility, setUseAiEligibility] = useState(true) const [maxRankedPicks, setMaxRankedPicks] = useState('3') const [votingStartAt, setVotingStartAt] = useState('') const [votingEndAt, setVotingEndAt] = useState('') const [evaluationRoundId, setEvaluationRoundId] = useState('') const [eligibilityMode, setEligibilityMode] = useState<'STAY_IN_MAIN' | 'SEPARATE_POOL'>('STAY_IN_MAIN') const [decisionMode, setDecisionMode] = useState<'JURY_VOTE' | 'ADMIN_DECISION'>('JURY_VOTE') // Helper to format date for datetime-local input const formatDateForInput = (date: Date | string | null | undefined): string => { if (!date) return '' const d = new Date(date) // Format: YYYY-MM-DDTHH:mm return d.toISOString().slice(0, 16) } // Load existing values when award data arrives useEffect(() => { if (award) { setName(award.name) setDescription(award.description || '') setCriteriaText(award.criteriaText || '') setScoringMode(award.scoringMode as 'PICK_WINNER' | 'RANKED' | 'SCORED') setUseAiEligibility(award.useAiEligibility) setMaxRankedPicks(String(award.maxRankedPicks || 3)) setVotingStartAt(formatDateForInput(award.votingStartAt)) setVotingEndAt(formatDateForInput(award.votingEndAt)) setEvaluationRoundId(award.evaluationRoundId || '') setEligibilityMode(award.eligibilityMode as 'STAY_IN_MAIN' | 'SEPARATE_POOL') setDecisionMode((award.decisionMode as typeof decisionMode) || 'JURY_VOTE') } }, [award]) const handleSubmit = async () => { if (!name.trim()) return try { await updateAward.mutateAsync({ id: awardId, name: name.trim(), description: description.trim() || undefined, criteriaText: criteriaText.trim() || undefined, useAiEligibility, scoringMode, maxRankedPicks: scoringMode === 'RANKED' ? parseInt(maxRankedPicks) : undefined, votingStartAt: votingStartAt ? new Date(votingStartAt) : undefined, votingEndAt: votingEndAt ? new Date(votingEndAt) : undefined, evaluationRoundId: evaluationRoundId || undefined, eligibilityMode, decisionMode, }) toast.success('Award updated') router.push(`/admin/awards/${awardId}`) } catch (error) { toast.error( error instanceof Error ? error.message : 'Failed to update award' ) } } if (isLoading) { return (
Update award settings and eligibility criteria
Use AI to automatically evaluate project eligibility based on the criteria above. Turn off for awards decided by feeling or manual selection.
Projects from this round will be considered for award eligibility
Whether award-eligible projects continue in the main competition or move to a separate track
When jurors can start voting (leave empty to set when opening voting)
Deadline for juror votes