Add LDAP avatar photos, UX polish, and TDS component upgrades
Lint / Python (flake8) (push) Successful in 1m13s
Lint / JS (eslint) (push) Successful in 9s
Security / Python Security (bandit) (push) Failing after 45s
Test / Python Tests (pytest) (push) Successful in 57s
Lint / Notify on failure (push) Has been skipped
Lint / Deploy (push) Successful in 5s
Lint / Python (flake8) (push) Successful in 1m13s
Lint / JS (eslint) (push) Successful in 9s
Security / Python Security (bandit) (push) Failing after 45s
Test / Python Tests (pytest) (push) Successful in 57s
Lint / Notify on failure (push) Has been skipped
Lint / Deploy (push) Successful in 5s
- Add /api/avatar endpoint querying lldap for user jpegPhoto; disk cache with sentinel pattern avoids repeat LDAP hits for users without photos - Add ldap3 dependency and ldap config block to config.json - Wire lt-avatar img overlay in base.html with capture-phase error fallback (lt-avatar-img-err) to reveal initials when image is absent - Fix lt-avatar CSS shim: position:relative + absolute inset on img (local base.css was missing these; added to style.css) - Replace all empty-state paragraphs with proper lt-empty-state markup (icon + title + body) across index, suppressions, inspector, app.js - Add lt-spinner--cyan next to refresh button; shows during refreshAll() - Replace inspector panel-section-title with lt-divider throughout - Add data-tooltip attributes to SFP DOM metrics, TX/RX/Carrier/Duplex/ Auto-neg/Error labels in links.html and inspector panel - Add tooltips to events table column headers (Sev, First Seen, Failures) - Fix links.html host panel timestamp (was reading sample.updated which is always undefined; now uses data.updated) - Fix UniFi status text casing (Online→ONLINE to match server render) - Remove dead topo-status-* class manipulation from updateTopology() - Always render alert-count-badge; toggle display:none when count is 0 - Fix double UniFi get_devices() call in monitor.py run loop - Fix chip-critical animation (was using green pulse-glow; now red) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+93
-22
@@ -5,6 +5,12 @@
|
||||
This file adds only gandalf-specific components.
|
||||
══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── lt-avatar image overlay (base.css compat shim) ───────────────── */
|
||||
/* Older base.css missing position:relative + position:absolute on img */
|
||||
.lt-avatar { position: relative; }
|
||||
.lt-avatar img { position: absolute; inset: 0; }
|
||||
.lt-avatar img.lt-avatar-img-err { display: none; }
|
||||
|
||||
/* ── Variable aliases bridging to base.css palette ────────────────── */
|
||||
:root {
|
||||
/* Short names used throughout custom components */
|
||||
@@ -54,6 +60,28 @@
|
||||
--glow-cyan: none;
|
||||
--glow-xl: none;
|
||||
}
|
||||
[data-theme="light"] .topology {
|
||||
background-image: radial-gradient(circle, rgba(0,100,160,0.07) 1px, transparent 1px);
|
||||
}
|
||||
[data-theme="light"] .topo-vc-label {
|
||||
background: rgba(235,238,242,.88);
|
||||
}
|
||||
|
||||
/* ── Header overlap fix ───────────────────────────────────────────
|
||||
.lt-container's padding shorthand resets padding-top, defeating
|
||||
.lt-main's padding-top. The combined selector restores it. */
|
||||
.lt-main.lt-container {
|
||||
padding-top: calc(var(--header-height) + var(--space-lg));
|
||||
}
|
||||
@media (max-height: 500px) and (orientation: landscape) {
|
||||
.lt-main.lt-container { padding-top: calc(42px + var(--space-md)); }
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.lt-main.lt-container { padding-top: calc(50px + var(--space-md)); }
|
||||
}
|
||||
@media (max-width: 479px) {
|
||||
.lt-main.lt-container { padding-top: calc(46px + var(--space-sm)); }
|
||||
}
|
||||
|
||||
/* ── Refresh button loading state ────────────────────────────────── */
|
||||
[data-action="refresh"].is-loading {
|
||||
@@ -173,7 +201,7 @@
|
||||
border: 1px solid;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
.chip-critical { color: var(--red); border-color: var(--red); text-shadow: var(--glow-red); animation: pulse-glow 2s infinite; }
|
||||
.chip-critical { color: var(--red); border-color: var(--red); text-shadow: var(--glow-red); animation: topo-pulse-down 2s ease-in-out infinite; }
|
||||
.chip-warning { color: var(--orange); border-color: var(--orange); }
|
||||
.chip-ok { color: var(--green); border-color: var(--green-muted); text-shadow: var(--glow); }
|
||||
.status-meta { display: flex; align-items: center; gap: 10px; white-space: nowrap; }
|
||||
@@ -217,12 +245,27 @@
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
}
|
||||
/* Corner accent triangle — mirrors test code's status-tinted corner */
|
||||
.host-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; right: 0;
|
||||
width: 0; height: 0;
|
||||
border-style: solid;
|
||||
border-width: 0 10px 10px 0;
|
||||
border-color: transparent var(--border-color) transparent transparent;
|
||||
transition: border-color .2s;
|
||||
}
|
||||
.host-card:hover { border-color: var(--accent-orange); box-shadow: 0 0 12px rgba(255,107,0,.1); }
|
||||
.host-card-up { border-left: 3px solid var(--green); }
|
||||
.host-card-up::after { border-color: transparent rgba(0,255,136,.45) transparent transparent; }
|
||||
.host-card-down { border-left: 3px solid var(--red); box-shadow: inset 3px 0 10px rgba(255,45,85,.08); }
|
||||
.host-card-down::after { border-color: transparent rgba(255,45,85,.55) transparent transparent; }
|
||||
.host-card-degraded { border-left: 3px solid var(--orange); }
|
||||
.host-card-degraded::after { border-color: transparent rgba(255,107,0,.45) transparent transparent; }
|
||||
.host-card-header { margin-bottom: 8px; }
|
||||
.host-name-row { display: flex; align-items: center; gap: 6px; margin-bottom: 3px; }
|
||||
.host-name { font-weight: bold; font-size: .88em; color: var(--amber); letter-spacing: .04em; }
|
||||
@@ -286,7 +329,9 @@
|
||||
|
||||
/* ── Topology diagram ─────────────────────────────────────────────── */
|
||||
.topology {
|
||||
background: var(--bg2);
|
||||
background-color: var(--bg2);
|
||||
background-image: radial-gradient(circle, rgba(0,212,255,0.07) 1px, transparent 1px);
|
||||
background-size: 22px 22px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 20px 16px 16px;
|
||||
margin-bottom: 16px;
|
||||
@@ -329,9 +374,18 @@
|
||||
background: linear-gradient(to bottom, var(--cyan), var(--green));
|
||||
opacity: .7;
|
||||
}
|
||||
/* Blurred copy of the wire for a soft glow halo */
|
||||
.topo-vc-wire::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: inherit;
|
||||
filter: blur(5px);
|
||||
opacity: .5;
|
||||
}
|
||||
.topo-vc-label {
|
||||
position: absolute;
|
||||
left: calc(50% + 6px);
|
||||
left: calc(50% + 7px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: .58em;
|
||||
@@ -339,6 +393,8 @@
|
||||
white-space: nowrap;
|
||||
letter-spacing: .06em;
|
||||
font-family: var(--font);
|
||||
background: rgba(3,5,8,.7);
|
||||
padding: 1px 4px;
|
||||
}
|
||||
|
||||
.topo-v2-node {
|
||||
@@ -355,26 +411,45 @@
|
||||
min-width: 110px;
|
||||
text-align: center;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* Top highlight strip — color matches node type / status */
|
||||
.topo-v2-node::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
opacity: .4;
|
||||
}
|
||||
.topo-v2-status-up.topo-v2-node::before { background: var(--green); opacity: .65; }
|
||||
.topo-v2-status-down.topo-v2-node::before { background: var(--red); opacity: .75; }
|
||||
.topo-v2-status-degraded.topo-v2-node::before { background: var(--orange); opacity: .65; }
|
||||
.topo-v2-status-unknown.topo-v2-node::before { opacity: .15; }
|
||||
.topo-v2-icon { font-size: 1.3em; line-height: 1; }
|
||||
.topo-v2-label { font-weight: bold; letter-spacing: .04em; }
|
||||
.topo-v2-sub { font-size: .58em; color: var(--text-muted); letter-spacing: .02em; }
|
||||
.topo-v2-vlan { font-size: .54em; color: var(--cyan); opacity: .75; }
|
||||
|
||||
.topo-v2-internet { border-color: var(--cyan); color: var(--cyan); text-shadow: var(--glow-cyan); }
|
||||
.topo-v2-router { border-color: var(--cyan); color: var(--cyan); text-shadow: var(--glow-cyan); }
|
||||
.topo-v2-switch { border-color: var(--amber); color: var(--amber); text-shadow: var(--glow-amber); }
|
||||
.topo-v2-internet { border-color: var(--cyan); color: var(--cyan); text-shadow: var(--glow-cyan); box-shadow: 0 0 12px rgba(0,212,255,.12); }
|
||||
.topo-v2-router { border-color: var(--cyan); color: var(--cyan); text-shadow: var(--glow-cyan); box-shadow: 0 0 12px rgba(0,212,255,.14); }
|
||||
.topo-v2-switch { border-color: var(--amber); color: var(--amber); text-shadow: var(--glow-amber); box-shadow: 0 0 12px rgba(255,179,0,.12); }
|
||||
.topo-v2-host { border-color: var(--border-color); color: var(--text); cursor: default; }
|
||||
|
||||
@keyframes topo-pulse-down {
|
||||
0%,100% { box-shadow: 0 0 6px rgba(255,45,85,.3); }
|
||||
50% { box-shadow: 0 0 18px rgba(255,45,85,.75), 0 0 30px rgba(255,45,85,.2); }
|
||||
}
|
||||
|
||||
.topo-v2-status-up { border-color: var(--green); box-shadow: 0 0 8px rgba(0,255,136,.2); }
|
||||
.topo-v2-status-down { border-color: var(--red); box-shadow: 0 0 8px rgba(255,45,85,.35); animation: pulse-glow 2s infinite; }
|
||||
.topo-v2-status-down { border-color: var(--red); animation: topo-pulse-down 2s ease-in-out infinite; }
|
||||
.topo-v2-status-degraded { border-color: var(--orange); box-shadow: 0 0 8px rgba(255,107,0,.2); }
|
||||
.topo-v2-status-unknown { border-color: var(--border-color); }
|
||||
.topo-v2-offrack { border-style: dashed !important; }
|
||||
|
||||
.topo-badge { font-size: .68em; padding: 1px 5px; border: 1px solid; letter-spacing: .03em; }
|
||||
.topo-badge-up { color: var(--green); border-color: var(--green); text-shadow: var(--glow); }
|
||||
.topo-badge-down { color: var(--red); border-color: var(--red); animation: pulse-glow 1.5s infinite; }
|
||||
.topo-badge-down { color: var(--red); border-color: var(--red); animation: topo-pulse-down 1.5s ease-in-out infinite; }
|
||||
.topo-badge-degraded { color: var(--orange); border-color: var(--orange); }
|
||||
.topo-badge-unknown { color: var(--text-muted); border-color: var(--border-color); }
|
||||
|
||||
@@ -398,6 +473,7 @@
|
||||
background: repeating-linear-gradient(to bottom, var(--red) 0 6px, transparent 6px 10px) !important;
|
||||
background-size: 2px 10px !important;
|
||||
opacity: .9 !important;
|
||||
transition: none !important;
|
||||
animation: wire-dash-anim .7s linear infinite;
|
||||
}
|
||||
|
||||
@@ -419,8 +495,9 @@
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
background: var(--green);
|
||||
opacity: .45;
|
||||
opacity: .55;
|
||||
margin: 0 4px;
|
||||
box-shadow: 0 0 6px rgba(0,255,136,.4);
|
||||
}
|
||||
.topo-bus-10g-label {
|
||||
font-size: .56em;
|
||||
@@ -483,7 +560,7 @@
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font);
|
||||
}
|
||||
.topo-legend-line-10g { width: 24px; height: 2px; background: var(--green); display: inline-block; }
|
||||
.topo-legend-line-10g { width: 24px; height: 2px; background: var(--green); display: inline-block; box-shadow: 0 0 4px rgba(0,255,136,.5); }
|
||||
.topo-legend-line-1g { width: 24px; height: 0; border-top: 2px dashed var(--amber); display: inline-block; }
|
||||
.topo-legend-line-wan { width: 24px; height: 2px; background: linear-gradient(to right, var(--cyan), var(--green)); display: inline-block; }
|
||||
|
||||
@@ -564,6 +641,8 @@
|
||||
.traffic-label { font-size: .62em; color: var(--text-muted); width: 20px; text-transform: uppercase; letter-spacing: .04em; flex-shrink: 0; }
|
||||
.traffic-row .lt-progress { flex: 1; height: 5px; }
|
||||
.traffic-value { font-size: .7em; color: var(--text-dim); width: 68px; text-align: right; flex-shrink: 0; }
|
||||
/* Amber variant for lt-progress (65-85% utilisation warning) */
|
||||
.lt-progress--amber .lt-progress-bar { background: var(--amber); box-shadow: 0 0 5px var(--amber), 0 0 10px rgba(255,179,0,.35); }
|
||||
|
||||
/* SFP / optical panel */
|
||||
.sfp-panel {
|
||||
@@ -751,7 +830,7 @@
|
||||
.switch-port-block.poe-active:hover { box-shadow: var(--glow-amber); }
|
||||
.switch-port-block.uplink { background: var(--cyan-dim); border-color: var(--cyan); color: var(--cyan); }
|
||||
.switch-port-block.uplink:hover { box-shadow: var(--glow-cyan); }
|
||||
.switch-port-block.selected { outline: 2px solid #fff; outline-offset: 1px; }
|
||||
.switch-port-block.selected { outline: 2px solid rgba(255,255,255,.85); outline-offset: 1px; box-shadow: 0 0 8px rgba(255,255,255,.5); }
|
||||
.port-num { line-height: 1; font-weight: bold; }
|
||||
.port-speed { font-size: .72em; opacity: .7; line-height: 1; font-weight: normal; }
|
||||
.port-lldp { font-size: .62em; opacity: .65; line-height: 1; max-width: 32px; overflow: hidden; white-space: nowrap; text-overflow: clip; font-weight: normal; }
|
||||
@@ -820,17 +899,9 @@
|
||||
transition: all .15s;
|
||||
}
|
||||
.panel-close:hover { color: var(--red); border-color: var(--red); }
|
||||
.panel-section-title {
|
||||
font-size: .62em;
|
||||
font-weight: bold;
|
||||
color: var(--amber);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
margin: 10px 0 5px;
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 1px solid rgba(255,107,0,.12);
|
||||
}
|
||||
.panel-section-title:first-of-type { margin-top: 0; }
|
||||
/* Inspector panel uses lt-divider — compact spacing overrides */
|
||||
.inspector-panel .lt-divider { margin: 8px 0 4px; }
|
||||
.inspector-panel .lt-divider-label { color: var(--amber); font-size: .6em; }
|
||||
.panel-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user