From 761a203063679153ac12984e629f04fd6319b783 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 2 Mar 2026 20:43:49 +0100 Subject: [PATCH] fix: sort ranking display by compositeScore, fix threshold cutoff line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sort deduped ranking entries by compositeScore descending during localOrder init — ensures correct display order for both formula and old AI snapshots - Fix threshold cutoff: scan forward to find first non-qualifying project instead of backward scan that left non-qualifying projects above the line Co-Authored-By: Claude Opus 4.6 --- .../admin/round/ranking-dashboard.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/admin/round/ranking-dashboard.tsx b/src/components/admin/round/ranking-dashboard.tsx index c835f0c..bb317e3 100644 --- a/src/components/admin/round/ranking-dashboard.tsx +++ b/src/components/admin/round/ranking-dashboard.tsx @@ -425,7 +425,12 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran const dedupedStartup = dedup(startup) const dedupedConcept = dedup(concept) - // Track original AI order for override detection (same effect = always in sync) + // 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) + + // Track original order for override detection (same effect = always in sync) const order: Record = {} dedupedStartup.forEach((r, i) => { order[r.projectId] = i + 1 }) dedupedConcept.forEach((r, i) => { order[r.projectId] = i + 1 }) @@ -938,10 +943,17 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran let cutoffIndex = -1 if (isThresholdMode) { - // Find the LAST index in the list where the project meets the threshold - for (let i = localOrder[category].length - 1; i >= 0; i--) { - const e = rankingMap.get(localOrder[category][i]) - if ((e?.avgGlobalScore ?? 0) >= threshold) { cutoffIndex = i; break } + // 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). + const firstFailIdx = localOrder[category].findIndex((id) => { + const e = rankingMap.get(id) + return (e?.avgGlobalScore ?? 0) < threshold + }) + if (firstFailIdx === -1) { + // All meet threshold — cutoff after the last one + cutoffIndex = localOrder[category].length - 1 + } else if (firstFailIdx > 0) { + cutoffIndex = firstFailIdx - 1 } } else if (advanceCount > 0) { cutoffIndex = advanceCount - 1