From fc7a37094be70f377c0d0394a2398337ebde7848 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 18 Feb 2026 10:28:45 +0100 Subject: [PATCH] Exclude SEPARATE_POOL award projects from main pool finalization - finalizeResults now queries confirmed SEPARATE_POOL shortlist and excludes those projects from the main pool ELIGIBLE set - Finalize confirmation dialog shows breakdown: main pool vs award-routed - Finalize toast includes award-routed count - Audit log records routedToAwards count Co-Authored-By: Claude Opus 4.6 --- .../admin/round/filtering-dashboard.tsx | 8 ++++++- src/server/routers/filtering.ts | 23 +++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/admin/round/filtering-dashboard.tsx b/src/components/admin/round/filtering-dashboard.tsx index 1827e1a..afd9b6f 100644 --- a/src/components/admin/round/filtering-dashboard.tsx +++ b/src/components/admin/round/filtering-dashboard.tsx @@ -250,6 +250,7 @@ export function FilteringDashboard({ competitionId, roundId }: FilteringDashboar utils.project.list.invalidate() toast.success( `Finalized: ${data.passed} passed, ${data.filteredOut} filtered out` + + (data.routedToAwards > 0 ? `, ${data.routedToAwards} routed to award tracks` : '') + (data.advancedToStageName ? `. Next round: ${data.advancedToStageName}` : '') ) if (data.categoryWarnings.length > 0) { @@ -483,7 +484,12 @@ export function FilteringDashboard({ competitionId, roundId }: FilteringDashboar This will mark PASSED projects as eligible and FILTERED_OUT projects as rejected. {stats && ( - {stats.passed} will pass, {stats.filteredOut} will be filtered out, {stats.flagged} flagged for review. + {stats.passed - (stats.routedToAwards || 0)} will pass to main pool, {stats.filteredOut} will be filtered out{stats.flagged > 0 ? `, ${stats.flagged} flagged for review` : ''}. + {(stats.routedToAwards || 0) > 0 && ( + + {stats.routedToAwards} already routed to award tracks (excluded from main pool). + + )} )} This action can be reversed but requires manual intervention. diff --git a/src/server/routers/filtering.ts b/src/server/routers/filtering.ts index 990b105..eb264c1 100644 --- a/src/server/routers/filtering.ts +++ b/src/server/routers/filtering.ts @@ -1153,6 +1153,19 @@ export const filteringRouter = router({ const passedIds = passedResults.map((r) => r.projectId) + // Exclude projects confirmed for SEPARATE_POOL award tracks from main pool + const awardRouted = await ctx.prisma.awardEligibility.findMany({ + where: { + projectId: { in: passedIds }, + shortlisted: true, + confirmedAt: { not: null }, + award: { eligibilityMode: 'SEPARATE_POOL' }, + }, + select: { projectId: true }, + }) + const awardRoutedIds = new Set(awardRouted.map((a) => a.projectId)) + const mainPoolPassedIds = passedIds.filter((id) => !awardRoutedIds.has(id)) + const operations: Prisma.PrismaPromise[] = [] if (filteredOutIds.length > 0) { @@ -1164,10 +1177,10 @@ export const filteringRouter = router({ ) } - if (passedIds.length > 0) { + if (mainPoolPassedIds.length > 0) { operations.push( ctx.prisma.project.updateMany({ - where: { id: { in: passedIds } }, + where: { id: { in: mainPoolPassedIds } }, data: { status: 'ELIGIBLE' }, }) ) @@ -1197,7 +1210,8 @@ export const filteringRouter = router({ entityId: input.roundId, detailsJson: { action: 'FINALIZE_FILTERING', - passed: passedIds.length, + passed: mainPoolPassedIds.length, + routedToAwards: awardRoutedIds.size, filteredOut: filteredOutIds.length, demotedToFlagged: demotedIds.length, categoryTargets: input.categoryTargets || null, @@ -1207,7 +1221,8 @@ export const filteringRouter = router({ }) return { - passed: passedIds.length, + passed: mainPoolPassedIds.length, + routedToAwards: awardRoutedIds.size, filteredOut: filteredOutIds.length, demotedToFlagged: demotedIds.length, categoryCounts,