Round detail overhaul, file requirements, project management, audit log fix
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m32s
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:
@@ -72,24 +72,25 @@ export const liveRouter = router({
|
||||
},
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_SESSION_STARTED',
|
||||
entityType: 'Round',
|
||||
entityId: input.roundId,
|
||||
detailsJson: {
|
||||
sessionId: created.sessionId,
|
||||
projectCount: input.projectOrder.length,
|
||||
firstProjectId: input.projectOrder[0],
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return created
|
||||
})
|
||||
|
||||
// Audit outside transaction so failures don't roll back the session start
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_SESSION_STARTED',
|
||||
entityType: 'Round',
|
||||
entityId: input.roundId,
|
||||
detailsJson: {
|
||||
sessionId: cursor.sessionId,
|
||||
projectCount: input.projectOrder.length,
|
||||
firstProjectId: input.projectOrder[0],
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return cursor
|
||||
}),
|
||||
|
||||
@@ -123,30 +124,27 @@ export const liveRouter = router({
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await ctx.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: {
|
||||
activeProjectId: input.projectId,
|
||||
activeOrderIndex: index,
|
||||
},
|
||||
})
|
||||
const updated = await ctx.prisma.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: {
|
||||
activeProjectId: input.projectId,
|
||||
activeOrderIndex: index,
|
||||
},
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_ACTIVE_PROJECT_SET',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
projectId: input.projectId,
|
||||
orderIndex: index,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return result
|
||||
// Audit outside transaction so failures don't roll back the project set
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_ACTIVE_PROJECT_SET',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
projectId: input.projectId,
|
||||
orderIndex: index,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updated
|
||||
@@ -182,31 +180,28 @@ export const liveRouter = router({
|
||||
|
||||
const targetProjectId = projectOrder[input.index]
|
||||
|
||||
const updated = await ctx.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: {
|
||||
activeProjectId: targetProjectId,
|
||||
activeOrderIndex: input.index,
|
||||
},
|
||||
})
|
||||
const updated = await ctx.prisma.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: {
|
||||
activeProjectId: targetProjectId,
|
||||
activeOrderIndex: input.index,
|
||||
},
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_JUMP',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
fromIndex: cursor.activeOrderIndex,
|
||||
toIndex: input.index,
|
||||
projectId: targetProjectId,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return result
|
||||
// Audit outside transaction so failures don't roll back the jump
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_JUMP',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
fromIndex: cursor.activeOrderIndex,
|
||||
toIndex: input.index,
|
||||
projectId: targetProjectId,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updated
|
||||
@@ -255,22 +250,23 @@ export const liveRouter = router({
|
||||
},
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_REORDER',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
projectCount: input.projectOrder.length,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updatedCursor
|
||||
})
|
||||
|
||||
// Audit outside transaction so failures don't roll back the reorder
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_REORDER',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: {
|
||||
projectCount: input.projectOrder.length,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updated
|
||||
}),
|
||||
|
||||
@@ -291,24 +287,21 @@ export const liveRouter = router({
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await ctx.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: { isPaused: true },
|
||||
})
|
||||
const updated = await ctx.prisma.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: { isPaused: true },
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_PAUSED',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: { activeProjectId: cursor.activeProjectId },
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return result
|
||||
// Audit outside transaction so failures don't roll back the pause
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_PAUSED',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: { activeProjectId: cursor.activeProjectId },
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updated
|
||||
@@ -331,24 +324,21 @@ export const liveRouter = router({
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await ctx.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: { isPaused: false },
|
||||
})
|
||||
const updated = await ctx.prisma.liveProgressCursor.update({
|
||||
where: { id: cursor.id },
|
||||
data: { isPaused: false },
|
||||
})
|
||||
|
||||
await logAudit({
|
||||
prisma: tx,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_RESUMED',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: { activeProjectId: cursor.activeProjectId },
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return result
|
||||
// Audit outside transaction so failures don't roll back the resume
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
userId: ctx.user.id,
|
||||
action: 'LIVE_RESUMED',
|
||||
entityType: 'LiveProgressCursor',
|
||||
entityId: cursor.id,
|
||||
detailsJson: { activeProjectId: cursor.activeProjectId },
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return updated
|
||||
|
||||
Reference in New Issue
Block a user