onu detail polish + clickable OLT detail modal

- Fix alarm card overflow: alarm rows stack (severity+text on one line that
  wraps, meta below) instead of squeezing the text to ~0 width next to the
  nowrap meta — which had made long alarm text render one char per line in
  narrow columns. Also min-width:0 on cards/kv so long IPv6/descr strings
  don't blow out the grid track.
- Combine Parent OLT + Upstream switch into one "Parent OLT & uplink" card.
- ONU list/search de-compacted: add equipment/version + PON-mode filters;
  two-line rows (serial+status, then equipment · version · last-seen).
- UNI card labels the speed as configured ("cfg Auto/Auto"); negotiated
  speed isn't in the payloads — link up/down still comes from LAN-LOS.

Clickable parent OLT -> OLT detail modal (showOltModal):
- new src/oltdetail.js (pure, shared; reuses summarizeAlarms) + getOltState
  / getOltAlarms client methods + api:getOltDetail (main + web + bridges).
- shows identity, traffic + util, ASIC temperature, ONU counts, laser,
  alarms, upstream switch; plus a connected-ONU list with per-ONU
  FEC/optical built from the loaded fleet (filtered by _status.oltMac),
  each row clickable back into that ONU's detail.

Docs: CLAUDE.md §17 + IPC table.

Verified: node --check; oltdetail.js unit-tested 9/9 (fw active-bank,
traffic down=TX/up=RX, util, ASIC temp, ONU counts, laser, LLDP, alarms);
rendered the OLT modal + richer list offscreen and confirmed the alarm-text
wrap fix (alarm head ~1-2 lines, not vertical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jon Vanvik 2026-06-23 16:10:34 +02:00
parent 276c3e9faa
commit 2046fad0f8
11 changed files with 321 additions and 59 deletions

View file

@ -530,8 +530,10 @@ button.danger:hover:not(:disabled) {
.onu-info-row:last-child { border-bottom: none; }
.onu-info-row:hover { background: rgba(33, 200, 255, 0.06); }
.onu-info-row.selected { background: rgba(33, 200, 255, 0.14); box-shadow: inset 3px 0 0 var(--neon); }
.onu-info-row .oir-serial { font-weight: 600; font-size: 12px; }
.onu-info-row .oir-serial { font-weight: 600; font-size: 12px; display: flex; gap: 8px; align-items: center; justify-content: space-between; }
.onu-info-row .oir-sub { font-size: 11px; color: var(--fg-dim); display: flex; gap: 8px; align-items: center; }
.onu-info-row .oir-meta { font-size: 11px; color: var(--fg-dim); overflow-wrap: anywhere; }
.onu-info-row .oir-meta .sep { opacity: 0.5; }
.onu-detail { overflow-y: auto; display: flex; flex-direction: column; gap: 14px; padding-right: 6px; }
.onu-detail h2 { margin: 0; font-size: 17px; color: var(--neon); text-shadow: var(--glow-neon); }
@ -542,12 +544,24 @@ button.danger:hover:not(:disabled) {
gap: 14px;
align-items: start;
}
/* min-width:0 stops a long unbreakable string (system descriptions, IPv6,
alarm text) from blowing the grid track out past the card. */
.detail-card { min-width: 0; }
.kv dd { min-width: 0; overflow-wrap: anywhere; }
svg.spark { vertical-align: middle; opacity: 0.9; }
.alarm-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
.alarm-row { display: flex; flex-direction: column; gap: 2px; padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
.alarm-row:last-child { border-bottom: none; }
.alarm-row .alarm-text { flex: 1; }
.alarm-row .alarm-meta { color: var(--fg-dim); font-size: 11px; white-space: nowrap; }
.alarm-row .alarm-head { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
.alarm-row .alarm-text { overflow-wrap: anywhere; }
.alarm-row .alarm-meta { color: var(--fg-dim); font-size: 11px; overflow-wrap: anywhere; }
.fw-upg { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--border); padding-top: 12px; }
.olt-link { color: var(--neon); text-decoration: none; border-bottom: 1px dotted currentColor; cursor: pointer; }
.olt-link:hover { text-shadow: var(--glow-neon); }
/* OLT detail modal */
.olt-modal { width: min(960px, 95vw); }
.olt-modal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; }
.olt-onu-list { max-height: 42vh; margin-top: 6px; }
.detail-card {
background: var(--bg-elev);
border: 1px solid var(--border);