|
|
|
@@ -259,7 +259,7 @@ function renderPanel(swName, idx) {
|
|
|
|
|
const poeCurStr = (d.poe_power != null && d.poe_power > 0) ? ` / draw <span class="val-amber">${d.poe_power.toFixed(1)}W</span>` : '';
|
|
|
|
|
poeHtml = `
|
|
|
|
|
<div class="lt-divider"><span class="lt-divider-label">PoE</span></div>
|
|
|
|
|
<div class="panel-row"><span class="panel-label">Class</span><span class="panel-val">class ${d.poe_class}${poeMaxStr}</span></div>
|
|
|
|
|
<div class="panel-row"><span class="panel-label">Class</span><span class="panel-val">class ${escHtml(String(d.poe_class))}${poeMaxStr}</span></div>
|
|
|
|
|
${d.poe_power != null ? `<div class="panel-row"><span class="panel-label">Draw</span><span class="panel-val">${d.poe_power > 0 ? `<span class="val-amber">${d.poe_power.toFixed(1)}W</span>` : '0W'}</span></div>` : ''}
|
|
|
|
|
${d.poe_mode ? `<div class="panel-row"><span class="panel-label">Mode</span><span class="panel-val">${escHtml(d.poe_mode)}</span></div>` : ''}`;
|
|
|
|
|
}
|
|
|
|
@@ -487,7 +487,13 @@ document.addEventListener('click', e => {
|
|
|
|
|
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; }
|
|
|
|
|
if (toggleDiag) {
|
|
|
|
|
const section = toggleDiag.parentElement;
|
|
|
|
|
const nowOpen = section.classList.toggle('diag-open');
|
|
|
|
|
const hint = toggleDiag.querySelector('.diag-toggle-hint');
|
|
|
|
|
if (hint) hint.textContent = nowOpen ? '[collapse]' : '[expand]';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Link Diagnostics ─────────────────────────────────────────────────
|
|
|
|
@@ -510,7 +516,10 @@ function runDiagnostic(swName, portIdx) {
|
|
|
|
|
pollDiagnostic(resp.job_id, statusEl, resultsEl);
|
|
|
|
|
})
|
|
|
|
|
.catch(e => {
|
|
|
|
|
statusEl.textContent = 'Error: ' + (e.message || 'Request failed');
|
|
|
|
|
const msg = (e && e.status === 429)
|
|
|
|
|
? 'Rate limit reached — max 5 diagnostics per minute. Please wait.'
|
|
|
|
|
: 'Error: ' + (e && e.message || 'Request failed');
|
|
|
|
|
statusEl.textContent = msg;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -520,7 +529,13 @@ function pollDiagnostic(jobId, statusEl, resultsEl) {
|
|
|
|
|
attempts++;
|
|
|
|
|
if (attempts > 120) { // 2min timeout
|
|
|
|
|
clearInterval(_diagPollTimer);
|
|
|
|
|
statusEl.textContent = 'Timed out waiting for results.';
|
|
|
|
|
_diagPollTimer = null;
|
|
|
|
|
statusEl.innerHTML = 'Timed out waiting for results. '
|
|
|
|
|
+ '<button class="lt-btn lt-btn-ghost lt-btn-sm" id="diag-retry-btn">Retry</button>';
|
|
|
|
|
document.getElementById('diag-retry-btn')?.addEventListener('click', () => {
|
|
|
|
|
const sel = document.querySelector('.switch-port-block.selected');
|
|
|
|
|
if (sel) runDiagnostic(sel.dataset.switch, parseInt(sel.dataset.portIdx));
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lt.api.get(`/api/diagnose/${jobId}`)
|
|
|
|
@@ -535,7 +550,12 @@ function pollDiagnostic(jobId, statusEl, resultsEl) {
|
|
|
|
|
.catch(() => {
|
|
|
|
|
clearInterval(_diagPollTimer);
|
|
|
|
|
_diagPollTimer = null;
|
|
|
|
|
statusEl.textContent = 'Error: lost connection while collecting diagnostics.';
|
|
|
|
|
statusEl.innerHTML = 'Error: lost connection while collecting diagnostics. '
|
|
|
|
|
+ '<button class="lt-btn lt-btn-ghost lt-btn-sm" id="diag-retry-btn">Retry</button>';
|
|
|
|
|
document.getElementById('diag-retry-btn')?.addEventListener('click', () => {
|
|
|
|
|
const sel = document.querySelector('.switch-port-block.selected');
|
|
|
|
|
if (sel) runDiagnostic(sel.dataset.switch, parseInt(sel.dataset.portIdx));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}, 2000);
|
|
|
|
|
}
|
|
|
|
|