feat: show submission round file requirements on project edit page
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m40s

Adds a new tRPC procedure `round.getSubmissionRoundForProgram` that
fetches the most recent SUBMISSION round for a given program, then
displays any `requiredDocuments` from its configJson as labeled info
cards above the general file upload section on the project edit page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 14:47:42 +01:00
parent 9b3a9f6cbf
commit 68aa393559
2 changed files with 73 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
'use client'
import { Suspense, use, useState, useEffect, useCallback } from 'react'
import { Suspense, use, useState, useEffect, useCallback, useMemo } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useForm } from 'react-hook-form'
@@ -57,6 +57,7 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Separator } from '@/components/ui/separator'
import { FileUpload } from '@/components/shared/file-upload'
import { ProjectLogo } from '@/components/shared/project-logo'
import { LogoUpload } from '@/components/shared/logo-upload'
@@ -133,6 +134,27 @@ function EditProjectContent({ projectId }: { projectId: string }) {
// Fetch existing tags for suggestions
const { data: existingTags } = trpc.project.getTags.useQuery({})
// Fetch submission round config to show required documents
const programId = project?.programId
const { data: submissionRound } = trpc.round.getSubmissionRoundForProgram.useQuery(
{ programId: programId! },
{ enabled: !!programId }
)
const submissionRoundConfig = useMemo(() => {
if (!submissionRound?.configJson) return null
const config = submissionRound.configJson as Record<string, unknown>
const docs = config.requiredDocuments as
| Array<{ name: string; required?: boolean; description?: string }>
| null
| undefined
if (!docs || docs.length === 0) return null
return {
roundName: submissionRound.name,
requiredDocuments: docs,
}
}, [submissionRound])
// Mutations
const utils = trpc.useUtils()
const updateProject = trpc.project.update.useMutation({
@@ -702,6 +724,33 @@ function EditProjectContent({ projectId }: { projectId: string }) {
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{submissionRoundConfig && (
<div className="mb-4 space-y-3">
<div>
<p className="text-sm font-medium">Required Documents</p>
<p className="text-xs text-muted-foreground">
From {submissionRoundConfig.roundName}
</p>
</div>
<div className="space-y-2">
{submissionRoundConfig.requiredDocuments.map((doc, i) => (
<div key={i} className="flex items-center justify-between rounded-md border border-dashed p-3">
<div>
<p className="text-sm font-medium">{doc.name}</p>
{doc.description && (
<p className="text-xs text-muted-foreground">{doc.description}</p>
)}
{doc.required && (
<Badge variant="outline" className="mt-1 text-xs">Required</Badge>
)}
</div>
</div>
))}
</div>
<Separator />
</div>
)}
{files && files.length > 0 ? (
<Table>
<TableHeader>

View File

@@ -861,4 +861,26 @@ export const roundRouter = router({
orderBy: { sortOrder: 'asc' },
})
}),
/**
* Get the most recent SUBMISSION round config for a program.
* Used on the project edit page to show required document slots.
*/
getSubmissionRoundForProgram: adminProcedure
.input(z.object({ programId: z.string() }))
.query(async ({ ctx, input }) => {
const round = await ctx.prisma.round.findFirst({
where: {
roundType: 'SUBMISSION',
competition: { programId: input.programId },
},
select: {
id: true,
name: true,
configJson: true,
},
orderBy: { sortOrder: 'desc' },
})
return round ?? null
}),
})