Remove all inline event handlers; replace with data-action delegation
Lint / Python (flake8) (push) Successful in 39s
Lint / JS (eslint) (push) Successful in 6s
Security / Python Security (bandit) (push) Successful in 50s
Test / Python Tests (pytest) (push) Successful in 51s
Lint / Notify on failure (push) Has been skipped
Lint / Deploy (push) Successful in 2s

- inspector.html: onclick on port blocks, close button, run-diagnostic button,
  and diag-toggle sections all converted to data-action attributes; single
  delegated click listener handles all cases + Escape key closes panel
- links.html: onclick on panel title headers, Collapse All, Expand All
  converted to data-action with delegated listener
- suppressions.html: onsubmit/onchange wired via addEventListener at init
- index.html: onsubmit/onchange on suppress modal form wired at init

No behavioural changes — pure event-handling refactor for TDS compliance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 17:53:48 -04:00
parent c025da85c1
commit 03375ef22f
4 changed files with 42 additions and 15 deletions
+4 -2
View File
@@ -399,11 +399,11 @@
<h3 class="lt-modal-title" id="suppress-modal-title">Suppress Alert</h3> <h3 class="lt-modal-title" id="suppress-modal-title">Suppress Alert</h3>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button> <button type="button" class="lt-modal-close" data-modal-close aria-label="Close"></button>
</div> </div>
<form id="suppress-form" onsubmit="submitSuppress(event)"> <form id="suppress-form">
<div class="lt-modal-body"> <div class="lt-modal-body">
<div class="lt-form-group" style="margin-bottom:12px"> <div class="lt-form-group" style="margin-bottom:12px">
<label class="lt-label" for="sup-type">Target Type</label> <label class="lt-label" for="sup-type">Target Type</label>
<select class="lt-select" id="sup-type" name="target_type" onchange="updateSuppressForm()"> <select class="lt-select" id="sup-type" name="target_type">
<option value="host">Host (all interfaces)</option> <option value="host">Host (all interfaces)</option>
<option value="interface">Specific Interface</option> <option value="interface">Specific Interface</option>
<option value="unifi_device">UniFi Device</option> <option value="unifi_device">UniFi Device</option>
@@ -449,6 +449,8 @@
{% block scripts %} {% block scripts %}
<script> <script>
lt.autoRefresh.start(refreshAll, 30000); lt.autoRefresh.start(refreshAll, 30000);
document.getElementById('suppress-form')?.addEventListener('submit', submitSuppress);
document.getElementById('sup-type')?.addEventListener('change', updateSuppressForm);
function updateEventAges() { function updateEventAges() {
document.querySelectorAll('.event-age[data-ts]').forEach(el => { document.querySelectorAll('.event-age[data-ts]').forEach(el => {
+21 -7
View File
@@ -115,7 +115,7 @@ function portBlockHtml(idx, port, swName, sfpBlock) {
return `<div class="switch-port-block ${state}${sfpCls}" return `<div class="switch-port-block ${state}${sfpCls}"
data-switch="${escHtml(swName)}" data-port-idx="${idx}" data-switch="${escHtml(swName)}" data-port-idx="${idx}"
title="${title}" title="${title}"
onclick="selectPort(this)"><span class="port-num">${numLabel}</span>${speedHtml}${lldpHtml}</div>`; data-action="select-port"><span class="port-num">${numLabel}</span>${speedHtml}${lldpHtml}</div>`;
} }
// ── Chassis legend HTML ────────────────────────────────────────────────── // ── Chassis legend HTML ──────────────────────────────────────────────────
@@ -169,8 +169,8 @@ function renderChassis(swName, sw) {
const lldpHtml = lldpName ? `<span class="port-lldp">${lldpName}</span>` : ''; const lldpHtml = lldpName ? `<span class="port-lldp">${lldpName}</span>` : '';
chassisHtml += `<div class="switch-port-block ${state}${sfpCls}" chassisHtml += `<div class="switch-port-block ${state}${sfpCls}"
data-switch="${escHtml(swName)}" data-port-idx="${idx}" data-switch="${escHtml(swName)}" data-port-idx="${idx}"
title="${title}" title="${title}" aria-label="${title}"
onclick="selectPort(this)"><span class="port-num">${idx}</span>${speedHtml}${lldpHtml}</div>`; data-action="select-port"><span class="port-num">${idx}</span>${speedHtml}${lldpHtml}</div>`;
} }
chassisHtml += '</div>'; chassisHtml += '</div>';
} }
@@ -318,7 +318,7 @@ function renderPanel(swName, idx) {
_apiData.hosts && _apiData.hosts[d.lldp.system_name]); _apiData.hosts && _apiData.hosts[d.lldp.system_name]);
const diagHtml = hasDiagTarget ? ` const diagHtml = hasDiagTarget ? `
<div class="diag-bar"> <div class="diag-bar">
<button class="btn-diag" onclick="runDiagnostic('${escHtml(swName)}', ${idx})">Run Link Diagnostics</button> <button class="btn-diag" data-action="run-diagnostic" data-sw="${escHtml(swName)}" data-idx="${idx}">Run Link Diagnostics</button>
<span class="diag-status" id="diag-status"></span> <span class="diag-status" id="diag-status"></span>
</div> </div>
<div class="diag-results" id="diag-results"></div>` : ''; <div class="diag-results" id="diag-results"></div>` : '';
@@ -330,7 +330,7 @@ function renderPanel(swName, idx) {
<span class="panel-port-name">${escHtml(d.name)}</span>${isUplinkBadge} <span class="panel-port-name">${escHtml(d.name)}</span>${isUplinkBadge}
<div class="panel-meta">${escHtml(swName)} · port #${idx}</div> <div class="panel-meta">${escHtml(swName)} · port #${idx}</div>
</div> </div>
<button class="panel-close" onclick="closePanel()">✕</button> <button class="panel-close" data-action="close-panel" aria-label="Close panel">✕</button>
</div> </div>
<div class="panel-section-title">Link</div> <div class="panel-section-title">Link</div>
@@ -468,6 +468,20 @@ lt.keys.on('Escape', () => {
if (document.getElementById('inspector-panel').classList.contains('open')) closePanel(); if (document.getElementById('inspector-panel').classList.contains('open')) closePanel();
}); });
document.addEventListener('click', e => {
const portBlock = e.target.closest('[data-action="select-port"]');
if (portBlock) { selectPort(portBlock); return; }
const closeBtn = e.target.closest('[data-action="close-panel"]');
if (closeBtn) { closePanel(); return; }
const diagBtn = e.target.closest('[data-action="run-diagnostic"]');
if (diagBtn) { runDiagnostic(diagBtn.dataset.sw, parseInt(diagBtn.dataset.idx, 10)); return; }
const toggleDiag = e.target.closest('[data-action="toggle-diag"]');
if (toggleDiag) { toggleDiag.parentElement.classList.toggle('diag-open'); return; }
});
// ── Link Diagnostics ───────────────────────────────────────────────── // ── Link Diagnostics ─────────────────────────────────────────────────
let _diagPollTimer = null; let _diagPollTimer = null;
@@ -635,7 +649,7 @@ function renderDiagnosticResults(d, container) {
}).join(''); }).join('');
nicStatHtml = ` nicStatHtml = `
<div class="diag-section diag-collapsible"> <div class="diag-section diag-collapsible">
<div class="diag-section-header diag-toggle" onclick="this.parentElement.classList.toggle('diag-open')"> <div class="diag-section-header diag-toggle" data-action="toggle-diag">
ethtool -S (NIC stats) <span class="diag-toggle-hint">[expand]</span> ethtool -S (NIC stats) <span class="diag-toggle-hint">[expand]</span>
</div> </div>
<div class="diag-section-body"> <div class="diag-section-body">
@@ -711,7 +725,7 @@ function renderDiagnosticResults(d, container) {
}).join(''); }).join('');
dmesgHtml = ` dmesgHtml = `
<div class="diag-section diag-collapsible"> <div class="diag-section diag-collapsible">
<div class="diag-section-header diag-toggle" onclick="this.parentElement.classList.toggle('diag-open')"> <div class="diag-section-header diag-toggle" data-action="toggle-diag">
Kernel Events (dmesg) <span class="diag-toggle-hint">[expand]</span> Kernel Events (dmesg) <span class="diag-toggle-hint">[expand]</span>
</div> </div>
<div class="diag-section-body"> <div class="diag-section-body">
+12 -4
View File
@@ -327,7 +327,7 @@ function renderUnifiSwitches(unifiSwitches, dataUpdated) {
return ` return `
<div class="link-host-panel" id="panel-${CSS.escape(swName)}"> <div class="link-host-panel" id="panel-${CSS.escape(swName)}">
<div class="link-host-title" onclick="togglePanel(this.closest('.link-host-panel'))"> <div class="link-host-title" data-action="toggle-panel">
<span class="link-host-name">${escHtml(swName)}</span> <span class="link-host-name">${escHtml(swName)}</span>
<span class="link-host-ip">${escHtml(sw.ip || '')}</span> <span class="link-host-ip">${escHtml(sw.ip || '')}</span>
<span class="link-host-upd">${escHtml(sw.model || '')}${updStr ? ' · ' + updStr : ''}${poeLoad}</span> <span class="link-host-upd">${escHtml(sw.model || '')}${updStr ? ' · ' + updStr : ''}${poeLoad}</span>
@@ -415,8 +415,8 @@ function renderLinks(data) {
parts.push(buildLinkSummary(hosts, unifiSwitches)); parts.push(buildLinkSummary(hosts, unifiSwitches));
parts.push(`<div class="link-collapse-bar"> parts.push(`<div class="link-collapse-bar">
<button class="lt-btn lt-btn-ghost lt-btn-sm" onclick="collapseAll()">Collapse All</button> <button class="lt-btn lt-btn-ghost lt-btn-sm" data-action="collapse-all">Collapse All</button>
<button class="lt-btn lt-btn-ghost lt-btn-sm" onclick="expandAll()">Expand All</button> <button class="lt-btn lt-btn-ghost lt-btn-sm" data-action="expand-all">Expand All</button>
</div>`); </div>`);
parts.push('<div class="link-host-list">'); parts.push('<div class="link-host-list">');
@@ -432,7 +432,7 @@ function renderLinks(data) {
parts.push(` parts.push(`
<div class="link-host-panel" id="panel-${CSS.escape(hostname)}"> <div class="link-host-panel" id="panel-${CSS.escape(hostname)}">
<div class="link-host-title" onclick="togglePanel(this.closest('.link-host-panel'))"> <div class="link-host-title" data-action="toggle-panel">
<span class="link-host-name">${escHtml(hostname)}</span> <span class="link-host-name">${escHtml(hostname)}</span>
<span class="link-host-ip">${escHtml(ip)}</span> <span class="link-host-ip">${escHtml(ip)}</span>
<span class="link-host-upd">${updStr}</span> <span class="link-host-upd">${updStr}</span>
@@ -511,5 +511,13 @@ async function loadLinks() {
loadLinks(); loadLinks();
lt.autoRefresh.start(loadLinks, 60000); lt.autoRefresh.start(loadLinks, 60000);
document.addEventListener('click', e => {
const toggleTitle = e.target.closest('[data-action="toggle-panel"]');
if (toggleTitle) { togglePanel(toggleTitle.closest('.link-host-panel')); return; }
if (e.target.closest('[data-action="collapse-all"]')) { collapseAll(); return; }
if (e.target.closest('[data-action="expand-all"]')) { expandAll(); return; }
});
</script> </script>
{% endblock %} {% endblock %}
+5 -2
View File
@@ -15,11 +15,11 @@
</div> </div>
<div class="lt-card"> <div class="lt-card">
<div class="lt-card-body"> <div class="lt-card-body">
<form id="create-suppression-form" onsubmit="createSuppression(event)"> <form id="create-suppression-form">
<div class="form-row"> <div class="form-row">
<div class="lt-form-group"> <div class="lt-form-group">
<label class="lt-label" for="s-type">Target Type <span class="required">*</span></label> <label class="lt-label" for="s-type">Target Type <span class="required">*</span></label>
<select class="lt-select" id="s-type" name="target_type" onchange="onTypeChange()"> <select class="lt-select" id="s-type" name="target_type">
<option value="host">Host (all interfaces)</option> <option value="host">Host (all interfaces)</option>
<option value="interface">Specific Interface</option> <option value="interface">Specific Interface</option>
<option value="unifi_device">UniFi Device</option> <option value="unifi_device">UniFi Device</option>
@@ -294,6 +294,9 @@
} }
} }
document.getElementById('s-type')?.addEventListener('change', onTypeChange);
document.getElementById('create-suppression-form')?.addEventListener('submit', createSuppression);
document.addEventListener('click', e => { document.addEventListener('click', e => {
const pill = e.target.closest('#create-suppression-form .pill[data-duration]'); const pill = e.target.closest('#create-suppression-form .pill[data-duration]');
if (pill) { if (pill) {