feat: live sparkline updates on SSE ping
This commit is contained in:
parent
31d1fa7b04
commit
ef2b2c043d
|
|
@ -53,9 +53,15 @@
|
||||||
const downCount = monitorsWithPings.filter(m => m.pings[0]?.up === false).length;
|
const downCount = monitorsWithPings.filter(m => m.pings[0]?.up === false).length;
|
||||||
summary.innerHTML = `<span class="text-green-400">${upCount} up</span> · <span class="${downCount > 0 ? 'text-red-400' : 'text-gray-500'}">${downCount} down</span> · ${monitors.length} total`;
|
summary.innerHTML = `<span class="text-green-400">${upCount} up</span> · <span class="${downCount > 0 ? 'text-red-400' : 'text-gray-500'}">${downCount} down</span> · ${monitors.length} total`;
|
||||||
|
|
||||||
|
// Store latencies per monitor for live sparkline updates
|
||||||
|
window._monitorLatencies = window._monitorLatencies || {};
|
||||||
|
monitorsWithPings.forEach(m => {
|
||||||
|
window._monitorLatencies[m.id] = m.pings.filter(c => c.latency_ms != null).map(c => c.latency_ms).reverse();
|
||||||
|
});
|
||||||
|
|
||||||
list.innerHTML = monitorsWithPings.map(m => {
|
list.innerHTML = monitorsWithPings.map(m => {
|
||||||
const lastPing = m.pings[0];
|
const lastPing = m.pings[0];
|
||||||
const latencies = m.pings.filter(c => c.latency_ms != null).map(c => c.latency_ms).reverse();
|
const latencies = window._monitorLatencies[m.id];
|
||||||
const avgLatency = latencies.length ? Math.round(latencies.reduce((a, b) => a + b, 0) / latencies.length) : null;
|
const avgLatency = latencies.length ? Math.round(latencies.reduce((a, b) => a + b, 0) / latencies.length) : null;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
|
@ -69,7 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-6 shrink-0 ml-4">
|
<div class="flex items-center gap-6 shrink-0 ml-4">
|
||||||
<div class="hidden sm:block">${sparkline(latencies)}</div>
|
<div class="hidden sm:block stat-sparkline">${sparkline(latencies)}</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="text-sm text-gray-300 stat-latency">${avgLatency != null ? avgLatency + 'ms' : '—'}</div>
|
<div class="text-sm text-gray-300 stat-latency">${avgLatency != null ? avgLatency + 'ms' : '—'}</div>
|
||||||
<div class="text-xs text-gray-500 stat-last">${lastPing ? timeAgo(lastPing.checked_at) : 'no pings'}</div>
|
<div class="text-xs text-gray-500 stat-last">${lastPing ? timeAgo(lastPing.checked_at) : 'no pings'}</div>
|
||||||
|
|
@ -96,17 +102,28 @@
|
||||||
const monitors = await api('/monitors/');
|
const monitors = await api('/monitors/');
|
||||||
monitors.forEach(m => {
|
monitors.forEach(m => {
|
||||||
const es = watchMonitor(m.id, (ping) => {
|
const es = watchMonitor(m.id, (ping) => {
|
||||||
// Update the card's last ping info without full reload
|
|
||||||
const card = document.querySelector(`[data-monitor-id="${m.id}"]`);
|
const card = document.querySelector(`[data-monitor-id="${m.id}"]`);
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
|
// Status dot
|
||||||
const statusDot = card.querySelector('.status-dot');
|
const statusDot = card.querySelector('.status-dot');
|
||||||
const latencyEl = card.querySelector('.stat-latency');
|
if (statusDot) statusDot.className = `status-dot w-2.5 h-2.5 rounded-full ${ping.up ? 'bg-green-500' : 'bg-red-500'}`;
|
||||||
const lastEl = card.querySelector('.stat-last');
|
|
||||||
if (statusDot) {
|
// Latency text
|
||||||
statusDot.className = `status-dot w-2.5 h-2.5 rounded-full ${ping.up ? 'bg-green-500' : 'bg-red-500'}`;
|
if (ping.latency_ms) card.querySelector('.stat-latency').textContent = `${ping.latency_ms}ms`;
|
||||||
|
|
||||||
|
// Timestamp
|
||||||
|
card.querySelector('.stat-last').innerHTML = timeAgo(ping.checked_at);
|
||||||
|
|
||||||
|
// Sparkline — push new value, keep last 20, redraw
|
||||||
|
if (ping.latency_ms != null) {
|
||||||
|
const lats = window._monitorLatencies[m.id] || [];
|
||||||
|
lats.push(ping.latency_ms);
|
||||||
|
if (lats.length > 20) lats.shift();
|
||||||
|
window._monitorLatencies[m.id] = lats;
|
||||||
|
const sparkEl = card.querySelector('.stat-sparkline');
|
||||||
|
if (sparkEl) sparkEl.innerHTML = sparkline(lats);
|
||||||
}
|
}
|
||||||
if (latencyEl && ping.latency_ms) latencyEl.textContent = `${ping.latency_ms}ms`;
|
|
||||||
if (lastEl) lastEl.innerHTML = timeAgo(ping.checked_at);
|
|
||||||
});
|
});
|
||||||
if (es) sseConnections.push(es);
|
if (es) sseConnections.push(es);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue