Round detail overhaul, file requirements, project management, audit log fix
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m32s

- Redesign round detail page with 6 tabs (overview, projects, filtering, assignments, config, documents)
- Add jury group assignment selector in round stats bar
- Add FileRequirementsEditor component replacing SubmissionWindowManager
- Add FilteringDashboard component for AI-powered project screening
- Add project removal from rounds (single + bulk) with cascading to subsequent rounds
- Add project add/remove UI in ProjectStatesTable with confirmation dialogs
- Fix logAudit inside $transaction pattern across all 12 router files
  (PostgreSQL aborted-transaction state caused silent operation failures)
- Fix special awards creation, deletion, status update, and winner assignment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 07:49:39 +01:00
parent f572336781
commit 7f334ed095
18 changed files with 3387 additions and 968 deletions

View File

@@ -36,23 +36,19 @@ export const competitionRouter = router({
where: { id: input.programId },
})
const competition = await ctx.prisma.$transaction(async (tx) => {
const created = await tx.competition.create({
data: input,
})
const competition = await ctx.prisma.competition.create({
data: input,
})
await logAudit({
prisma: tx,
userId: ctx.user.id,
action: 'CREATE',
entityType: 'Competition',
entityId: created.id,
detailsJson: { name: input.name, programId: input.programId },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return created
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'CREATE',
entityType: 'Competition',
entityId: competition.id,
detailsJson: { name: input.name, programId: input.programId },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return competition
@@ -178,33 +174,29 @@ export const competitionRouter = router({
}
}
const competition = await ctx.prisma.$transaction(async (tx) => {
const previous = await tx.competition.findUniqueOrThrow({ where: { id } })
const previous = await ctx.prisma.competition.findUniqueOrThrow({ where: { id } })
const updated = await tx.competition.update({
where: { id },
data,
})
const competition = await ctx.prisma.competition.update({
where: { id },
data,
})
await logAudit({
prisma: tx,
userId: ctx.user.id,
action: 'UPDATE',
entityType: 'Competition',
entityId: id,
detailsJson: {
changes: data,
previous: {
name: previous.name,
status: previous.status,
slug: previous.slug,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'UPDATE',
entityType: 'Competition',
entityId: id,
detailsJson: {
changes: data,
previous: {
name: previous.name,
status: previous.status,
slug: previous.slug,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return updated
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return competition
@@ -216,24 +208,20 @@ export const competitionRouter = router({
delete: adminProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
const competition = await ctx.prisma.$transaction(async (tx) => {
const updated = await tx.competition.update({
where: { id: input.id },
data: { status: 'ARCHIVED' },
})
const competition = await ctx.prisma.competition.update({
where: { id: input.id },
data: { status: 'ARCHIVED' },
})
await logAudit({
prisma: tx,
userId: ctx.user.id,
action: 'DELETE',
entityType: 'Competition',
entityId: input.id,
detailsJson: { action: 'archived' },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return updated
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'DELETE',
entityType: 'Competition',
entityId: input.id,
detailsJson: { action: 'archived' },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return competition