Admin platform audit: fix bugs, harden backend, add auto-refresh, clean dead code
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s

Phase 1 — Critical bugs:
- Fix deliberation participant selection (wire jury group query)
- Fix reports "By Round" tab (inline content instead of 404 route)
- Fix messages "Sent History" (add message.sent procedure, wire tab)
- Add missing fields to competition award form (criteriaText, maxRankedPicks)
- Wire LiveControlPanel buttons (cursor, voting, scores)
- Fix ResultLockControls empty snapshot (fetch actual data before lock)
- Fix SubmissionWindowManager losing fields on edit

Phase 2 — Backend fixes:
- Remove write-in-query from specialAward.get
- Fix award eligibility job overwriting manual shortlist overrides
- Fix filtering startJob deleting all prior results (defer cleanup to post-success)
- Tighten access control: protectedProcedure → adminProcedure on 8 procedures
- Add audit logging to deliberation mutations
- Add FINALIST/SEMIFINALIST delete guard on project.delete/bulkDelete

Phase 3 — Auto-refresh:
- Add refetchInterval to 15+ admin pages/components (10s–30s)
- Fix AI job polling: derive speed from job status for all viewers

Phase 4 — Dead code cleanup:
- Delete unused command-palette, pdf-report, admin-page-transition
- Remove dead subItems sidebar code, unused GripVertical import
- Replace redundant isGenerating state with mutation.isPending
- Add Role column to jury members table
- Remove misleading manual mentor assignment stub

Phase 5 — UX improvements:
- Fix rounds page single-competition assumption (add selector)
- Remove raw UUID fallback in deliberation config
- Fix programs page "Stage" → "Round" terminology

Phase 6 — Backend hardening:
- Complete logAudit calls (add prisma, ipAddress, userAgent)
- Batch analytics queries (fix N+1 in getCrossRoundComparison, getYearOverYear)
- Batch user.bulkCreate writes (assignments, jury memberships, intents)
- Remove any casts from deliberation service (typed PrismaClient + TransactionClient)
- Fix stale DeliberationStatus enum values blocking build

40 files changed, 1010 insertions(+), 612 deletions(-)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 08:20:13 +01:00
parent aa1bf564ee
commit 1308c3ba87
40 changed files with 1011 additions and 613 deletions

View File

@@ -1,6 +1,7 @@
import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, adminProcedure, juryProcedure, protectedProcedure } from '../trpc'
import { logAudit } from '@/server/utils/audit'
import {
createSession,
openVoting,
@@ -48,7 +49,26 @@ export const deliberationRouter = router({
})
)
.mutation(async ({ ctx, input }) => {
return createSession(input, ctx.prisma)
const session = await createSession(input, ctx.prisma)
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'CREATE',
entityType: 'DeliberationSession',
entityId: session.id,
detailsJson: {
competitionId: input.competitionId,
roundId: input.roundId,
category: input.category,
mode: input.mode,
participantCount: input.participantUserIds.length,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return session
}),
/**
@@ -98,7 +118,26 @@ export const deliberationRouter = router({
})
)
.mutation(async ({ ctx, input }) => {
return submitVote(input, ctx.prisma)
const vote = await submitVote(input, ctx.prisma)
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'CREATE',
entityType: 'DeliberationVote',
entityId: input.sessionId,
detailsJson: {
sessionId: input.sessionId,
projectId: input.projectId,
rank: input.rank,
isWinnerPick: input.isWinnerPick,
runoffRound: input.runoffRound,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return vote
}),
/**
@@ -264,7 +303,7 @@ export const deliberationRouter = router({
})
)
.mutation(async ({ ctx, input }) => {
return updateParticipantStatus(
const result = await updateParticipantStatus(
input.sessionId,
input.userId,
input.status,
@@ -272,5 +311,23 @@ export const deliberationRouter = router({
ctx.user.id,
ctx.prisma,
)
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'UPDATE',
entityType: 'DeliberationParticipant',
entityId: input.sessionId,
detailsJson: {
sessionId: input.sessionId,
targetUserId: input.userId,
status: input.status,
replacedById: input.replacedById,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return result
}),
})