Fix field name mismatches, add events filter, in-place suppression refresh
Lint / Python (flake8) (push) Failing after 50s
Lint / JS (eslint) (push) Successful in 7s
Test / Python Tests (pytest) (push) Successful in 51s
Lint / Notify on failure (push) Successful in 2s
Lint / Deploy (push) Has been skipped
Security / Python Security (bandit) (push) Failing after 59s
Lint / Python (flake8) (push) Failing after 50s
Lint / JS (eslint) (push) Successful in 7s
Test / Python Tests (pytest) (push) Successful in 51s
Lint / Notify on failure (push) Successful in 2s
Lint / Deploy (push) Has been skipped
Security / Python Security (bandit) (push) Failing after 59s
- links.html: fix all field name bugs (auto_negotiation→autoneg, full_duplex, tx/rx_errors/drops_per_sec→_rate, tx/rx_bytes_per_sec→_rate, poe_total_w/poe_max_w computed from ports, renderUnifiSwitches uses top-level updated timestamp) - suppressions.html: in-place DOM refresh after create/remove (no page reload), datalist autocomplete for target names, form reset after submit - inspector.html: ESC key closes detail panel via lt.keys.on - index.html: events filter bar with search input + severity pills (All/Critical/Warning), MutationObserver re-applies filter after dynamic updates - style.css: g-section-actions, events-filter-bar, sev-pills layout - app.js/db.py/monitor.py: carry forward prior session fixes (Promise.allSettled, daemon_ok, stale connection handling, double Prometheus call, self.cfg fix) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+27
-11
@@ -7,7 +7,10 @@
|
||||
const _fetch = window.fetch;
|
||||
window.fetch = async function (...args) {
|
||||
const resp = await _fetch(...args);
|
||||
if (resp.status === 401) window.location.reload();
|
||||
if (resp.status === 401) {
|
||||
window.location.reload();
|
||||
throw new Error('Session expired — reloading');
|
||||
}
|
||||
return resp;
|
||||
};
|
||||
})();
|
||||
@@ -29,28 +32,41 @@ function _toIso(s) {
|
||||
|
||||
// ── Dashboard auto-refresh ────────────────────────────────────────────
|
||||
async function refreshAll() {
|
||||
const refreshBtn = document.querySelector('[data-action="refresh"]');
|
||||
if (refreshBtn) refreshBtn.classList.add('is-loading');
|
||||
try {
|
||||
const [net, status] = await Promise.all([
|
||||
const [netResult, statusResult] = await Promise.allSettled([
|
||||
lt.api.get('/api/network'),
|
||||
lt.api.get('/api/status'),
|
||||
]);
|
||||
updateHostGrid(net.hosts || {});
|
||||
updateUnifiTable(net.unifi || []);
|
||||
updateEventsTable(status.events || [], status.total_active);
|
||||
updateStatusBar(status.summary || {}, status.last_check || '');
|
||||
updateTopology(net.hosts || {});
|
||||
} catch (e) {
|
||||
console.warn('Refresh failed:', e);
|
||||
if (netResult.status === 'fulfilled') {
|
||||
const net = netResult.value;
|
||||
updateHostGrid(net.hosts || {});
|
||||
updateUnifiTable(net.unifi || []);
|
||||
updateTopology(net.hosts || {});
|
||||
} else {
|
||||
console.warn('Network API failed:', netResult.reason);
|
||||
}
|
||||
if (statusResult.status === 'fulfilled') {
|
||||
const status = statusResult.value;
|
||||
updateEventsTable(status.events || [], status.total_active);
|
||||
updateStatusBar(status.summary || {}, status.last_check || '', status.daemon_ok);
|
||||
} else {
|
||||
console.warn('Status API failed:', statusResult.reason);
|
||||
}
|
||||
} finally {
|
||||
if (refreshBtn) refreshBtn.classList.remove('is-loading');
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusBar(summary, lastCheck) {
|
||||
function updateStatusBar(summary, lastCheck, daemonOk) {
|
||||
const bar = document.querySelector('.status-chips');
|
||||
if (!bar) return;
|
||||
const chips = [];
|
||||
if (daemonOk === false) chips.push('<span class="chip chip-critical">⚠ MONITOR OFFLINE</span>');
|
||||
if (summary.critical) chips.push(`<span class="chip chip-critical">● ${summary.critical} CRITICAL</span>`);
|
||||
if (summary.warning) chips.push(`<span class="chip chip-warning">● ${summary.warning} WARNING</span>`);
|
||||
if (!summary.critical && !summary.warning) chips.push('<span class="chip chip-ok">✔ ALL SYSTEMS NOMINAL</span>');
|
||||
if (!summary.critical && !summary.warning && daemonOk !== false) chips.push('<span class="chip chip-ok">✔ ALL SYSTEMS NOMINAL</span>');
|
||||
bar.innerHTML = chips.join('');
|
||||
|
||||
const lc = document.getElementById('last-check');
|
||||
|
||||
@@ -40,6 +40,31 @@
|
||||
--glow-xl: 0 0 8px var(--accent-green), 0 0 20px rgba(0,255,136,.5);
|
||||
}
|
||||
|
||||
/* ── Light theme overrides for dim/glow variables ────────────────── */
|
||||
[data-theme="light"] {
|
||||
--green-dim: rgba(0,160,80,.08);
|
||||
--green-muted: rgba(0,160,80,.45);
|
||||
--amber-dim: rgba(180,120,0,.07);
|
||||
--cyan-dim: rgba(0,140,180,.08);
|
||||
--red-dim: rgba(200,30,60,.06);
|
||||
--orange-dim: rgba(180,80,0,.06);
|
||||
--glow: none;
|
||||
--glow-amber: none;
|
||||
--glow-red: none;
|
||||
--glow-cyan: none;
|
||||
--glow-xl: none;
|
||||
}
|
||||
|
||||
/* ── Refresh button loading state ────────────────────────────────── */
|
||||
[data-action="refresh"].is-loading {
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
cursor: wait;
|
||||
}
|
||||
[data-action="refresh"].is-loading::after {
|
||||
content: '…';
|
||||
}
|
||||
|
||||
/* ── Animations used by custom components ─────────────────────────── */
|
||||
@keyframes pulse-red {
|
||||
0%,100% { box-shadow: 0 0 0 0 rgba(255,45,85,.5); }
|
||||
@@ -85,6 +110,10 @@
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 1px 7px;
|
||||
}
|
||||
.g-section-actions { margin-left: auto; }
|
||||
.events-filter-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.events-filter-bar .lt-input-sm { width: 220px; }
|
||||
.sev-pills { display: flex; gap: 4px; }
|
||||
.g-page-header { margin-bottom: 20px; }
|
||||
.g-page-title {
|
||||
font-size: 1em;
|
||||
|
||||
Reference in New Issue
Block a user