feat: link health summary, recently resolved panel, event duration
- dashboard: pass recent_resolved (last 24h, limit 10) to index template; render "Recently Resolved" section showing type, target, resolved time, and calculated duration (first_seen → resolved_at) - dashboard: event-age spans now also update via setInterval; duration shown for resolved events (e.g. "2h 15m") - links page: link health summary panel shows server iface count, error/flap counts, switch port up/down, PoE total draw/capacity bar; only shows problematic stats if non-zero; shows "All OK ✔" when clean - style.css: new classes for summary panel, resolved row/badge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -250,6 +250,44 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Recently Resolved (last 24h) ───────────────────────────────── -->
|
||||
{% if recent_resolved %}
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">Recently Resolved</h2>
|
||||
<span class="section-badge section-badge-resolved">{{ recent_resolved | length }} in last 24h</span>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sev</th>
|
||||
<th>Type</th>
|
||||
<th>Target</th>
|
||||
<th>Detail</th>
|
||||
<th>Resolved</th>
|
||||
<th>Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in recent_resolved %}
|
||||
<tr class="row-resolved">
|
||||
<td><span class="badge badge-resolved">{{ e.severity }}</span></td>
|
||||
<td>{{ e.event_type | replace('_', ' ') }}</td>
|
||||
<td><strong>{{ e.target_name }}</strong></td>
|
||||
<td>{{ e.target_detail or '–' }}</td>
|
||||
<td class="ts-cell">
|
||||
<span class="event-age" data-ts="{{ e.resolved_at }}">{{ e.resolved_at }}</span>
|
||||
</td>
|
||||
<td class="ts-cell event-duration" data-first="{{ e.first_seen }}" data-resolved="{{ e.resolved_at }}">–</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- ── Quick-suppress modal ─────────────────────────────────────────── -->
|
||||
<div id="suppress-modal" class="modal-overlay" style="display:none">
|
||||
<div class="modal">
|
||||
@@ -326,5 +364,21 @@
|
||||
|
||||
updateEventAges();
|
||||
setInterval(updateEventAges, 60000);
|
||||
|
||||
// ── Event duration (resolved_at - first_seen) ──────────────────
|
||||
function fmtDuration(firstTs, resolvedTs) {
|
||||
if (!firstTs || !resolvedTs) return '–';
|
||||
const parse = s => new Date(s.replace(' UTC', 'Z').replace(' ', 'T'));
|
||||
const secs = Math.floor((parse(resolvedTs) - parse(firstTs)) / 1000);
|
||||
if (secs < 0) return '–';
|
||||
if (secs < 60) return `${secs}s`;
|
||||
if (secs < 3600) return `${Math.floor(secs/60)}m`;
|
||||
if (secs < 86400) return `${Math.floor(secs/3600)}h ${Math.floor((secs%3600)/60)}m`;
|
||||
return `${Math.floor(secs/86400)}d`;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.event-duration[data-first][data-resolved]').forEach(el => {
|
||||
el.textContent = fmtDuration(el.dataset.first, el.dataset.resolved);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user