"use client" const PALETTE = [ "var(--chart-line-1)", "var(--chart-line-2)", "var(--chart-line-3)", "var(--chart-line-4)", "var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", ] /** Легенда для демо-данных (ключи `msk`, `spb`, …). */ const LEGACY_LABELS: Record = { msk: "MSK", spb: "SPB", fra: "FRA", ams: "AMS", } interface LatencyChartProps { series: Record /** Подписи линий (ключ → короткое имя). Если нет — берётся legacy или сам ключ. */ labels?: Record } export function LatencyChart({ series, labels: labelsProp }: LatencyChartProps) { const W = 800 const H = 220 const pad = { l: 44, r: 12, t: 12, b: 28 } const iw = W - pad.l - pad.r const ih = H - pad.t - pad.b const entries = Object.entries(series).filter(([, arr]) => arr.length > 0) const pointCount = entries.length ? Math.max(...entries.map(([, a]) => a.length), 2) : 60 const allVals = entries.flatMap(([, arr]) => arr) const maxVal = Math.ceil(Math.max(1, ...allVals) / 20) * 20 + 10 const xAt = (i: number, n: number) => pad.l + (i / Math.max(n - 1, 1)) * iw const yAt = (v: number) => pad.t + (1 - v / maxVal) * ih const gridLines = [0, 0.25, 0.5, 0.75, 1] const tickIndices = [ 0, Math.floor((pointCount - 1) * 0.25), Math.floor((pointCount - 1) * 0.5), Math.floor((pointCount - 1) * 0.75), pointCount - 1, ] const labelForIndex = (i: number) => { const spanMin = 60 const m = Math.round(spanMin * (1 - i / Math.max(pointCount - 1, 1))) return `-${m}м` } return (
{gridLines.map((p, i) => ( {Math.round(maxVal * (1 - p))}мс ))} {tickIndices.map((i) => ( {labelForIndex(i)} ))} {entries.map(([key, arr], idx) => { const n = arr.length const pts = arr.map((v, i) => `${xAt(i, n).toFixed(1)},${yAt(v).toFixed(1)}`).join(" ") const color = PALETTE[idx % PALETTE.length] return ( ) })}
{entries.map(([key], idx) => { const color = PALETTE[idx % PALETTE.length] const leg = labelsProp?.[key] ?? LEGACY_LABELS[key] ?? key.replace(/^src-/, "") return (
{leg}
) })}
) }