142 lines
4.7 KiB
TypeScript
142 lines
4.7 KiB
TypeScript
|
|
'use client'
|
||
|
|
|
||
|
|
import { trpc } from '@/lib/trpc/client'
|
||
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||
|
|
import { Badge } from '@/components/ui/badge'
|
||
|
|
import { Button } from '@/components/ui/button'
|
||
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
||
|
|
import { FilePreview } from '@/components/shared/file-viewer'
|
||
|
|
import { FileText, Download, ShieldAlert } from 'lucide-react'
|
||
|
|
|
||
|
|
export default function FinalsDocumentsPage() {
|
||
|
|
const { data: programId, isLoading: programLoading } =
|
||
|
|
trpc.competition.getActiveProgramId.useQuery()
|
||
|
|
const { data, isLoading, error } = trpc.finalist.listReviewDocuments.useQuery(
|
||
|
|
{ programId: programId! },
|
||
|
|
{ enabled: !!programId, retry: false },
|
||
|
|
)
|
||
|
|
|
||
|
|
if (error?.data?.code === 'FORBIDDEN') {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardContent className="flex flex-col items-center py-12 text-center">
|
||
|
|
<ShieldAlert className="h-10 w-10 text-muted-foreground/50 mb-3" />
|
||
|
|
<p className="font-medium">No access</p>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
This review is for the Grand-Final jury and program admins.
|
||
|
|
</p>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
// No active program resolved — nothing to review.
|
||
|
|
if (!programLoading && !programId) {
|
||
|
|
return (
|
||
|
|
<Card>
|
||
|
|
<CardContent className="flex flex-col items-center py-12 text-center">
|
||
|
|
<FileText className="h-10 w-10 text-muted-foreground/50 mb-3" />
|
||
|
|
<p className="font-medium">No active program</p>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Finalist documents will appear here once a program is active.
|
||
|
|
</p>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isLoading || !data) {
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
<Skeleton className="h-8 w-64" />
|
||
|
|
<Skeleton className="h-64" />
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
const fmt = new Intl.DateTimeFormat(undefined, {
|
||
|
|
dateStyle: 'long',
|
||
|
|
timeStyle: 'short',
|
||
|
|
})
|
||
|
|
return (
|
||
|
|
<div className="space-y-6">
|
||
|
|
<div>
|
||
|
|
<h1 className="text-2xl font-bold tracking-tight text-brand-blue">
|
||
|
|
Finalist Documents
|
||
|
|
</h1>
|
||
|
|
<p className="text-muted-foreground mt-1">
|
||
|
|
{data.submittedCount} of {data.totalCount} teams complete
|
||
|
|
{data.round.deadline
|
||
|
|
? ` · due ${fmt.format(new Date(data.round.deadline))}`
|
||
|
|
: ''}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
{data.teams.map((team) => (
|
||
|
|
<Card key={team.projectId}>
|
||
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
||
|
|
<CardTitle className="text-lg">{team.teamName}</CardTitle>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
{team.category && (
|
||
|
|
<Badge variant="secondary">{team.category}</Badge>
|
||
|
|
)}
|
||
|
|
<Badge
|
||
|
|
variant={team.submitted ? 'default' : 'outline'}
|
||
|
|
className={
|
||
|
|
team.submitted
|
||
|
|
? 'bg-emerald-50 text-emerald-700 border-emerald-200'
|
||
|
|
: ''
|
||
|
|
}
|
||
|
|
>
|
||
|
|
{team.submitted ? 'Complete' : 'Incomplete'}
|
||
|
|
</Badge>
|
||
|
|
</div>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="grid gap-4 md:grid-cols-2">
|
||
|
|
{team.documents.map((doc) => (
|
||
|
|
<div
|
||
|
|
key={doc.requirementId}
|
||
|
|
className="rounded-lg border p-3 space-y-2"
|
||
|
|
>
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="font-medium text-sm flex items-center gap-2">
|
||
|
|
<FileText className="h-4 w-4" /> {doc.requirementName}
|
||
|
|
</span>
|
||
|
|
{doc.file && (
|
||
|
|
<Button
|
||
|
|
asChild
|
||
|
|
variant="ghost"
|
||
|
|
size="sm"
|
||
|
|
className="h-7 px-2 text-xs"
|
||
|
|
>
|
||
|
|
<a
|
||
|
|
href={doc.file.url}
|
||
|
|
target="_blank"
|
||
|
|
rel="noreferrer"
|
||
|
|
>
|
||
|
|
<Download className="h-3 w-3 mr-1" /> Open
|
||
|
|
</a>
|
||
|
|
</Button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
{doc.file ? (
|
||
|
|
<FilePreview
|
||
|
|
file={{
|
||
|
|
mimeType: doc.file.mimeType,
|
||
|
|
fileName: doc.file.fileName,
|
||
|
|
}}
|
||
|
|
url={doc.file.url}
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<p className="text-sm text-muted-foreground py-4 text-center">
|
||
|
|
Not yet uploaded
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|