Files
MOPC-Portal/src/components/charts/score-distribution.tsx
Matt 9d945c33f9
All checks were successful
Build and Push Docker Image / build (push) Successful in 12m29s
Observer platform overhaul: Nivo charts, round-type stats, UX improvements
Phase 1: Fix 6 backend data bugs in analytics.ts (roundName filtering,
unscored projects, criteria scores, activeRoundCount scoping, email
privacy leaks in juror consistency + workload)

Phase 2-3: Migrate all 9 chart components from Recharts to Nivo
(@nivo/bar, @nivo/line, @nivo/pie, @nivo/scatterplot) with shared brand
theme, scoreGradient colors, and STATUS_COLORS map. Fixes scatter plot
outlier coloring and pie chart label visibility bugs.

Phase 4: Add round-type-aware stats (getRoundTypeStats backend +
RoundTypeStatsCards component) showing appropriate metrics per round
type (intake/filtering/evaluation/submission/mentoring/live/deliberation).

Phase 5: UX improvements — Stage→Round terminology, clickable dashboard
round links, URL-based round selection (?round=), round type indicators
in selectors, accessible Toggle-based cross-round comparison, sortable
project table columns (title/score/evaluations), brand score colors on
dashboard bar chart with aria labels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:44:38 +01:00

64 lines
1.7 KiB
TypeScript

'use client'
import { ResponsiveBar } from '@nivo/bar'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { nivoTheme, scoreGradient } from './chart-theme'
interface ScoreDistributionProps {
data: { score: number; count: number }[]
averageScore: number
totalScores: number
}
export function ScoreDistributionChart({
data,
averageScore,
totalScores,
}: ScoreDistributionProps) {
const chartData = data.map((d) => ({
score: String(d.score),
count: d.count,
}))
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span>Score Distribution</span>
<span className="text-sm font-normal text-muted-foreground">
Avg: {averageScore.toFixed(2)} ({totalScores} scores)
</span>
</CardTitle>
</CardHeader>
<CardContent>
<div style={{ height: '300px' }}>
<ResponsiveBar
data={chartData}
keys={['count']}
indexBy="score"
theme={nivoTheme}
colors={(bar) => scoreGradient(Number(bar.indexValue))}
borderRadius={4}
enableLabel={true}
labelSkipHeight={12}
labelTextColor="#ffffff"
axisBottom={{
legend: 'Score',
legendPosition: 'middle',
legendOffset: 36,
}}
axisLeft={{
legend: 'Count',
legendPosition: 'middle',
legendOffset: -40,
}}
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
padding={0.2}
animate={true}
/>
</div>
</CardContent>
</Card>
)
}