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