From cee5f45252dc7f2aa525e0a2ea586fdd3bedca9d Mon Sep 17 00:00:00 2001 From: ced4568 Date: Sat, 25 Apr 2026 18:33:21 -0500 Subject: [PATCH] Add NOC alert panel V3.5 --- index.html | 14 +++++++++- script.js | 70 ++++++++++++++++++++++++++++++++++++++++++++----- style.css | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index 25afaa8..74dc008 100644 --- a/index.html +++ b/index.html @@ -91,7 +91,7 @@ Built using real infrastructure: Proxmox • K3s • TrueNAS • VLAN segmentation • APRS RF Edge Systems

- +

--

@@ -114,6 +114,18 @@
+ +
+
+

🚨 NOC Alert Panel

+ Top 3 System Notes +
+ +
+

Loading alerts...

+
+
+
diff --git a/script.js b/script.js index 4adc1ca..1b3de68 100644 --- a/script.js +++ b/script.js @@ -5,8 +5,6 @@ document.addEventListener("DOMContentLoaded", () => { } loadNocStatus(); - - // Auto-refresh NOC dashboard every 30 seconds setInterval(loadNocStatus, 30000); }); @@ -26,6 +24,7 @@ async function loadNocStatus() { const vlans = document.getElementById("metric-vlans"); const edge = document.getElementById("metric-edge"); const grid = document.getElementById("noc-system-grid"); + const alertList = document.getElementById("noc-alert-list"); if (!statusText || !nodes || !services || !vlans || !edge || !grid) { return; @@ -33,10 +32,10 @@ async function loadNocStatus() { statusText.textContent = `${data.nocName} — ${data.status} — Last updated: ${data.lastUpdated}`; - nodes.textContent = data.summary.nodes; - services.textContent = data.summary.services; - vlans.textContent = data.summary.vlans; - edge.textContent = data.summary.edgeSystems; + animateMetric(nodes, data.summary.nodes); + animateMetric(services, data.summary.services); + animateMetric(vlans, data.summary.vlans); + animateMetric(edge, data.summary.edgeSystems); grid.innerHTML = ""; @@ -78,6 +77,10 @@ async function loadNocStatus() { grid.appendChild(card); }); + + if (alertList) { + renderAlerts(data.systems, alertList); + } } catch (error) { const statusText = document.getElementById("noc-status-text"); @@ -87,4 +90,59 @@ async function loadNocStatus() { console.error("Failed to load NOC status:", error); } +} + +function animateMetric(element, value) { + element.classList.remove("metric-pop"); + element.textContent = value; + + void element.offsetWidth; + element.classList.add("metric-pop"); +} + +function renderAlerts(systems, alertList) { + const priority = systems + .map((system) => { + const severity = (system.severity || "Unknown").toLowerCase(); + const status = (system.status || "").toLowerCase(); + + let score = 0; + + if (severity.includes("critical") || status.includes("offline")) { + score = 3; + } else if (severity.includes("degraded")) { + score = 2; + } else if (severity.includes("unknown")) { + score = 1; + } + + return { ...system, score }; + }) + .filter((system) => system.score > 0) + .sort((a, b) => b.score - a.score) + .slice(0, 3); + + if (priority.length === 0) { + alertList.innerHTML = ` +
+ All monitored systems healthy + No critical or degraded systems detected. +
+ `; + return; + } + + alertList.innerHTML = priority + .map((system) => { + const alertClass = + system.score === 3 ? "critical" : system.score === 2 ? "degraded" : "unknown"; + + return ` +
+ ${system.name} + ${system.severity || system.status} • ${system.layer} • ${system.latency || "N/A"} +
+ `; + }) + .join(""); } \ No newline at end of file diff --git a/style.css b/style.css index f347cb3..a8afa7f 100644 --- a/style.css +++ b/style.css @@ -647,4 +647,81 @@ a:hover { color: #ff5f56; background: rgba(255, 95, 86, 0.1); border: 1px solid rgba(255, 95, 86, 0.28); +} + +.noc-alert-panel { + margin: 1.5rem 0; + padding: 1.25rem; + border-radius: var(--radius); + background: rgba(0, 0, 0, 0.22); + border: 1px solid var(--line); + box-shadow: var(--shadow); +} + +.noc-alert-header { + display: flex; + justify-content: space-between; + gap: 1rem; + align-items: center; + margin-bottom: 1rem; +} + +.noc-alert-header h3 { + margin: 0; +} + +.noc-alert-header span { + color: var(--muted); + font-size: 0.9rem; +} + +.noc-alert-list { + display: grid; + gap: 0.75rem; +} + +.noc-alert { + display: grid; + gap: 0.2rem; + padding: 0.9rem 1rem; + border-radius: 14px; + border-left: 4px solid var(--accent-2); + background: rgba(255, 255, 255, 0.035); +} + +.noc-alert span { + color: var(--muted); + font-size: 0.9rem; +} + +.noc-alert.healthy { + border-left-color: var(--accent-2); +} + +.noc-alert.degraded { + border-left-color: var(--warning); +} + +.noc-alert.critical { + border-left-color: #ff5f56; +} + +.noc-alert.unknown { + border-left-color: var(--accent); +} + +.metric-pop { + animation: metricPop 0.35s ease; +} + +@keyframes metricPop { + 0% { + transform: scale(0.96); + opacity: 0.7; + } + + 100% { + transform: scale(1); + opacity: 1; + } } \ No newline at end of file