feat: show submission round file requirements on project edit page
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m40s
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:
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'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 Link from 'next/link'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
@@ -57,6 +57,7 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table'
|
} from '@/components/ui/table'
|
||||||
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { FileUpload } from '@/components/shared/file-upload'
|
import { FileUpload } from '@/components/shared/file-upload'
|
||||||
import { ProjectLogo } from '@/components/shared/project-logo'
|
import { ProjectLogo } from '@/components/shared/project-logo'
|
||||||
import { LogoUpload } from '@/components/shared/logo-upload'
|
import { LogoUpload } from '@/components/shared/logo-upload'
|
||||||
@@ -133,6 +134,27 @@ function EditProjectContent({ projectId }: { projectId: string }) {
|
|||||||
// Fetch existing tags for suggestions
|
// Fetch existing tags for suggestions
|
||||||
const { data: existingTags } = trpc.project.getTags.useQuery({})
|
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
|
// Mutations
|
||||||
const utils = trpc.useUtils()
|
const utils = trpc.useUtils()
|
||||||
const updateProject = trpc.project.update.useMutation({
|
const updateProject = trpc.project.update.useMutation({
|
||||||
@@ -702,7 +724,34 @@ function EditProjectContent({ projectId }: { projectId: string }) {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{files && files.length > 0 ? (
|
{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>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|||||||
@@ -861,4 +861,26 @@ export const roundRouter = router({
|
|||||||
orderBy: { sortOrder: 'asc' },
|
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
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user