fix: sort ranking display by avgGlobalScore, not compositeScore
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m33s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -425,10 +425,14 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
const dedupedStartup = dedup(startup)
|
const dedupedStartup = dedup(startup)
|
||||||
const dedupedConcept = dedup(concept)
|
const dedupedConcept = dedup(concept)
|
||||||
|
|
||||||
// Sort by compositeScore descending — ensures display order matches the formula
|
// Sort by avgGlobalScore descending (the metric displayed to the admin), with
|
||||||
// regardless of how the snapshot was originally generated (AI vs formula).
|
// compositeScore as tiebreaker. This ensures the visible ordering matches the
|
||||||
dedupedStartup.sort((a, b) => b.compositeScore - a.compositeScore)
|
// numbers on screen AND the threshold cutoff line lands correctly (it checks
|
||||||
dedupedConcept.sort((a, b) => b.compositeScore - a.compositeScore)
|
// 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)
|
// Track original order for override detection (same effect = always in sync)
|
||||||
const order: Record<string, number> = {}
|
const order: Record<string, number> = {}
|
||||||
@@ -944,7 +948,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
let cutoffIndex = -1
|
let cutoffIndex = -1
|
||||||
if (isThresholdMode) {
|
if (isThresholdMode) {
|
||||||
// Find the FIRST project that does NOT meet the threshold — cutoff goes before it.
|
// 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 firstFailIdx = localOrder[category].findIndex((id) => {
|
||||||
const e = rankingMap.get(id)
|
const e = rankingMap.get(id)
|
||||||
return (e?.avgGlobalScore ?? 0) < threshold
|
return (e?.avgGlobalScore ?? 0) < threshold
|
||||||
@@ -993,7 +997,9 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: -20 }}
|
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'
|
||||||
|
: ''}
|
||||||
>
|
>
|
||||||
<SortableProjectRow
|
<SortableProjectRow
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
|
|||||||
Reference in New Issue
Block a user