feat(insights): add VelocityChart grouped BarChart component

Renders DONE-task counts per sprint per product as grouped bars with
SERIES_COLORS, a dotted average ReferenceLine, and an empty-state for
<2 completed sprints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-02 16:12:27 +02:00
parent 27828c8834
commit 9d047b37ec

View file

@ -0,0 +1,54 @@
'use client'
import {
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
Legend,
ReferenceLine,
ResponsiveContainer,
} from 'recharts'
import type { VelocityData } from '@/lib/insights/velocity'
import { SERIES_COLORS } from '@/lib/chart-colors'
interface Props {
data: VelocityData
}
export function VelocityChart({ data }: Props) {
const { sprints, productNames } = data
if (sprints.length < 2) {
return (
<p className="text-muted-foreground text-sm">
Velocity wordt zichtbaar na 2+ voltooide sprints
</p>
)
}
const chartData = sprints.map((s, i) => {
const row: Record<string, string | number> = { sprintLabel: `S${i + 1}` }
row[s.productName] = s.doneCount
return row
})
const totalDone = sprints.reduce((sum, s) => sum + s.doneCount, 0)
const avg = Math.round((totalDone / sprints.length) * 10) / 10
return (
<ResponsiveContainer width="100%" height={260}>
<BarChart data={chartData}>
<XAxis dataKey="sprintLabel" tick={{ fontSize: 11 }} />
<YAxis allowDecimals={false} tick={{ fontSize: 11 }} />
<Tooltip />
<Legend />
<ReferenceLine y={avg} stroke="var(--muted-foreground)" strokeDasharray="3 3" />
{productNames.map((p, i) => (
<Bar key={p.id} dataKey={p.name} fill={SERIES_COLORS[i % SERIES_COLORS.length]} />
))}
</BarChart>
</ResponsiveContainer>
)
}