From ee68f8af41f8fb6cf8bae67723214510f1efd9ab Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 27 Apr 2026 13:24:34 +0200 Subject: [PATCH] feat: side panel shows per-juror baseline and balanced contribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the ranking router's roundEvaluationScores response with per-juror grading stats (mean, stddev, count) plus the round's overall mean/stddev. The side-sheet juror rows render 'typical X.XX → contributes Y.YY' next to each Score badge whenever balanced is on, making the z-rescaling visible per individual rather than only as a project-level number. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../admin/round/ranking-dashboard.tsx | 22 +++++++++++++++++++ src/server/routers/ranking.ts | 15 ++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/components/admin/round/ranking-dashboard.tsx b/src/components/admin/round/ranking-dashboard.tsx index 4b29132..3f63ec9 100644 --- a/src/components/admin/round/ranking-dashboard.tsx +++ b/src/components/admin/round/ranking-dashboard.tsx @@ -1136,6 +1136,28 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran )} Score: {a.evaluation?.globalScore?.toFixed(1) ?? '—'} + {useBalanced && (() => { + const userId = a.user?.id + const score = a.evaluation?.globalScore + if (!userId || score == null) return null + const stats = evalScores?.jurorStats?.[userId] + const overallMean = evalScores?.overallMean + const overallStddev = evalScores?.overallStddev + if (!stats || overallMean == null || overallStddev == null || overallStddev === 0) return null + const z = stats.stddev > 0 + ? (score - stats.mean) / stats.stddev + : (score - overallMean) / overallStddev + const contributesAs = overallMean + z * overallStddev + return ( + e.stopPropagation()} + > + typical {stats.mean.toFixed(2)} → contributes {contributesAs.toFixed(2)} + + ) + })()} {isExpanded && a.evaluation?.feedbackText && ( diff --git a/src/server/routers/ranking.ts b/src/server/routers/ranking.ts index 2e1e5dd..5757c9a 100644 --- a/src/server/routers/ranking.ts +++ b/src/server/routers/ranking.ts @@ -537,6 +537,19 @@ export const rankingRouter = router({ } } - return { byProject, balanced } + // Per-juror grading stats so the side panel can render each juror's + // personal baseline and rescaled contribution. + const jurorStats: Record = {} + for (const [userId, s] of balanceCtx.jurorStats.entries()) { + jurorStats[userId] = { mean: s.mean, stddev: s.stddev, count: s.count } + } + + return { + byProject, + balanced, + jurorStats, + overallMean: balanceCtx.overallMean, + overallStddev: balanceCtx.overallStddev, + } }), })