Auto-reassign projects when juror declares conflict of interest
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s
When a juror declares COI, the system now automatically: - Finds an eligible replacement juror (not at capacity, no COI, not already assigned) - Deletes the conflicted assignment and creates a new one - Notifies the replacement juror and admins - Load-balances by picking the juror with fewest current assignments Also adds: - "Reassign (COI)" action in assignment table dropdown with COI badge indicator - Admin "Reassign to another juror" in COI review now triggers actual reassignment - Per-juror notify button is now always visible (not just on hover) - reassignCOI admin procedure for retroactive manual reassignment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { TRPCError } from '@trpc/server'
|
||||
import { router, protectedProcedure, adminProcedure, juryProcedure } from '../trpc'
|
||||
import { logAudit } from '@/server/utils/audit'
|
||||
import { notifyAdmins, NotificationTypes } from '../services/in-app-notification'
|
||||
import { reassignAfterCOI } from './assignment'
|
||||
import { sendManualReminders } from '../services/evaluation-reminders'
|
||||
import { generateSummary } from '@/server/services/ai-evaluation-summary'
|
||||
|
||||
@@ -536,7 +537,23 @@ export const evaluationRouter = router({
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return coi
|
||||
// Auto-reassign the project to another eligible juror
|
||||
let reassignment: { newJurorId: string; newJurorName: string } | null = null
|
||||
if (input.hasConflict) {
|
||||
try {
|
||||
reassignment = await reassignAfterCOI({
|
||||
assignmentId: input.assignmentId,
|
||||
auditUserId: ctx.user.id,
|
||||
auditIp: ctx.ip,
|
||||
auditUserAgent: ctx.userAgent,
|
||||
})
|
||||
} catch (err) {
|
||||
// Don't fail the COI declaration if reassignment fails
|
||||
console.error('[COI] Auto-reassignment failed:', err)
|
||||
}
|
||||
}
|
||||
|
||||
return { ...coi, reassignment }
|
||||
}),
|
||||
|
||||
/**
|
||||
@@ -599,6 +616,17 @@ export const evaluationRouter = router({
|
||||
},
|
||||
})
|
||||
|
||||
// If admin chose "reassigned", trigger actual reassignment
|
||||
let reassignment: { newJurorId: string; newJurorName: string } | null = null
|
||||
if (input.reviewAction === 'reassigned') {
|
||||
reassignment = await reassignAfterCOI({
|
||||
assignmentId: coi.assignmentId,
|
||||
auditUserId: ctx.user.id,
|
||||
auditIp: ctx.ip,
|
||||
auditUserAgent: ctx.userAgent,
|
||||
})
|
||||
}
|
||||
|
||||
// Audit log
|
||||
await logAudit({
|
||||
prisma: ctx.prisma,
|
||||
@@ -611,12 +639,13 @@ export const evaluationRouter = router({
|
||||
assignmentId: coi.assignmentId,
|
||||
userId: coi.userId,
|
||||
projectId: coi.projectId,
|
||||
reassignedTo: reassignment?.newJurorId,
|
||||
},
|
||||
ipAddress: ctx.ip,
|
||||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
return coi
|
||||
return { ...coi, reassignment }
|
||||
}),
|
||||
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user