From 8c5f4998a82932579fc26bb0d2e259f35444f6ff Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 2 Mar 2026 21:20:11 +0100 Subject: [PATCH] fix: sort ranking display by avgGlobalScore, not compositeScore The display was sorted by compositeScore (which factors in pass rate and z-score normalization) but the cutoff line and displayed values use avgGlobalScore. This caused projects with high averages but low pass rates (e.g. 1/2 Yes) to appear below the cutoff even when their average exceeded the threshold. Now sorts by avgGlobalScore (the visible metric) with compositeScore as tiebreaker. Also adds a green left border to advancing projects for clearer threshold highlighting. Co-Authored-By: Claude Opus 4.6 --- .../admin/round/ranking-dashboard.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/admin/round/ranking-dashboard.tsx b/src/components/admin/round/ranking-dashboard.tsx index bb317e3..ef1ec29 100644 --- a/src/components/admin/round/ranking-dashboard.tsx +++ b/src/components/admin/round/ranking-dashboard.tsx @@ -425,10 +425,14 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran const dedupedStartup = dedup(startup) const dedupedConcept = dedup(concept) - // Sort by compositeScore descending — ensures display order matches the formula - // regardless of how the snapshot was originally generated (AI vs formula). - dedupedStartup.sort((a, b) => b.compositeScore - a.compositeScore) - dedupedConcept.sort((a, b) => b.compositeScore - a.compositeScore) + // Sort by avgGlobalScore descending (the metric displayed to the admin), with + // compositeScore as tiebreaker. This ensures the visible ordering matches the + // numbers on screen AND the threshold cutoff line lands correctly (it checks + // avgGlobalScore, so the list must be sorted by that same metric). + dedupedStartup.sort((a, b) => + (b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore) + dedupedConcept.sort((a, b) => + (b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore) // Track original order for override detection (same effect = always in sync) const order: Record = {} @@ -944,7 +948,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran let cutoffIndex = -1 if (isThresholdMode) { // Find the FIRST project that does NOT meet the threshold — cutoff goes before it. - // Works correctly when list is sorted by compositeScore (which correlates with avgGlobalScore). + // Works correctly because localOrder is sorted by avgGlobalScore (the same metric). const firstFailIdx = localOrder[category].findIndex((id) => { const e = rankingMap.get(id) return (e?.avgGlobalScore ?? 0) < threshold @@ -993,7 +997,9 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} - className={isAdvancing ? 'rounded-lg bg-emerald-50 dark:bg-emerald-950/20' : ''} + className={isAdvancing + ? 'rounded-lg bg-emerald-50 border-l-4 border-emerald-400 dark:bg-emerald-950/20 dark:border-emerald-600' + : ''} >