import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts" export type ChartSeries = { key: string; label: string; color: string } const GRID = "rgba(130,130,150,0.14)" const AXIS = "rgba(130,130,150,0.85)" function niceCeil(v: number): number { if (v <= 0) return 10 const mag = Math.pow(10, Math.floor(Math.log10(v))) const n = v / mag const step = n <= 1 ? 1 : n <= 2 ? 2 : n <= 5 ? 5 : 10 return step * mag } function fmt(v: number, unit: string): string { const n = unit === "%" ? Math.round(v) : v >= 1000 ? `${(v / 1000).toFixed(1)}k` : Math.round(v).toString() return `${n}${unit}` } function ChartTooltip({ active, payload, unit }: any) { if (!active || !payload?.length) return null return (
{payload.map((p: any) => (
{p.name} {fmt(p.value, unit)}
))}
) } /** Wiederverwendbarer Live-Verlaufsgraph (Recharts Area, glatte Splines, Gradient-Fill, * Hover-Tooltip). yMode='percent' → 0..100 in 25er-Schritten; 'auto' → dynamisch (nice). */ export function LiveAreaChart({ data, series, unit = "%", yMode = "percent", height = 176, }: { data: any[] series: ChartSeries[] unit?: string yMode?: "percent" | "auto" height?: number }) { const peak = data.reduce( (m, row) => series.reduce((mm, s) => Math.max(mm, Number(row[s.key]) || 0), m), 0 ) const yMax = yMode === "percent" ? Math.min(100, Math.max(25, Math.ceil((peak * 1.2) / 25) * 25)) : Math.max(niceCeil(peak * 1.15), 10) return (
{series.map((s) => ( ))} fmt(v, unit)} width={42} axisLine={false} tickLine={false} tick={{ fontSize: 10, fill: AXIS }} /> } cursor={{ stroke: AXIS, strokeOpacity: 0.4, strokeDasharray: "3 3" }} /> {series.map((s) => ( ))}
) }