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
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:
@@ -9,7 +9,7 @@ import { sendStyledNotificationEmail, sendTeamMemberInviteEmail } from '@/lib/em
|
||||
import { logAudit } from '@/server/utils/audit'
|
||||
import { createNotification, notifyProjectMentors, NotificationTypes } from '../services/in-app-notification'
|
||||
import { checkRequirementsAndTransition, triggerInProgressOnActivity, transitionProject, isTerminalState } from '../services/round-engine'
|
||||
import { getFinalDocumentStatusForProject } from '../services/final-documents'
|
||||
import { getFinalDocumentStatusForProject, finalistUploadsEnabled } from '../services/final-documents'
|
||||
import { EvaluationConfigSchema, MentoringConfigSchema } from '@/types/competition-configs'
|
||||
import type { PrismaClient, Prisma, RoundType } from '@prisma/client'
|
||||
|
||||
@@ -337,12 +337,15 @@ export const applicantRouter = router({
|
||||
if (input.roundId) {
|
||||
const round = await ctx.prisma.round.findUnique({
|
||||
where: { id: input.roundId },
|
||||
select: { name: true, status: true, roundType: true, finalizedAt: true },
|
||||
select: { name: true, status: true, roundType: true, finalizedAt: true, configJson: true },
|
||||
})
|
||||
if (round) {
|
||||
const uploadable =
|
||||
round.status === 'ROUND_ACTIVE' ||
|
||||
(round.roundType === 'LIVE_FINAL' && round.status === 'ROUND_DRAFT' && !round.finalizedAt)
|
||||
round.roundType === 'LIVE_FINAL'
|
||||
? !round.finalizedAt &&
|
||||
(round.status === 'ROUND_DRAFT' || round.status === 'ROUND_ACTIVE') &&
|
||||
finalistUploadsEnabled(round.configJson)
|
||||
: round.status === 'ROUND_ACTIVE'
|
||||
if (!uploadable) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
@@ -568,12 +571,15 @@ export const applicantRouter = router({
|
||||
if (file.roundId) {
|
||||
const round = await ctx.prisma.round.findUnique({
|
||||
where: { id: file.roundId },
|
||||
select: { status: true, roundType: true, finalizedAt: true },
|
||||
select: { status: true, roundType: true, finalizedAt: true, configJson: true },
|
||||
})
|
||||
if (round) {
|
||||
const modifiable =
|
||||
round.status === 'ROUND_ACTIVE' ||
|
||||
(round.roundType === 'LIVE_FINAL' && round.status === 'ROUND_DRAFT' && !round.finalizedAt)
|
||||
round.roundType === 'LIVE_FINAL'
|
||||
? !round.finalizedAt &&
|
||||
(round.status === 'ROUND_DRAFT' || round.status === 'ROUND_ACTIVE') &&
|
||||
finalistUploadsEnabled(round.configJson)
|
||||
: round.status === 'ROUND_ACTIVE'
|
||||
if (!modifiable) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
@@ -1472,6 +1478,7 @@ export const applicantRouter = router({
|
||||
slug: true,
|
||||
roundType: true,
|
||||
windowCloseAt: true,
|
||||
configJson: true,
|
||||
specialAwardId: true,
|
||||
specialAward: { select: { name: true } },
|
||||
},
|
||||
@@ -1490,8 +1497,9 @@ export const applicantRouter = router({
|
||||
|
||||
openRounds = allActiveRounds
|
||||
.filter((r) => {
|
||||
// LIVE_FINAL (grand-final documents) only shows to enrolled finalists.
|
||||
if (r.roundType === 'LIVE_FINAL' && !projectRoundIds.has(r.id)) return false
|
||||
// LIVE_FINAL (grand-final documents) only shows to enrolled finalists,
|
||||
// and only when the admin has enabled revised uploads.
|
||||
if (r.roundType === 'LIVE_FINAL' && (!projectRoundIds.has(r.id) || !finalistUploadsEnabled(r.configJson))) return false
|
||||
// Award round project isn't in → hide
|
||||
if (r.specialAwardId && !projectRoundIds.has(r.id)) return false
|
||||
// Main round when project is in award track and has no state in this round → hide
|
||||
|
||||
Reference in New Issue
Block a user