53 lines
2.3 KiB
TypeScript
53 lines
2.3 KiB
TypeScript
// Shared sparkline utilities used by both apps/web (dashboard) and apps/status
|
|
// (public status pages). Pure HTML/SVG output, no client JS required for the
|
|
// first paint.
|
|
|
|
import { REGION_COLORS } from "../plans";
|
|
|
|
export function sparkline(values: number[], width = 120, height = 32, color = '#60a5fa', region = 'default'): string {
|
|
if (!values.length) return '';
|
|
const max = Math.max(...values, 1);
|
|
const min = Math.min(...values, 0);
|
|
const range = max - min || 1;
|
|
const step = width / Math.max(values.length - 1, 1);
|
|
const points = values.map((v, i) => {
|
|
const x = i * step;
|
|
const y = height - ((v - min) / range) * (height - 4) - 2;
|
|
return `${x},${y}`;
|
|
}).join(' ');
|
|
return `<svg width="${width}" height="${height}" class="inline-block" data-vals="${values.join(',')}" data-region="${region}"><polyline points="${points}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
}
|
|
|
|
export function pickBestRegion(pings: Array<{ latency_ms?: number | null; region?: string | null }>): { region: string; values: number[]; latest: number | null } {
|
|
const withLatency = pings.filter((p) => p.latency_ms != null);
|
|
if (!withLatency.length) return { region: 'default', values: [], latest: null };
|
|
|
|
const byRegion: Record<string, number[]> = {};
|
|
for (const p of withLatency) {
|
|
const key = p.region || 'default';
|
|
if (!byRegion[key]) byRegion[key] = [];
|
|
byRegion[key].push(p.latency_ms!);
|
|
}
|
|
|
|
const recentRegions = new Set(withLatency.slice(-3).map((p) => p.region || 'default'));
|
|
|
|
let bestRegion = 'default';
|
|
let bestAvg = Infinity;
|
|
for (const [region, vals] of Object.entries(byRegion)) {
|
|
if (!recentRegions.has(region)) continue;
|
|
const recent = vals.slice(-3);
|
|
const avg = recent.reduce((a, b) => a + b, 0) / recent.length;
|
|
if (avg < bestAvg) { bestAvg = avg; bestRegion = region; }
|
|
}
|
|
|
|
const values = byRegion[bestRegion] || [];
|
|
return { region: bestRegion, values, latest: values.length ? values[values.length - 1]! : null };
|
|
}
|
|
|
|
export function sparklineFromPings(pings: Array<{ latency_ms?: number | null; region?: string | null }>, width = 120, height = 32): string {
|
|
const { region, values } = pickBestRegion(pings);
|
|
if (!values.length) return '';
|
|
const color = REGION_COLORS[region] || '#60a5fa';
|
|
return sparkline(values, width, height, color, region);
|
|
}
|