feat: Live Dashboard redesign — 6 columns, hover details, last-alert column

Replaces the 11-column table with severity-sorted record rows: host,
combined status chip, latency, alert chips, time-in-state, and the most
recent alert message. Per-family address/state/latency/last-change move
to a hover card on the status chip and permanently to a new Connectivity
table in the Host Overview info section (host info API now includes
connections). Last-alert is seeded from /api/0/alerts and kept fresh
from the event stream; the event log below uses the same row idiom with
its filters intact. WS reconnect state shows in the toolbar instead of
a modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NfPpSpccTWBfZg1FTveyaU
This commit is contained in:
2026-07-10 13:45:03 -04:00
co-authored by Claude Fable 5
parent 5d9f161706
commit 8d268adf70
4 changed files with 524 additions and 670 deletions
+24
View File
@@ -444,6 +444,30 @@
<span class="info-label">Last Packet</span><span class="info-value">${lastPkt}</span>
</div>`;
if (data.connections && data.connections.length) {
html += `<div class="info-thresholds-title">Connectivity</div>
<table class="data-table"><thead><tr>
<th>Family</th><th>Address</th><th>State</th>
<th class="num">RTT</th><th>Last Change</th><th>Last Packet</th>
</tr></thead><tbody>`;
for (const c of data.connections) {
const st = escHtml(c.state || '—');
const stCls = c.state === 'up' ? 'status-up' : (c.state ? 'status-down' : '');
const rtt = (c.state === 'up' && c.rtt) ? Math.round(c.rtt) + ' ms' : '—';
const chg = c.statetime ? new Date(c.statetime * 1000).toLocaleString() : '—';
const seen = c.lastbeat ? new Date(c.lastbeat * 1000).toLocaleString() : '—';
html += `<tr>
<td class="key">${escHtml(c.family)}</td>
<td>${escHtml(c.addr || '—')}</td>
<td><span class="${stCls}">${st}</span></td>
<td class="num">${rtt}</td>
<td>${chg}</td>
<td>${seen}</td>
</tr>`;
}
html += `</tbody></table>`;
}
if (data.thresholds === null) {
html += `<div class="info-note">Threshold alerting not configured.</div>`;
} else if (data.thresholds.length === 0) {