diff --git a/apps/web/src/views/detail.ejs b/apps/web/src/views/detail.ejs index a251d03..fef6edf 100644 --- a/apps/web/src/views/detail.ejs +++ b/apps/web/src/views/detail.ejs @@ -179,12 +179,26 @@ 'us-west': 'πŸ‡ΊπŸ‡Έ US West', 'ap-southeast': 'πŸ‡ΈπŸ‡¬ AP Southeast' }; + const MAX_RUNS = 50; let chartPings = <%~ JSON.stringify(chartPings.map(p => ({ latency_ms: p.latency_ms, region: p.region || '__none__', checked_at: p.checked_at, up: p.up, run_id: p.run_id || null, status_code: p.status_code }))) %>; + // Trim initial data to MAX_RUNS + (function() { + const seen = []; const s = new Set(); + for (const p of chartPings) { + const rid = p.run_id || p.checked_at; + if (!s.has(rid)) { s.add(rid); seen.push(rid); } + } + if (seen.length > MAX_RUNS) { + const stale = new Set(seen.slice(0, seen.length - MAX_RUNS)); + chartPings = chartPings.filter(p => !stale.has(p.run_id || p.checked_at)); + } + })(); + function renderChart() { const canvas = document.getElementById('chart-canvas'); const container = canvas.parentElement; @@ -208,18 +222,23 @@ return; } - // Compute a shared x-position per run_id (avg checked_at of all pings in the run) - // so all regions in the same run align vertically on the chart + // Build ordered list of unique runs, sorted by avg checked_at const runTimes = {}; for (const p of data) { - const rid = p.run_id || p.checked_at; // fallback for pings without run_id + const rid = p.run_id || p.checked_at; if (!runTimes[rid]) runTimes[rid] = []; runTimes[rid].push(new Date(p.checked_at).getTime()); } - const runAvgTime = {}; - for (const [rid, times] of Object.entries(runTimes)) { - runAvgTime[rid] = times.reduce((a, b) => a + b, 0) / times.length; - } + const runs = Object.keys(runTimes).sort((a, b) => { + const avgA = runTimes[a].reduce((x, y) => x + y, 0) / runTimes[a].length; + const avgB = runTimes[b].reduce((x, y) => x + y, 0) / runTimes[b].length; + return avgA - avgB; + }); + + // Evenly space runs across the chart width (fixed spacing like SSR) + const runIndex = {}; + runs.forEach((rid, i) => { runIndex[rid] = i; }); + const maxIdx = Math.max(runs.length - 1, 1); // Group by region const byRegion = {}; @@ -232,13 +251,12 @@ const allLat = data.map(p => p.latency_ms); const yMin = Math.min(...allLat), yMax = Math.max(...allLat); const yRange = yMax - yMin || 1; - const avgTimes = Object.values(runAvgTime); - const tMin = Math.min(...avgTimes), tMax = Math.max(...avgTimes); - const tRange = tMax - tMin || 1; - function toX(t) { return pad.left + ((t - tMin) / tRange) * cW; } function toY(v) { return pad.top + cH - ((v - yMin) / yRange) * cH; } - function pingX(p) { return toX(runAvgTime[p.run_id || p.checked_at]); } + function pingX(p) { + const idx = runIndex[p.run_id || p.checked_at] || 0; + return pad.left + (idx / maxIdx) * cW; + } // Grid lines ctx.strokeStyle = 'rgba(75,85,99,0.3)'; ctx.lineWidth = 0.5; @@ -310,9 +328,6 @@ // Store for hover canvas._regionPoints = regionPoints; - canvas._toX = toX; canvas._toY = toY; - canvas._tMin = tMin; canvas._tRange = tRange; - canvas._W = W; canvas._H = H; canvas._pad = pad; canvas._cW = cW; } // Group pings by run_id for tooltip @@ -472,7 +487,6 @@ status_code: ping.status_code }); // Count distinct runs and remove oldest runs as a group - const MAX_RUNS = 50; const seen = []; const runSet = new Set(); for (const p of chartPings) { const rid = p.run_id || p.checked_at;