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:
@@ -275,6 +275,17 @@
|
||||
{% if summary.critical or summary.warning %}
|
||||
<span class="g-section-badge">{{ (summary.critical or 0) + (summary.warning or 0) }}</span>
|
||||
{% endif %}
|
||||
<div class="g-section-actions">
|
||||
<div class="events-filter-bar">
|
||||
<input type="search" class="lt-input lt-input-sm" id="events-search"
|
||||
placeholder="Filter by target, type, description…" autocomplete="off">
|
||||
<div class="sev-pills">
|
||||
<button type="button" class="pill active" data-sev="">All</button>
|
||||
<button type="button" class="pill" data-sev="critical">Critical</button>
|
||||
<button type="button" class="pill" data-sev="warning">Warning</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="events-table-wrap">
|
||||
{% if events %}
|
||||
@@ -462,5 +473,35 @@
|
||||
document.querySelectorAll('.event-duration[data-first][data-resolved]').forEach(el => {
|
||||
el.textContent = fmtDuration(el.dataset.first, el.dataset.resolved);
|
||||
});
|
||||
|
||||
// ── Events table filter ────────────────────────────────────────
|
||||
let _filterSev = '';
|
||||
|
||||
function applyEventsFilter() {
|
||||
const q = (document.getElementById('events-search')?.value || '').toLowerCase();
|
||||
const tbody = document.querySelector('#events-table tbody');
|
||||
if (!tbody) return;
|
||||
tbody.querySelectorAll('tr').forEach(row => {
|
||||
if (row.children.length < 3) { row.style.display = ''; return; }
|
||||
const sevMatch = !_filterSev || row.classList.contains(`row-${_filterSev}`);
|
||||
const textMatch = !q || row.textContent.toLowerCase().includes(q);
|
||||
row.style.display = (sevMatch && textMatch) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('events-search')?.addEventListener('input', applyEventsFilter);
|
||||
|
||||
document.querySelector('.sev-pills')?.addEventListener('click', e => {
|
||||
const pill = e.target.closest('.pill[data-sev]');
|
||||
if (!pill) return;
|
||||
document.querySelectorAll('.sev-pills .pill').forEach(p => p.classList.remove('active'));
|
||||
pill.classList.add('active');
|
||||
_filterSev = pill.dataset.sev;
|
||||
applyEventsFilter();
|
||||
});
|
||||
|
||||
// Re-apply filter after dynamic table updates
|
||||
new MutationObserver(applyEventsFilter)
|
||||
.observe(document.getElementById('events-table-wrap'), { childList: true, subtree: true });
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user