76 lines
2 KiB
TypeScript
76 lines
2 KiB
TypeScript
|
|
/* ResolutionTrend — line chart of average resolution hours per day completed,
|
||
|
|
* last 30 days. Single series, Wulf Blue stroke. */
|
||
|
|
|
||
|
|
'use client';
|
||
|
|
|
||
|
|
import {
|
||
|
|
CartesianGrid,
|
||
|
|
Line,
|
||
|
|
LineChart,
|
||
|
|
ResponsiveContainer,
|
||
|
|
Tooltip,
|
||
|
|
XAxis,
|
||
|
|
YAxis,
|
||
|
|
} from 'recharts';
|
||
|
|
|
||
|
|
interface ResolutionPoint {
|
||
|
|
date: string;
|
||
|
|
avgHours: number | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface ResolutionTrendProps {
|
||
|
|
data: ResolutionPoint[];
|
||
|
|
height?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
function fmtDate(iso: string) {
|
||
|
|
return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
||
|
|
}
|
||
|
|
|
||
|
|
export function ResolutionTrend({ data, height = 180 }: ResolutionTrendProps) {
|
||
|
|
return (
|
||
|
|
<ResponsiveContainer width="100%" height={height}>
|
||
|
|
<LineChart data={data} margin={{ top: 4, right: 8, bottom: 4, left: 0 }}>
|
||
|
|
<CartesianGrid stroke="var(--border)" strokeDasharray="2 4" vertical={false} />
|
||
|
|
<XAxis
|
||
|
|
dataKey="date"
|
||
|
|
tickFormatter={fmtDate}
|
||
|
|
interval="preserveStartEnd"
|
||
|
|
minTickGap={48}
|
||
|
|
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
|
||
|
|
axisLine={false}
|
||
|
|
tickLine={false}
|
||
|
|
/>
|
||
|
|
<YAxis
|
||
|
|
tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
|
||
|
|
axisLine={false}
|
||
|
|
tickLine={false}
|
||
|
|
width={32}
|
||
|
|
tickFormatter={(v: number) => `${v}h`}
|
||
|
|
/>
|
||
|
|
<Tooltip
|
||
|
|
contentStyle={{
|
||
|
|
background: 'var(--popover)',
|
||
|
|
border: '1px solid var(--border)',
|
||
|
|
borderRadius: 6,
|
||
|
|
fontSize: 12,
|
||
|
|
}}
|
||
|
|
labelFormatter={(value) => fmtDate(String(value))}
|
||
|
|
formatter={(value) =>
|
||
|
|
value == null ? ['—', 'avg'] : [`${Number(value).toFixed(1)} h`, 'avg']
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
<Line
|
||
|
|
type="monotone"
|
||
|
|
dataKey="avgHours"
|
||
|
|
stroke="var(--primary)"
|
||
|
|
strokeWidth={2}
|
||
|
|
dot={false}
|
||
|
|
connectNulls
|
||
|
|
isAnimationActive={false}
|
||
|
|
/>
|
||
|
|
</LineChart>
|
||
|
|
</ResponsiveContainer>
|
||
|
|
);
|
||
|
|
}
|