refactor tier 3
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Server-rendered "X ago" timestamp. Returns an HTML span carrying the original
|
||||
// epoch ms in a data attribute so a tiny client script can refresh it without a
|
||||
// re-render. Reused by the dashboard and the public status pages.
|
||||
export function timeAgoSSR(date: string | Date): string {
|
||||
const ts = new Date(date).getTime();
|
||||
const s = Math.ceil((Date.now() - ts) / 1000) || 1;
|
||||
const text =
|
||||
s < 60 ? `${s}s ago`
|
||||
: s < 3600 ? `${Math.floor(s / 60)}m ago`
|
||||
: s < 86400 ? `${Math.floor(s / 3600)}h ago`
|
||||
: `${Math.floor(s / 86400)}d ago`;
|
||||
return `<span class="timestamp" data-ts="${ts}">${text}</span>`;
|
||||
}
|
||||
Reference in New Issue
Block a user