Platform review round 2: audit logging migration, nav unification, DB indexes, and UI polish

- Migrate ~41 inline audit log calls to shared logAudit() utility across all routers
- Add transaction-aware prisma parameter to logAudit() for atomic operations
- Unify jury/mentor/observer navigation into shared RoleNav component
- Add composite DB indexes (Evaluation, GracePeriod, AuditLog) for query performance
- Fix profile page: consolidate dual save buttons, proper useEffect initialization
- Enhance auth error page with MOPC branding and navigation
- Improve observer dashboard with prominent read-only badge
- Fix DI-3: fetch projects before bulk status update for accurate notifications
- Remove unused aiBoost field from smart-assignment scoring
- Add shared image-upload utility and structured logger module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-05 21:09:06 +01:00
parent 8d0979e649
commit 002a9dbfc3
34 changed files with 1688 additions and 1782 deletions

View File

@@ -1,6 +1,7 @@
import { z } from 'zod'
import { router, protectedProcedure, adminProcedure } from '../trpc'
import { getPresignedUrl } from '@/lib/minio'
import { logAudit } from '../utils/audit'
// Bucket for partner logos
export const PARTNER_BUCKET = 'mopc-partners'
@@ -174,16 +175,15 @@ export const partnerRouter = router({
})
// Audit log
await ctx.prisma.auditLog.create({
data: {
userId: ctx.user.id,
action: 'CREATE',
entityType: 'Partner',
entityId: partner.id,
detailsJson: { name: input.name, partnerType: input.partnerType },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'CREATE',
entityType: 'Partner',
entityId: partner.id,
detailsJson: { name: input.name, partnerType: input.partnerType },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return partner
@@ -218,16 +218,15 @@ export const partnerRouter = router({
})
// Audit log
await ctx.prisma.auditLog.create({
data: {
userId: ctx.user.id,
action: 'UPDATE',
entityType: 'Partner',
entityId: id,
detailsJson: data,
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'UPDATE',
entityType: 'Partner',
entityId: id,
detailsJson: data,
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return partner
@@ -244,16 +243,15 @@ export const partnerRouter = router({
})
// Audit log
await ctx.prisma.auditLog.create({
data: {
userId: ctx.user.id,
action: 'DELETE',
entityType: 'Partner',
entityId: input.id,
detailsJson: { name: partner.name },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'DELETE',
entityType: 'Partner',
entityId: input.id,
detailsJson: { name: partner.name },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return partner
@@ -308,15 +306,14 @@ export const partnerRouter = router({
)
// Audit log
await ctx.prisma.auditLog.create({
data: {
userId: ctx.user.id,
action: 'REORDER',
entityType: 'Partner',
detailsJson: { count: input.items.length },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'REORDER',
entityType: 'Partner',
detailsJson: { count: input.items.length },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return { success: true }
@@ -339,15 +336,14 @@ export const partnerRouter = router({
})
// Audit log
await ctx.prisma.auditLog.create({
data: {
userId: ctx.user.id,
action: 'BULK_UPDATE',
entityType: 'Partner',
detailsJson: { ids: input.ids, visibility: input.visibility },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
},
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'BULK_UPDATE',
entityType: 'Partner',
detailsJson: { ids: input.ids, visibility: input.visibility },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return { updated: input.ids.length }