Dynamic resolved count, host search filter, lt-divider for UniFi section
- db.py: add resolved_24h to get_status_summary() so each /api/status poll carries the fresh 24h resolved count - app.js: wire stat-resolved-val to update from summary.resolved_24h so the Resolved 24h card stays accurate after auto-refresh - index.html: add lt-toolbar/lt-search above host grid for quick client-side host filtering by name - links.html: replace custom unifi-section-header div with lt-divider - style.css: remove unused .unifi-section-header rules Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -222,10 +222,17 @@ def get_status_summary() -> dict:
|
||||
WHERE resolved_at IS NULL GROUP BY severity"""
|
||||
)
|
||||
counts = {r['severity']: r['cnt'] for r in cur.fetchall()}
|
||||
cur.execute(
|
||||
"""SELECT COUNT(*) as cnt FROM network_events
|
||||
WHERE resolved_at IS NOT NULL
|
||||
AND resolved_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)"""
|
||||
)
|
||||
resolved_24h = cur.fetchone()['cnt']
|
||||
return {
|
||||
'critical': counts.get('critical', 0),
|
||||
'warning': counts.get('warning', 0),
|
||||
'info': counts.get('info', 0),
|
||||
'resolved_24h': resolved_24h,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -92,8 +92,10 @@ function updateStatusBar(summary, lastCheck, daemonOk) {
|
||||
// Update stat cards
|
||||
const scCrit = document.getElementById('stat-critical-val');
|
||||
const scWarn = document.getElementById('stat-warning-val');
|
||||
const scRes = document.getElementById('stat-resolved-val');
|
||||
if (scCrit) scCrit.textContent = critCount;
|
||||
if (scWarn) scWarn.textContent = warnCount;
|
||||
if (scRes && summary.resolved_24h != null) scRes.textContent = summary.resolved_24h;
|
||||
const statCritCard = document.getElementById('stat-critical');
|
||||
if (statCritCard) statCritCard.classList.toggle('lt-stat-card--alert', critCount > 0);
|
||||
|
||||
|
||||
@@ -706,23 +706,6 @@
|
||||
.poe-bar-warn { background: var(--amber); }
|
||||
.poe-bar-crit { background: var(--red); }
|
||||
|
||||
/* UniFi section divider */
|
||||
.unifi-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 24px 0 12px;
|
||||
color: var(--cyan);
|
||||
font-size: .75em;
|
||||
letter-spacing: .1em;
|
||||
}
|
||||
.unifi-section-header::before,
|
||||
.unifi-section-header::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--cyan), transparent);
|
||||
}
|
||||
|
||||
|
||||
.link-loading { padding: 20px; text-align: center; color: var(--text-muted); font-size: .8em; }
|
||||
|
||||
@@ -208,6 +208,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Host cards -->
|
||||
<div class="lt-toolbar" style="margin-bottom:10px" id="host-toolbar">
|
||||
<div class="lt-toolbar-left">
|
||||
<div class="lt-search">
|
||||
<input type="search" class="lt-input lt-search-input" id="host-search"
|
||||
placeholder="Filter hosts…" autocomplete="off" style="width:180px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="host-grid" id="host-grid">
|
||||
{% for name, host in snapshot.hosts.items() %}
|
||||
{% set suppressed = suppressions | selectattr('target_name', 'equalto', name) | list %}
|
||||
@@ -568,6 +576,15 @@
|
||||
new MutationObserver(applyEventsFilter)
|
||||
.observe(document.getElementById('events-table-wrap'), { childList: true, subtree: true });
|
||||
|
||||
// Host grid search filter
|
||||
document.getElementById('host-search')?.addEventListener('input', function() {
|
||||
const q = this.value.trim().toLowerCase();
|
||||
document.querySelectorAll('#host-grid .host-card').forEach(card => {
|
||||
const name = (card.dataset.host || '').toLowerCase();
|
||||
card.style.display = (!q || name.includes(q)) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Stat card clicks — filter events table by severity
|
||||
document.querySelectorAll('.lt-stat-card[data-stat-filter]').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
|
||||
@@ -358,7 +358,7 @@ function renderUnifiSwitches(unifiSwitches, dataUpdated) {
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
return `<div class="unifi-section-header">UNIFI SWITCH PORTS</div>${html}`;
|
||||
return `<div class="lt-divider" style="margin:20px 0 12px"><span class="lt-divider-label" style="color:var(--cyan);letter-spacing:.1em">UNIFI SWITCH PORTS</span></div>${html}`;
|
||||
}
|
||||
|
||||
// ── Panel collapse / expand ───────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user