feat(final-docs): judges see all teams' prior-round files; revised uploads behind admin toggle
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m47s

The finals jury needs the teams' EXISTING submissions (pitch deck, exec summary,
business plan, videos from prior rounds) — which all 9 teams already have. So:

- listFinalistDocumentsForReview now returns ALL of each finalist team's files
  across every round (labeled by doc type + round; finale uploads flagged
  'Revised for finals'), with presigned URLs. NOT gated — judges always see.
- Revised re-uploads are now an admin toggle (Round.configJson.allowFinalistRevisedUploads,
  default OFF): gates the banner/panel (getFinalDocumentStatusForProject), the
  upload guard (getUploadUrl/deleteFile), the documents-page round, and the
  reminders (manual + cron). When off, teams aren't prompted/able to upload.
- finalist.get/setRevisedUploadSetting + a Switch on the admin finale overview.
- judge review component rewritten to a per-team labeled file list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-09 17:19:09 +02:00
parent f8f2d77e3b
commit 8a4184d20f
7 changed files with 241 additions and 122 deletions

View File

@@ -1690,4 +1690,37 @@ export const finalistRouter = router({
if (!allowed) throw new TRPCError({ code: 'FORBIDDEN', message: 'You do not have access to the finalist documents review.' })
return listFinalistDocumentsForReview(ctx.prisma, input.programId)
}),
/** Read whether finalists may upload revised grand-final documents (admin toggle). */
getRevisedUploadSetting: adminProcedure
.input(z.object({ roundId: z.string() }))
.query(async ({ ctx, input }) => {
const round = await ctx.prisma.round.findUnique({ where: { id: input.roundId }, select: { configJson: true } })
const cfg = (round?.configJson ?? {}) as { allowFinalistRevisedUploads?: boolean }
return { enabled: !!cfg.allowFinalistRevisedUploads }
}),
/** Toggle whether finalists may upload revised grand-final documents (admin setting on the LIVE_FINAL round). */
setRevisedUploadSetting: adminProcedure
.input(z.object({ roundId: z.string(), enabled: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const round = await ctx.prisma.round.findUnique({ where: { id: input.roundId }, select: { configJson: true, roundType: true } })
if (!round || round.roundType !== 'LIVE_FINAL') {
throw new TRPCError({ code: 'BAD_REQUEST', message: 'Not a grand-final round' })
}
const cfg = (round.configJson ?? {}) as Record<string, unknown>
await ctx.prisma.round.update({
where: { id: input.roundId },
data: { configJson: { ...cfg, allowFinalistRevisedUploads: input.enabled } },
})
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'FINALIST_REVISED_UPLOADS_TOGGLED',
entityType: 'Round',
entityId: input.roundId,
detailsJson: { enabled: input.enabled },
})
return { ok: true, enabled: input.enabled }
}),
})