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:
parent
27828c8834
commit
9d047b37ec
1 changed files with 54 additions and 0 deletions
54
app/(app)/insights/components/velocity-chart.tsx
Normal file
54
app/(app)/insights/components/velocity-chart.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue