diff --git a/src/app/(admin)/admin/competitions/[competitionId]/assignments/page.tsx b/src/app/(admin)/admin/competitions/[competitionId]/assignments/page.tsx deleted file mode 100644 index b3edd71..0000000 --- a/src/app/(admin)/admin/competitions/[competitionId]/assignments/page.tsx +++ /dev/null @@ -1,206 +0,0 @@ -'use client' - -import { useState } from 'react' -import { useParams, useRouter } from 'next/navigation' -import { ArrowLeft, Loader2, PlayCircle, Zap } from 'lucide-react' -import { toast } from 'sonner' -import { trpc } from '@/lib/trpc/client' -import { Button } from '@/components/ui/button' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { Skeleton } from '@/components/ui/skeleton' -import { CoverageReport } from '@/components/admin/assignment/coverage-report' -import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' - -export default function AssignmentsDashboardPage() { - const params = useParams() - const router = useRouter() - const competitionId = params.competitionId as string - - const [selectedRoundId, setSelectedRoundId] = useState('') - const [previewSheetOpen, setPreviewSheetOpen] = useState(false) - - const aiAssignmentMutation = trpc.roundAssignment.aiPreview.useMutation({ - onSuccess: () => { - toast.success('AI assignments ready!', { - action: { label: 'Review', onClick: () => setPreviewSheetOpen(true) }, - duration: 10000, - }) - }, - onError: (err) => toast.error(`AI generation failed: ${err.message}`), - }) - - const { data: competition, isLoading: isLoadingCompetition } = trpc.competition.getById.useQuery({ - id: competitionId, - }) - - const { data: selectedRound } = trpc.round.getById.useQuery( - { id: selectedRoundId }, - { enabled: !!selectedRoundId } - ) - - const requiredReviews = (selectedRound?.configJson as Record)?.requiredReviewsPerProject as number || 3 - - const { data: unassignedQueue, isLoading: isLoadingQueue } = - trpc.roundAssignment.unassignedQueue.useQuery( - { roundId: selectedRoundId, requiredReviews }, - { enabled: !!selectedRoundId } - ) - - const rounds = competition?.rounds || [] - const currentRound = rounds.find((r) => r.id === selectedRoundId) - - if (isLoadingCompetition) { - return ( -
- - -
- ) - } - - if (!competition) { - return ( -
-

Competition not found

-
- ) - } - - return ( -
- - -
-
-

Assignment Dashboard

-

Manage jury assignments for rounds

-
-
- - - - Select Round - Choose a round to view and manage assignments - - - - - - - {selectedRoundId && ( -
-
- - {aiAssignmentMutation.data && ( - - )} -
- - - - Coverage Report - Unassigned Queue - - - - - - - - - - Unassigned Projects - - Projects with fewer than {requiredReviews} assignments - - - - {isLoadingQueue ? ( -
- {[1, 2, 3].map((i) => ( - - ))} -
- ) : unassignedQueue && unassignedQueue.length > 0 ? ( -
- {unassignedQueue.map((project: any) => ( -
-
-

{project.title}

-

- {project.competitionCategory || 'No category'} -

-
-
- {project.assignmentCount || 0} / {requiredReviews} assignments -
-
- ))} -
- ) : ( -

- All projects have sufficient assignments -

- )} -
-
-
-
- - aiAssignmentMutation.mutate({ roundId: selectedRoundId, requiredReviews })} - onResetAI={() => aiAssignmentMutation.reset()} - /> -
- )} -
- ) -} diff --git a/src/app/(admin)/admin/competitions/[competitionId]/awards/[awardId]/page.tsx b/src/app/(admin)/admin/competitions/[competitionId]/awards/[awardId]/page.tsx deleted file mode 100644 index 63c1543..0000000 --- a/src/app/(admin)/admin/competitions/[competitionId]/awards/[awardId]/page.tsx +++ /dev/null @@ -1,154 +0,0 @@ -'use client'; - -import { use } from 'react'; -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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Badge } from '@/components/ui/badge'; -import { ArrowLeft } from 'lucide-react'; -import type { Route } from 'next'; - -export default function AwardDetailPage({ - params: paramsPromise -}: { - params: Promise<{ competitionId: string; awardId: string }>; -}) { - const params = use(paramsPromise); - const router = useRouter(); - const { data: award, isLoading } = trpc.specialAward.get.useQuery({ - id: params.awardId - }); - - if (isLoading) { - return ( -
-
- -
-

Loading...

-
-
-
- ); - } - - if (!award) { - return ( -
-
- -
-

Award Not Found

-
-
-
- ); - } - - return ( -
-
- -
-

{award.name}

-

{award.description || 'No description'}

-
-
- - - - Overview - Eligible Projects - Winners - - - - - - Award Information - Configuration and settings - - -
-
-

Scoring Mode

- - {award.scoringMode} - -
-
-

AI Eligibility

- - {award.useAiEligibility ? 'Enabled' : 'Disabled'} - -
-
-

Status

- - {award.status} - -
-
-

Program

-

{award.program?.name}

-
-
-
-
-
- - - - - Eligible Projects - - Projects that qualify for this award ({award?.eligibleCount || 0}) - - - -

- {award?.eligibleCount || 0} eligible projects -

-
-
-
- - - - - Award Winners - Selected winners for this award - - - {award?.winnerProject ? ( -
-
-

{award.winnerProject.title}

-

{award.winnerProject.teamName}

-
- Winner -
- ) : ( -

No winner selected yet

- )} -
-
-
-
-
- ); -} diff --git a/src/app/(admin)/admin/competitions/[competitionId]/awards/new/page.tsx b/src/app/(admin)/admin/competitions/[competitionId]/awards/new/page.tsx deleted file mode 100644 index 9112d0f..0000000 --- a/src/app/(admin)/admin/competitions/[competitionId]/awards/new/page.tsx +++ /dev/null @@ -1,199 +0,0 @@ -'use client'; - -import { use, useState } from 'react'; -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 { Checkbox } from '@/components/ui/checkbox'; -import { ArrowLeft } from 'lucide-react'; -import { toast } from 'sonner'; -import type { Route } from 'next'; - -export default function NewAwardPage({ params: paramsPromise }: { params: Promise<{ competitionId: string }> }) { - const params = use(paramsPromise); - const router = useRouter(); - const utils = trpc.useUtils(); - - const [formData, setFormData] = useState({ - name: '', - description: '', - criteriaText: '', - useAiEligibility: false, - scoringMode: 'PICK_WINNER' as 'PICK_WINNER' | 'RANKED' | 'SCORED', - maxRankedPicks: '3', - }); - - const { data: competition } = trpc.competition.getById.useQuery({ - id: params.competitionId - }); - - const { data: juryGroups } = trpc.juryGroup.list.useQuery({ - competitionId: params.competitionId - }); - - const createMutation = trpc.specialAward.create.useMutation({ - onSuccess: () => { - utils.specialAward.list.invalidate(); - toast.success('Award created successfully'); - router.push(`/admin/competitions/${params.competitionId}/awards` as Route); - }, - onError: (err) => { - toast.error(err.message); - } - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!formData.name.trim()) { - toast.error('Award name is required'); - return; - } - - if (!competition?.programId) { - toast.error('Competition data not loaded'); - return; - } - - createMutation.mutate({ - programId: competition.programId, - competitionId: params.competitionId, - name: formData.name.trim(), - description: formData.description.trim() || undefined, - criteriaText: formData.criteriaText.trim() || undefined, - scoringMode: formData.scoringMode, - useAiEligibility: formData.useAiEligibility, - maxRankedPicks: formData.scoringMode === 'RANKED' ? parseInt(formData.maxRankedPicks) : undefined, - }); - }; - - return ( -
-
- -
-

Create Special Award

-

Define a new award for this competition

-
-
- -
- - - Award Details - Configure the award properties and eligibility - - -
- - setFormData({ ...formData, name: e.target.value })} - placeholder="e.g., Best Innovation Award" - required - /> -
- -
- -