import type { PrismaClient } from '@prisma/client' export type FinalDocRequirement = { id: string name: string acceptedMimeTypes: string[] isRequired: boolean uploaded: boolean file: { id: string; fileName: string; mimeType: string; bucket: string; objectKey: string; createdAt: Date } | null } export type FinalDocumentStatus = { roundId: string roundName: string deadline: Date | null deadlinePassed: boolean requirements: FinalDocRequirement[] allRequiredUploaded: boolean } /** Resolve the program's active LIVE_FINAL round, or null. */ export async function getActiveFinaleRound(prisma: PrismaClient, programId: string) { return prisma.round.findFirst({ where: { competition: { programId }, roundType: 'LIVE_FINAL', status: 'ROUND_ACTIVE' }, orderBy: { sortOrder: 'desc' }, select: { id: true, name: true, windowCloseAt: true }, }) } /** * Per-project grand-final document status. Returns null unless the project is * enrolled (ProjectRoundState) in the program's active LIVE_FINAL round. */ export async function getFinalDocumentStatusForProject( prisma: PrismaClient, projectId: string, ): Promise { const project = await prisma.project.findUnique({ where: { id: projectId }, select: { id: true, programId: true }, }) if (!project) return null const round = await getActiveFinaleRound(prisma, project.programId) if (!round) return null const enrolled = await prisma.projectRoundState.findFirst({ where: { projectId, roundId: round.id }, select: { id: true }, }) if (!enrolled) return null const requirements = await prisma.fileRequirement.findMany({ where: { roundId: round.id }, orderBy: { sortOrder: 'asc' }, select: { id: true, name: true, acceptedMimeTypes: true, isRequired: true }, }) const files = await prisma.projectFile.findMany({ where: { projectId, roundId: round.id, requirementId: { in: requirements.map((r) => r.id) } }, orderBy: { createdAt: 'desc' }, select: { id: true, requirementId: true, fileName: true, mimeType: true, bucket: true, objectKey: true, createdAt: true }, }) const fileByReq = new Map() for (const f of files) if (f.requirementId && !fileByReq.has(f.requirementId)) fileByReq.set(f.requirementId, f) const reqStatuses: FinalDocRequirement[] = requirements.map((r) => { const f = fileByReq.get(r.id) ?? null return { id: r.id, name: r.name, acceptedMimeTypes: r.acceptedMimeTypes, isRequired: r.isRequired, uploaded: !!f, file: f ? { id: f.id, fileName: f.fileName, mimeType: f.mimeType, bucket: f.bucket, objectKey: f.objectKey, createdAt: f.createdAt } : null, } }) const required = reqStatuses.filter((r) => r.isRequired) const allRequiredUploaded = required.length > 0 && required.every((r) => r.uploaded) const deadline = round.windowCloseAt ?? null return { roundId: round.id, roundName: round.name, deadline, deadlinePassed: deadline ? new Date() > deadline : false, requirements: reqStatuses, allRequiredUploaded, } }