feat: side panel shows raw + balanced averages, list drops delta
Removes the per-row '⇢ X.X' annotation from the ranking list — the list view stays clean. The side panel's stats area gains a combined Avg Score card that shows Raw and Balanced side-by-side, with the active one (per the round's toggle) bolded and tagged 'used for ranking'. Pass Rate and Evaluators move below into a 2-col grid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,6 @@ type SortableProjectRowProps = {
|
||||
entry: (RankedProjectEntry & { originalIndex?: number }) | undefined
|
||||
projectInfo: ProjectInfo | undefined
|
||||
jurorScores: JurorScore[] | undefined
|
||||
balancedScore: number | null
|
||||
onSelect: () => void
|
||||
isSelected: boolean
|
||||
originalRank: number | undefined // from snapshotOrder — always in sync with localOrder
|
||||
@@ -97,7 +96,6 @@ function SortableProjectRow({
|
||||
entry,
|
||||
projectInfo,
|
||||
jurorScores,
|
||||
balancedScore,
|
||||
onSelect,
|
||||
isSelected,
|
||||
originalRank,
|
||||
@@ -202,27 +200,6 @@ function SortableProjectRow({
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
{/* Raw + balanced averages shown side by side */}
|
||||
{entry?.avgGlobalScore !== null && entry?.avgGlobalScore !== undefined && jurorScores && jurorScores.length > 1 && (
|
||||
<div className="flex items-center gap-1.5 text-xs" title="Raw juror average vs. juror-balanced average (z-score normalized per juror, rescaled to 1-10)">
|
||||
<span className="font-medium text-muted-foreground">
|
||||
{entry.avgGlobalScore.toFixed(1)}
|
||||
</span>
|
||||
{balancedScore != null && Math.abs(balancedScore - entry.avgGlobalScore) >= 0.05 && (
|
||||
<span
|
||||
className={cn(
|
||||
'font-semibold tabular-nums rounded px-1.5 py-0.5 border',
|
||||
balancedScore > entry.avgGlobalScore
|
||||
? 'bg-emerald-50 text-emerald-700 border-emerald-200'
|
||||
: 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
)}
|
||||
>
|
||||
⇢ {balancedScore.toFixed(1)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Advance decision indicator */}
|
||||
<div className={cn(
|
||||
'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium',
|
||||
@@ -1000,7 +977,6 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
entry={rankingMap.get(projectId)}
|
||||
projectInfo={projectInfoMap.get(projectId)}
|
||||
jurorScores={evalScores?.byProject[projectId]}
|
||||
balancedScore={evalScores?.balanced[projectId]?.balancedAverage ?? null}
|
||||
onSelect={() => setSelectedProjectId(projectId)}
|
||||
isSelected={selectedProjectId === projectId}
|
||||
originalRank={hasReorders ? snapshotOrder[projectId] : undefined}
|
||||
@@ -1075,31 +1051,50 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
||||
</div>
|
||||
<Switch checked={useBalanced} onCheckedChange={persistUseBalanced} />
|
||||
</div>
|
||||
{/* Stats summary */}
|
||||
{projectDetail.stats && (
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="rounded-lg border p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">Avg Score</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{projectDetail.stats.averageGlobalScore?.toFixed(1) ?? '—'}
|
||||
</p>
|
||||
{/* Stats summary: combined Avg card with Raw + Balanced side-by-side */}
|
||||
{projectDetail.stats && (() => {
|
||||
const raw = selectedProjectId
|
||||
? evalScores?.balanced[selectedProjectId]?.rawAverage ?? null
|
||||
: null
|
||||
const balanced = selectedProjectId
|
||||
? evalScores?.balanced[selectedProjectId]?.balancedAverage ?? null
|
||||
: null
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="rounded-lg border p-3">
|
||||
<p className="text-xs text-muted-foreground mb-2">Avg Score</p>
|
||||
<div className="flex items-baseline gap-4 flex-wrap">
|
||||
<div className={`flex items-baseline gap-1 ${useBalanced ? 'text-muted-foreground' : 'font-semibold'}`}>
|
||||
<span className="text-xs">Raw</span>
|
||||
<span className="text-lg tabular-nums">{raw != null ? raw.toFixed(1) : '—'}</span>
|
||||
{!useBalanced && <span className="ml-1 text-[10px] text-muted-foreground">← used for ranking</span>}
|
||||
</div>
|
||||
<div className={`flex items-baseline gap-1 ${useBalanced ? 'font-semibold' : 'text-muted-foreground'}`}>
|
||||
<span className="text-xs">Balanced</span>
|
||||
<span className="text-lg tabular-nums">{balanced != null ? balanced.toFixed(1) : '—'}</span>
|
||||
{useBalanced && <span className="ml-1 text-[10px] text-muted-foreground">← used for ranking</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="rounded-lg border p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">Pass Rate</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{projectDetail.stats.totalEvaluations > 0
|
||||
? `${Math.round((projectDetail.stats.yesVotes / projectDetail.stats.totalEvaluations) * 100)}%`
|
||||
: '—'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">Evaluators</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{projectDetail.stats.totalEvaluations}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg border p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">Pass Rate</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{projectDetail.stats.totalEvaluations > 0
|
||||
? `${Math.round((projectDetail.stats.yesVotes / projectDetail.stats.totalEvaluations) * 100)}%`
|
||||
: '—'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">Evaluators</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{projectDetail.stats.totalEvaluations}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Per-juror evaluations */}
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user