TDS polish: lt-frame tables, links search toolbar, dead CSS cleanup
Lint / Python (flake8) (push) Successful in 56s
Lint / JS (eslint) (push) Successful in 8s
Security / Python Security (bandit) (push) Failing after 40s
Test / Python Tests (pytest) (push) Successful in 50s
Lint / Notify on failure (push) Has been skipped
Lint / Deploy (push) Successful in 3s
Lint / Python (flake8) (push) Successful in 56s
Lint / JS (eslint) (push) Successful in 8s
Security / Python Security (bandit) (push) Failing after 40s
Test / Python Tests (pytest) (push) Successful in 50s
Lint / Notify on failure (push) Has been skipped
Lint / Deploy (push) Successful in 3s
- index.html: wrap UniFi devices table in lt-frame with section header - links.html: add static lt-toolbar with lt-search filter and collapse controls above the dynamic container; remove collapse bar from renderLinks() since it's now static; add applyLinksSearch() to filter host/switch panels by name as user types - suppressions.html: wrap Available Targets section in lt-frame - style.css: remove unused .link-summary-panel and related rules (replaced by lt-stats-grid in previous commit) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -724,20 +724,6 @@
|
|||||||
background: linear-gradient(90deg, transparent, var(--cyan), transparent);
|
background: linear-gradient(90deg, transparent, var(--cyan), transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link health summary */
|
|
||||||
.link-summary-panel {
|
|
||||||
background: var(--bg2);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
padding: 12px 16px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.link-summary-panel.link-summary-has-alerts { border-color: var(--amber); }
|
|
||||||
.link-summary-grid { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-end; }
|
|
||||||
.link-summary-stat { min-width: 80px; }
|
|
||||||
.link-summary-stat.lss-alert .lss-label { color: var(--amber); }
|
|
||||||
.lss-label { display: block; font-size: .62em; color: var(--text-muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 2px; }
|
|
||||||
.lss-value { font-size: 1.2em; font-weight: bold; color: var(--text); }
|
|
||||||
.lss-sub { font-size: .7em; color: var(--text-muted); font-weight: normal; }
|
|
||||||
|
|
||||||
.link-loading { padding: 20px; text-align: center; color: var(--text-muted); font-size: .8em; }
|
.link-loading { padding: 20px; text-align: center; color: var(--text-muted); font-size: .8em; }
|
||||||
.link-loading::after { content: ' ...'; animation: blink 1s step-end infinite; }
|
.link-loading::after { content: ' ...'; animation: blink 1s step-end infinite; }
|
||||||
|
|||||||
+43
-38
@@ -270,44 +270,49 @@
|
|||||||
<div class="g-section-header">
|
<div class="g-section-header">
|
||||||
<h2 class="g-section-title">UniFi Devices</h2>
|
<h2 class="g-section-title">UniFi Devices</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="lt-table-wrap">
|
<div class="lt-frame">
|
||||||
<table class="lt-table" id="unifi-table">
|
<span class="lt-frame-bl">╚</span>
|
||||||
<caption class="lt-sr-only">UniFi network devices</caption>
|
<span class="lt-frame-br">╝</span>
|
||||||
<thead>
|
<div class="lt-section-header">Device Inventory</div>
|
||||||
<tr>
|
<div class="lt-table-wrap">
|
||||||
<th>Status</th>
|
<table class="lt-table" id="unifi-table">
|
||||||
<th>Name</th>
|
<caption class="lt-sr-only">UniFi network devices</caption>
|
||||||
<th>Type</th>
|
<thead>
|
||||||
<th>Model</th>
|
<tr>
|
||||||
<th>IP</th>
|
<th>Status</th>
|
||||||
<th>Actions</th>
|
<th>Name</th>
|
||||||
</tr>
|
<th>Type</th>
|
||||||
</thead>
|
<th>Model</th>
|
||||||
<tbody>
|
<th>IP</th>
|
||||||
{% for d in snapshot.unifi %}
|
<th>Actions</th>
|
||||||
<tr class="{% if not d.connected %}row-critical{% endif %}">
|
</tr>
|
||||||
<td>
|
</thead>
|
||||||
<span class="dot-{{ 'up' if d.connected else 'down' }}"></span>
|
<tbody>
|
||||||
{{ 'ONLINE' if d.connected else 'OFFLINE' }}
|
{% for d in snapshot.unifi %}
|
||||||
</td>
|
<tr class="{% if not d.connected %}row-critical{% endif %}">
|
||||||
<td><strong>{{ d.name }}</strong></td>
|
<td>
|
||||||
<td>{{ d.type }}</td>
|
<span class="dot-{{ 'up' if d.connected else 'down' }}"></span>
|
||||||
<td>{{ d.model }}</td>
|
{{ 'ONLINE' if d.connected else 'OFFLINE' }}
|
||||||
<td>{{ d.ip }}</td>
|
</td>
|
||||||
<td>
|
<td><strong>{{ d.name }}</strong></td>
|
||||||
{% if not d.connected %}
|
<td>{{ d.type }}</td>
|
||||||
<button class="lt-btn lt-btn-ghost lt-btn-sm btn-suppress"
|
<td>{{ d.model }}</td>
|
||||||
data-sup-type="unifi_device"
|
<td>{{ d.ip }}</td>
|
||||||
data-sup-name="{{ d.name }}"
|
<td>
|
||||||
data-sup-detail="">
|
{% if not d.connected %}
|
||||||
🔕 Suppress
|
<button class="lt-btn lt-btn-ghost lt-btn-sm btn-suppress"
|
||||||
</button>
|
data-sup-type="unifi_device"
|
||||||
{% endif %}
|
data-sup-name="{{ d.name }}"
|
||||||
</td>
|
data-sup-detail="">
|
||||||
</tr>
|
🔕 Suppress
|
||||||
{% endfor %}
|
</button>
|
||||||
</tbody>
|
{% endif %}
|
||||||
</table>
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
+26
-4
@@ -13,6 +13,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="lt-toolbar" id="links-toolbar" style="display:none">
|
||||||
|
<div class="lt-toolbar-left">
|
||||||
|
<div class="lt-search">
|
||||||
|
<input type="search" class="lt-input lt-search-input" id="links-search"
|
||||||
|
placeholder="Filter by host or switch name…" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="lt-toolbar-right">
|
||||||
|
<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" data-action="expand-all">Expand All</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="links-container">
|
<div id="links-container">
|
||||||
<div class="link-loading">Loading link statistics</div>
|
<div class="link-loading">Loading link statistics</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -439,10 +452,6 @@ function renderLinks(data) {
|
|||||||
const parts = [];
|
const parts = [];
|
||||||
|
|
||||||
parts.push(buildLinkSummary(hosts, unifiSwitches));
|
parts.push(buildLinkSummary(hosts, unifiSwitches));
|
||||||
parts.push(`<div class="link-collapse-bar">
|
|
||||||
<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" data-action="expand-all">Expand All</button>
|
|
||||||
</div>`);
|
|
||||||
parts.push('<div class="link-host-list">');
|
parts.push('<div class="link-host-list">');
|
||||||
|
|
||||||
for (const [hostname, ifaces] of Object.entries(hosts)) {
|
for (const [hostname, ifaces] of Object.entries(hosts)) {
|
||||||
@@ -471,6 +480,17 @@ function renderLinks(data) {
|
|||||||
parts.push('</div>');
|
parts.push('</div>');
|
||||||
document.getElementById('links-container').innerHTML = parts.join('');
|
document.getElementById('links-container').innerHTML = parts.join('');
|
||||||
restoreCollapseState();
|
restoreCollapseState();
|
||||||
|
document.getElementById('links-toolbar').style.display = '';
|
||||||
|
applyLinksSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Host/switch search filter ─────────────────────────────────────
|
||||||
|
function applyLinksSearch() {
|
||||||
|
const q = (document.getElementById('links-search')?.value || '').trim().toLowerCase();
|
||||||
|
document.querySelectorAll('.link-host-panel').forEach(panel => {
|
||||||
|
const text = (panel.querySelector('.link-host-name')?.textContent || '').toLowerCase();
|
||||||
|
panel.style.display = (!q || text.includes(q)) ? '' : 'none';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseAll() {
|
function collapseAll() {
|
||||||
@@ -552,5 +572,7 @@ document.addEventListener('click', e => {
|
|||||||
if (e.target.closest('[data-action="collapse-all"]')) { collapseAll(); return; }
|
if (e.target.closest('[data-action="collapse-all"]')) { collapseAll(); return; }
|
||||||
if (e.target.closest('[data-action="expand-all"]')) { expandAll(); return; }
|
if (e.target.closest('[data-action="expand-all"]')) { expandAll(); return; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('links-search')?.addEventListener('input', applyLinksSearch);
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+18
-11
@@ -184,20 +184,27 @@
|
|||||||
<div class="g-section-header">
|
<div class="g-section-header">
|
||||||
<h2 class="g-section-title">Available Targets</h2>
|
<h2 class="g-section-title">Available Targets</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="targets-grid">
|
<div class="lt-frame">
|
||||||
{% for name, host in snapshot.hosts.items() %}
|
<span class="lt-frame-bl">╚</span>
|
||||||
<div class="target-card">
|
<span class="lt-frame-br">╝</span>
|
||||||
<div class="target-name">{{ name }}</div>
|
<div class="lt-section-header">Host & Interface Reference</div>
|
||||||
<div class="target-type">{{ 'Proxmox Host (prometheus)' if host.source == 'prometheus' else 'Ping-only host' }}</div>
|
<div style="padding:12px 14px">
|
||||||
{% if host.interfaces %}
|
<div class="targets-grid">
|
||||||
<div class="target-ifaces">
|
{% for name, host in snapshot.hosts.items() %}
|
||||||
{% for iface in host.interfaces.keys() | sort %}
|
<div class="target-card">
|
||||||
<code class="iface-chip">{{ iface }}</code>
|
<div class="target-name">{{ name }}</div>
|
||||||
|
<div class="target-type">{{ 'Proxmox Host (prometheus)' if host.source == 'prometheus' else 'Ping-only host' }}</div>
|
||||||
|
{% if host.interfaces %}
|
||||||
|
<div class="target-ifaces">
|
||||||
|
{% for iface in host.interfaces.keys() | sort %}
|
||||||
|
<code class="iface-chip">{{ iface }}</code>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user