import { Suspense } from 'react' import Link from 'next/link' import { notFound, redirect } from 'next/navigation' import { auth } from '@/lib/auth' import { prisma } from '@/lib/prisma' export const dynamic = 'force-dynamic' import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { EvaluationForm } from '@/components/forms/evaluation-form' import { ArrowLeft, AlertCircle, Clock, FileText, Users } from 'lucide-react' import { isFuture, isPast } from 'date-fns' interface PageProps { params: Promise<{ id: string }> } // Define the criterion type for the evaluation form interface Criterion { id: string label: string description?: string scale: number weight?: number required?: boolean } async function EvaluateContent({ projectId }: { projectId: string }) { const session = await auth() const userId = session?.user?.id if (!userId) { redirect('/login') } // Check if user is assigned to this project const assignment = await prisma.assignment.findFirst({ where: { projectId, userId, }, include: { evaluation: { include: { form: true, }, }, round: { include: { program: { select: { name: true }, }, evaluationForms: { where: { isActive: true }, take: 1, }, }, }, }, }) // Get project details const project = await prisma.project.findUnique({ where: { id: projectId }, include: { files: true, }, }) if (!project) { notFound() } if (!assignment) { return (

Access Denied

You are not assigned to evaluate this project

) } const round = assignment.round const now = new Date() // Check voting window const isVotingOpen = round.status === 'ACTIVE' && round.votingStartAt && round.votingEndAt && new Date(round.votingStartAt) <= now && new Date(round.votingEndAt) >= now const isVotingUpcoming = round.votingStartAt && isFuture(new Date(round.votingStartAt)) const isVotingClosed = round.votingEndAt && isPast(new Date(round.votingEndAt)) // Check for grace period const gracePeriod = await prisma.gracePeriod.findFirst({ where: { roundId: round.id, userId, OR: [{ projectId: null }, { projectId }], extendedUntil: { gte: now }, }, }) const hasGracePeriod = !!gracePeriod const effectiveVotingOpen = isVotingOpen || hasGracePeriod // Check if already submitted const evaluation = assignment.evaluation const isSubmitted = evaluation?.status === 'SUBMITTED' || evaluation?.status === 'LOCKED' if (isSubmitted) { redirect(`/jury/projects/${projectId}/evaluation`) } // Get evaluation form criteria const evaluationForm = round.evaluationForms[0] if (!evaluationForm) { return (

Evaluation Form Not Available

The evaluation criteria for this round have not been configured yet. Please check back later.

) } // Parse criteria from JSON const criteria: Criterion[] = (evaluationForm.criteriaJson as unknown as Criterion[]) || [] // Handle voting not open if (!effectiveVotingOpen) { return (

{isVotingUpcoming ? 'Voting Not Yet Open' : 'Voting Period Closed'}

{isVotingUpcoming ? 'The voting window for this round has not started yet.' : 'The voting window for this round has ended.'}

) } return (
{/* Back button and project summary */}
{round.program.name} / {round.name}

Evaluate: {project.title}

{project.teamName && (
{project.teamName}
)}
{/* Quick file access */} {project.files.length > 0 && (
{project.files.length} file{project.files.length !== 1 ? 's' : ''}
)}
{/* Grace period notice */} {hasGracePeriod && gracePeriod && (
You have a grace period extension until{' '} {new Date(gracePeriod.extendedUntil).toLocaleString()}
)} {/* Evaluation Form */} | null, globalScore: evaluation.globalScore, binaryDecision: evaluation.binaryDecision, feedbackText: evaluation.feedbackText, status: evaluation.status, } : undefined } isVotingOpen={effectiveVotingOpen} deadline={round.votingEndAt} />
) } function EvaluateSkeleton() { return (
{[1, 2, 3].map((i) => (
))}
) } export default async function EvaluatePage({ params }: PageProps) { const { id } = await params return ( }> ) }