Fix project detail 500 error and round deletion data integrity
- Add missing migration for FileRequirement table and ProjectFile.requirementId
column (existed in Prisma schema but had no migration, causing all queries
with `include: { files: true }` to fail with column not found)
- Make projectTags query resilient with try-catch in project.get
- Reset project status to SUBMITTED when round is deleted (prevents orphaned
ASSIGNED status after ON DELETE SET NULL nullifies roundId)
- Fix round creation/update to allow requiredReviews=0 for filtering rounds
- Parse Zod validation errors in round creation error display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -327,17 +327,21 @@ export const projectRouter = router({
|
||||
},
|
||||
},
|
||||
},
|
||||
projectTags: {
|
||||
include: {
|
||||
tag: {
|
||||
select: { id: true, name: true, category: true, color: true },
|
||||
},
|
||||
},
|
||||
orderBy: { confidence: 'desc' },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Fetch project tags separately (table may not exist if migrations are pending)
|
||||
let projectTags: { id: string; projectId: string; tagId: string; confidence: number; tag: { id: string; name: string; category: string | null; color: string | null } }[] = []
|
||||
try {
|
||||
projectTags = await ctx.prisma.projectTag.findMany({
|
||||
where: { projectId: input.id },
|
||||
include: { tag: { select: { id: true, name: true, category: true, color: true } } },
|
||||
orderBy: { confidence: 'desc' },
|
||||
})
|
||||
} catch {
|
||||
// ProjectTag table may not exist yet
|
||||
}
|
||||
|
||||
// Check access for jury members
|
||||
if (ctx.user.role === 'JURY_MEMBER') {
|
||||
const assignment = await ctx.prisma.assignment.findFirst({
|
||||
@@ -381,6 +385,7 @@ export const projectRouter = router({
|
||||
|
||||
return {
|
||||
...project,
|
||||
projectTags,
|
||||
teamMembers: teamMembersWithAvatars,
|
||||
mentorAssignment: mentorWithAvatar,
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export const roundRouter = router({
|
||||
programId: z.string(),
|
||||
name: z.string().min(1).max(255),
|
||||
roundType: z.enum(['FILTERING', 'EVALUATION', 'LIVE_EVENT']).default('EVALUATION'),
|
||||
requiredReviews: z.number().int().min(1).max(10).default(3),
|
||||
requiredReviews: z.number().int().min(0).max(10).default(3),
|
||||
minAssignmentsPerJuror: z.number().int().min(1).max(50).default(5),
|
||||
maxAssignmentsPerJuror: z.number().int().min(1).max(100).default(20),
|
||||
sortOrder: z.number().int().optional(),
|
||||
@@ -208,7 +208,7 @@ export const roundRouter = router({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
slug: z.string().min(1).max(100).regex(/^[a-z0-9-]+$/).optional().nullable(),
|
||||
roundType: z.enum(['FILTERING', 'EVALUATION', 'LIVE_EVENT']).optional(),
|
||||
requiredReviews: z.number().int().min(1).max(10).optional(),
|
||||
requiredReviews: z.number().int().min(0).max(10).optional(),
|
||||
minAssignmentsPerJuror: z.number().int().min(1).max(50).optional(),
|
||||
maxAssignmentsPerJuror: z.number().int().min(1).max(100).optional(),
|
||||
submissionDeadline: z.date().optional().nullable(),
|
||||
@@ -615,6 +615,12 @@ export const roundRouter = router({
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
// Reset status for projects that will lose their roundId (ON DELETE SET NULL)
|
||||
await tx.project.updateMany({
|
||||
where: { roundId: input.id },
|
||||
data: { status: 'SUBMITTED' },
|
||||
})
|
||||
|
||||
// Delete evaluations first to avoid FK constraint on Evaluation.formId
|
||||
// (formId FK may not have CASCADE in older DB schemas)
|
||||
await tx.evaluation.deleteMany({
|
||||
|
||||
Reference in New Issue
Block a user