feat: list sort respects useBalancedRanking toggle
The two existing sort sites (initial init + threshold cutoff) now read from the local toggle. A second effect re-sorts the list when the toggle flips, but only when no manual reorder is pinned to the snapshot — persisted manual reorders always win, matching prior behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -411,12 +411,15 @@ 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 balanced (juror-corrected) score descending, falling back to raw
|
// Sort by balanced (juror-corrected) score descending when the toggle is
|
||||||
// avgGlobalScore when no balanced score is available, then compositeScore as
|
// on, otherwise by raw. compositeScore is the final tiebreaker. The
|
||||||
// a final tiebreaker. The threshold cutoff line uses the same metric so the
|
// threshold cutoff line uses the same metric so the cutoff lands in the
|
||||||
// cutoff lands in the correct spot regardless of which score type is used.
|
// right spot regardless of which score type is used.
|
||||||
const scoreFor = (projectId: string, raw: number | null | undefined) =>
|
const scoreFor = (projectId: string, raw: number | null | undefined) => {
|
||||||
evalScores.balanced[projectId]?.balancedAverage ?? raw ?? 0
|
const balanced = evalScores.balanced[projectId]?.balancedAverage
|
||||||
|
if (useBalanced && balanced != null) return balanced
|
||||||
|
return raw ?? 0
|
||||||
|
}
|
||||||
dedupedStartup.sort((a, b) =>
|
dedupedStartup.sort((a, b) =>
|
||||||
scoreFor(b.projectId, b.avgGlobalScore) - scoreFor(a.projectId, a.avgGlobalScore)
|
scoreFor(b.projectId, b.avgGlobalScore) - scoreFor(a.projectId, a.avgGlobalScore)
|
||||||
|| b.compositeScore - a.compositeScore)
|
|| b.compositeScore - a.compositeScore)
|
||||||
@@ -465,6 +468,49 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
}
|
}
|
||||||
}, [snapshot, evalScores])
|
}, [snapshot, evalScores])
|
||||||
|
|
||||||
|
// ─── Re-sort on toggle flip (after init) ─────────────────────────────────
|
||||||
|
// Only resorts when no server-side manual reorder is pinned for the snapshot;
|
||||||
|
// persisted manual reorders always win regardless of the score being used.
|
||||||
|
useEffect(() => {
|
||||||
|
if (!initialized.current || !snapshot || !evalScores) return
|
||||||
|
const reorders = (snapshot.reordersJson as Array<{
|
||||||
|
category: 'STARTUP' | 'BUSINESS_CONCEPT'
|
||||||
|
orderedProjectIds: string[]
|
||||||
|
}> | null) ?? []
|
||||||
|
const hasManualReorder =
|
||||||
|
reorders.some((r) => r.category === 'STARTUP') ||
|
||||||
|
reorders.some((r) => r.category === 'BUSINESS_CONCEPT')
|
||||||
|
if (hasManualReorder) return
|
||||||
|
const startup = (snapshot.startupRankingJson ?? []) as unknown as RankedProjectEntry[]
|
||||||
|
const concept = (snapshot.conceptRankingJson ?? []) as unknown as RankedProjectEntry[]
|
||||||
|
const dedup = (arr: RankedProjectEntry[]): RankedProjectEntry[] => {
|
||||||
|
const seen = new Set<string>()
|
||||||
|
return arr.filter((r) => {
|
||||||
|
if (seen.has(r.projectId)) return false
|
||||||
|
seen.add(r.projectId)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const scoreFor = (projectId: string, raw: number | null | undefined) => {
|
||||||
|
const balanced = evalScores.balanced[projectId]?.balancedAverage
|
||||||
|
if (useBalanced && balanced != null) return balanced
|
||||||
|
return raw ?? 0
|
||||||
|
}
|
||||||
|
const sortedStartup = dedup(startup).sort((a, b) =>
|
||||||
|
scoreFor(b.projectId, b.avgGlobalScore) - scoreFor(a.projectId, a.avgGlobalScore)
|
||||||
|
|| b.compositeScore - a.compositeScore)
|
||||||
|
const sortedConcept = dedup(concept).sort((a, b) =>
|
||||||
|
scoreFor(b.projectId, b.avgGlobalScore) - scoreFor(a.projectId, a.avgGlobalScore)
|
||||||
|
|| b.compositeScore - a.compositeScore)
|
||||||
|
setLocalOrder({
|
||||||
|
STARTUP: sortedStartup.map((r) => r.projectId),
|
||||||
|
BUSINESS_CONCEPT: sortedConcept.map((r) => r.projectId),
|
||||||
|
})
|
||||||
|
// Eslint disable: snapshot/evalScores are read but the resort should only
|
||||||
|
// run on toggle flip, not on every snapshot/scores refetch.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [useBalanced])
|
||||||
|
|
||||||
// ─── numericCriteria from eval form ─────────────────────────────────────
|
// ─── numericCriteria from eval form ─────────────────────────────────────
|
||||||
const numericCriteria = useMemo(() => {
|
const numericCriteria = useMemo(() => {
|
||||||
if (!evalForm?.criteriaJson) return []
|
if (!evalForm?.criteriaJson) return []
|
||||||
@@ -886,13 +932,15 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
: (evalConfig?.conceptAdvanceCount ?? 0))
|
: (evalConfig?.conceptAdvanceCount ?? 0))
|
||||||
const threshold = evalConfig?.advanceScoreThreshold ?? 0
|
const threshold = evalConfig?.advanceScoreThreshold ?? 0
|
||||||
|
|
||||||
// Effective ranking score = balanced (juror-corrected) average,
|
// Effective ranking score respects the per-round
|
||||||
// falling back to raw avgGlobalScore. Both the sort and the
|
// useBalancedRanking toggle. Both the sort and the threshold
|
||||||
// threshold check use this same value so the cutoff lands in
|
// check read from the same helper so the cutoff lands in the
|
||||||
// the right spot.
|
// right spot.
|
||||||
const effectiveScore = (id: string) => {
|
const effectiveScore = (id: string) => {
|
||||||
const e = rankingMap.get(id)
|
const e = rankingMap.get(id)
|
||||||
return evalScores?.balanced[id]?.balancedAverage ?? e?.avgGlobalScore ?? 0
|
const balanced = evalScores?.balanced[id]?.balancedAverage
|
||||||
|
if (useBalanced && balanced != null) return balanced
|
||||||
|
return e?.avgGlobalScore ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let cutoffIndex = -1
|
let cutoffIndex = -1
|
||||||
|
|||||||
Reference in New Issue
Block a user