Fix COI audit log always saying conflict + fix boolean criteria submission
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m19s

1. COI audit log: The declareCOI mutation always logged action
   'COI_DECLARED' regardless of whether the user clicked "No Conflict"
   or "Yes, I Have a Conflict". Now uses 'COI_NO_CONFLICT' when
   hasConflict is false, showing "confirmed no conflict of interest"
   in the audit trail.

2. Evaluation submission: The requireAllCriteriaScored validation
   only accepted numeric values (typeof === 'number'), but boolean
   criteria (yes/no questions) store true/false. This caused jurors
   to get "Missing scores for criteria: criterion-xxx" errors even
   after completing all fields. Now correctly validates boolean
   criteria with typeof === 'boolean'. Also improved the error
   message to show criterion labels instead of cryptic IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-20 12:53:43 +01:00
parent d9d6a63e4a
commit bf02684736
2 changed files with 11 additions and 6 deletions

View File

@@ -237,18 +237,22 @@ export const evaluationRouter = router({
select: { criteriaJson: true },
})
if (evalForm?.criteriaJson) {
const criteria = evalForm.criteriaJson as Array<{ id: string; type?: string; required?: boolean }>
const criteria = evalForm.criteriaJson as Array<{ id: string; label?: string; type?: string; required?: boolean }>
const scorableCriteria = criteria.filter(
(c) => c.type !== 'section_header' && c.type !== 'text' && c.required !== false
)
const scores = data.criterionScoresJson as Record<string, unknown> | undefined
const missingCriteria = scorableCriteria.filter(
(c) => !scores || typeof scores[c.id] !== 'number'
)
const missingCriteria = scorableCriteria.filter((c) => {
if (!scores) return true
const val = scores[c.id]
// Boolean criteria store true/false, numeric criteria store numbers
if (c.type === 'boolean') return typeof val !== 'boolean'
return typeof val !== 'number'
})
if (missingCriteria.length > 0) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: `Missing scores for criteria: ${missingCriteria.map((c) => c.id).join(', ')}`,
message: `Missing scores for criteria: ${missingCriteria.map((c) => c.label || c.id).join(', ')}`,
})
}
}
@@ -523,7 +527,7 @@ export const evaluationRouter = router({
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'COI_DECLARED',
action: input.hasConflict ? 'COI_DECLARED' : 'COI_NO_CONFLICT',
entityType: 'ConflictOfInterest',
entityId: coi.id,
detailsJson: {