From 654d3849eb0e0e9035cf50bb8801c47388f3f297 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Wed, 3 Jun 2026 13:00:34 +0200 Subject: [PATCH] fix: legend CSS, SI prefix for x-axis ticks, table sort cleanup --- web/app/globals.css | 4 ++++ web/components/ChartCard/CardShell.tsx | 2 +- web/components/ChartCard/TableViz.tsx | 22 +++------------------- web/components/ChartCard/UpsChart.tsx | 6 +++--- web/components/ChartCard/plotHelpers.ts | 9 +++++---- web/lib/formatNumber.ts | 7 ++++--- 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/web/app/globals.css b/web/app/globals.css index f581895..479fe85 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -14,4 +14,8 @@ body { background: var(--background); color: var(--foreground); font-family: ui-sans-serif, system-ui, sans-serif; +} + +.u-legend .u-series:first-child { + display: none; } \ No newline at end of file diff --git a/web/components/ChartCard/CardShell.tsx b/web/components/ChartCard/CardShell.tsx index 485dead..5de90db 100644 --- a/web/components/ChartCard/CardShell.tsx +++ b/web/components/ChartCard/CardShell.tsx @@ -46,7 +46,7 @@ export function CardShell({ title, onEdit, onDelete, empty, children, legendCont {/* Legend scrolls independently, capped at 25% card height */}
)} diff --git a/web/components/ChartCard/TableViz.tsx b/web/components/ChartCard/TableViz.tsx index d719a64..e41783a 100644 --- a/web/components/ChartCard/TableViz.tsx +++ b/web/components/ChartCard/TableViz.tsx @@ -14,27 +14,11 @@ interface Props { export default function TableViz({ config, rows, onEdit, onDelete }: Props) { const { localeMap } = useApp(); - const sortCol = config.signal_type === 'red' ? 'red' : 'green'; - const latest = new Map(); for (const row of rows) { latest.set(`${row.combinator}::${row.item_key}`, { green: row.green, red: row.red }); } - const tableRows = [...latest.entries()] - .sort((a, b) => { - const va = a[1][sortCol] ?? 0; - const vb = b[1][sortCol] ?? 0; - switch (config.order_by) { - case 'value_asc': - case 'delta_asc': - return va - vb; - case 'abs_desc': - return Math.abs(vb) - Math.abs(va); - default: - return vb - va; - } - }) - .slice(0, config.series_limit); + const tableRows = [...latest.entries()].slice(0, config.series_limit); return ( @@ -57,12 +41,12 @@ export default function TableViz({ config, rows, onEdit, onDelete }: Props) { {combinator} {config.signal_type !== 'red' && ( - {vals.green != null ? formatSI(vals.green) : '--'} + {vals.green != null ? formatSI(vals.green, undefined, 0) : '--'} )} {config.signal_type !== 'green' && ( - {vals.red != null ? formatSI(vals.red) : '--'} + {vals.red != null ? formatSI(vals.red, undefined, 0) : '--'} )} diff --git a/web/components/ChartCard/UpsChart.tsx b/web/components/ChartCard/UpsChart.tsx index f19887a..18b5447 100644 --- a/web/components/ChartCard/UpsChart.tsx +++ b/web/components/ChartCard/UpsChart.tsx @@ -32,9 +32,9 @@ export default function UpsChart({ config, upsRows, timeMode, onEdit, onDelete } const xAxis: uPlot.Axis = { ...AXIS_BASE, - ...(timeMode === 'real' && { - values: (_u, vals) => vals.map(v => v == null ? '' : new Date(v * 1000).toLocaleTimeString()), - }), + values: timeMode === 'real' + ? (_u, vals) => vals.map(v => v == null ? '' : new Date(v * 1000).toLocaleTimeString()) + : (_u, vals) => vals.map(v => v == null ? '' : formatSI(v)), }; return new uPlot({ diff --git a/web/components/ChartCard/plotHelpers.ts b/web/components/ChartCard/plotHelpers.ts index c7e1512..085839d 100644 --- a/web/components/ChartCard/plotHelpers.ts +++ b/web/components/ChartCard/plotHelpers.ts @@ -169,10 +169,11 @@ export function makeSignalsAxes(timeMode: 'real' | 'tick', locale?: string): uPl return [ { ...AXIS_BASE, - ...(timeMode === 'real' && { - values: (_u: uPlot, vals: (number | null)[]) => - vals.map(v => v == null ? '' : new Date(v * 1000).toLocaleTimeString()), - }), + values: timeMode === 'real' + ? (_u: uPlot, vals: (number | null)[]) => + vals.map(v => v == null ? '' : new Date(v * 1000).toLocaleTimeString()) + : (_u: uPlot, vals: (number | null)[]) => + vals.map(v => v == null ? '' : formatSI(v, locale)), }, { ...AXIS_BASE, diff --git a/web/lib/formatNumber.ts b/web/lib/formatNumber.ts index 5eda4e0..0fd03fa 100644 --- a/web/lib/formatNumber.ts +++ b/web/lib/formatNumber.ts @@ -4,19 +4,20 @@ const SI_THRESHOLDS = [ { limit: 1_000, divisor: 1_000, suffix: 'K' }, ] as const; -export function formatSI(v: number, locale?: string): string { +export function formatSI(v: number, locale?: string, fractionDigits?: number): string { const abs = Math.abs(v); + const fd = fractionDigits ?? 3; for (const { limit, divisor, suffix } of SI_THRESHOLDS) { if (abs >= limit) { const formatted = new Intl.NumberFormat(locale, { - maximumFractionDigits: 3, + maximumFractionDigits: fd, minimumFractionDigits: 0, }).format(v / divisor); return `${formatted}${suffix}`; } } return new Intl.NumberFormat(locale, { - maximumFractionDigits: 0, + maximumFractionDigits: fractionDigits != null ? fractionDigits : 0, minimumFractionDigits: 0, }).format(v); }