Jury inline doc preview, download fix, category tags, admin eval reset
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m55s

- Replace MultiWindowDocViewer with FileViewer for inline previews (PDF/image/video/Office)
- Fix cross-origin download using fetch+blob instead of <a download>
- Show Startup/Business Concept badge on jury project detail + evaluate pages
- Add admin resetEvaluation procedure with audit logging
- Add dropdown menu on admin assignment rows with Reset Evaluation + Delete
- Make file action buttons responsive on mobile (separate row below file info)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-18 14:03:38 +01:00
parent 9ce56f13fd
commit 0f6473c999
6 changed files with 318 additions and 248 deletions

View File

@@ -230,6 +230,67 @@ export const evaluationRouter = router({
return updated
}),
/**
* Reset (erase) an evaluation so a juror can start over (admin only)
* Deletes the evaluation record and resets the assignment's isCompleted flag.
*/
resetEvaluation: adminProcedure
.input(
z.object({
assignmentId: z.string(),
})
)
.mutation(async ({ ctx, input }) => {
const assignment = await ctx.prisma.assignment.findUniqueOrThrow({
where: { id: input.assignmentId },
include: {
evaluation: true,
user: { select: { id: true, name: true, email: true } },
project: { select: { id: true, title: true } },
},
})
if (!assignment.evaluation) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'No evaluation found for this assignment',
})
}
// Delete the evaluation and reset assignment completion in a transaction
await ctx.prisma.$transaction([
ctx.prisma.evaluation.delete({
where: { id: assignment.evaluation.id },
}),
ctx.prisma.assignment.update({
where: { id: input.assignmentId },
data: { isCompleted: false },
}),
])
// Audit log
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'EVALUATION_RESET',
entityType: 'Evaluation',
entityId: assignment.evaluation.id,
detailsJson: {
assignmentId: input.assignmentId,
jurorId: assignment.user.id,
jurorName: assignment.user.name || assignment.user.email,
projectId: assignment.project.id,
projectTitle: assignment.project.title,
previousStatus: assignment.evaluation.status,
previousGlobalScore: assignment.evaluation.globalScore,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return { success: true }
}),
/**
* Get aggregated stats for a project (admin only)
*/