Replace stale Grafana hero with live operations panel

This commit is contained in:
Keith Casko
2026-10-02 10:53:43 -04:00
parent 9e62baced4
commit 62fc9fe0f4
3 changed files with 180 additions and 9 deletions
+25 -4
View File
@@ -119,11 +119,32 @@
<div class="operations-window"> <div class="operations-window">
<div class="window-bar"> <div class="window-bar">
<span>GRAFANA / BYTEGEIST OPERATIONS</span> <span><i class="live-dot"></i> BYTEGEIST / LIVE OPERATIONS</span>
<a href="assets/images/portfolio-grafana-operations.png" target="_blank">FULL RES ↗</a> <small id="ops-source">PROMETHEUS / NODE EXPORTER / CADVISOR</small>
</div> </div>
<img src="assets/images/portfolio-grafana-operations.png" alt="Grafana operations dashboard snapshot from the ByteGeist monitoring stack" /> <div class="live-ops-panel" aria-label="Live ByteGeist operations telemetry">
<p class="evidence-note">CAPTURED OCT 2026 · GRAFANA OPERATIONS SNAPSHOT · LIVE VALUES ABOVE REFRESH EVERY 15s</p> <div class="ops-chart">
<div class="ops-chart-head">
<span>CPU LOAD</span>
<strong id="ops-cpu-value">--%</strong>
</div>
<div id="ops-cpu-bars" class="ops-bars" aria-hidden="true"></div>
</div>
<div class="ops-chart">
<div class="ops-chart-head">
<span>MEMORY</span>
<strong id="ops-memory-value">--%</strong>
</div>
<div id="ops-memory-bars" class="ops-bars" aria-hidden="true"></div>
</div>
<div class="ops-status-grid">
<div><span>HOST UPTIME</span><strong id="ops-uptime-value">--</strong></div>
<div><span>CONTAINERS</span><strong id="ops-containers-value">--</strong></div>
<div><span>SCRAPE TARGETS</span><strong id="ops-targets-value">--</strong></div>
<div><span>REFRESH</span><strong>15s</strong></div>
</div>
</div>
<p class="evidence-note">LIVE OPERATIONS VIEW · VALUES COME FROM THE SAME SANITIZED /API/STATUS TELEMETRY FEED</p>
</div> </div>
</div> </div>
+54 -5
View File
@@ -8,6 +8,16 @@ const fields = {
containers: document.querySelector("#containers-value"), containers: document.querySelector("#containers-value"),
targets: document.querySelector("#targets-value"), targets: document.querySelector("#targets-value"),
telemetry: document.querySelector("#telemetry-value"), telemetry: document.querySelector("#telemetry-value"),
opsCpu: document.querySelector("#ops-cpu-value"),
opsMemory: document.querySelector("#ops-memory-value"),
opsUptime: document.querySelector("#ops-uptime-value"),
opsContainers: document.querySelector("#ops-containers-value"),
opsTargets: document.querySelector("#ops-targets-value"),
};
const telemetryHistory = {
cpu: [],
memory: [],
}; };
let lastSuccessfulUpdate = null; let lastSuccessfulUpdate = null;
@@ -41,17 +51,56 @@ function setUnavailable() {
} }
fields.telemetry.textContent = "UNAVAILABLE"; fields.telemetry.textContent = "UNAVAILABLE";
fields.telemetry.classList.add("metric-error"); fields.telemetry.classList.add("metric-error");
fields.opsCpu.textContent = "--%";
fields.opsMemory.textContent = "--%";
fields.opsUptime.textContent = "--";
fields.opsContainers.textContent = "--";
fields.opsTargets.textContent = "--";
refreshElement.textContent = "TELEMETRY UNAVAILABLE"; refreshElement.textContent = "TELEMETRY UNAVAILABLE";
refreshElement.classList.add("telemetry-error"); refreshElement.classList.add("telemetry-error");
} }
function renderOpsBars(elementId, values) {
const container = document.querySelector(elementId);
if (!container) return;
container.replaceChildren();
values.forEach((value) => {
const bar = document.createElement("i");
bar.style.setProperty("--bar-height", Math.max(4, Math.min(100, value)) + "%");
container.appendChild(bar);
});
}
function pushHistory(series, value) {
series.push(value);
if (series.length > 24) series.shift();
}
function renderStatus(data) { function renderStatus(data) {
fields.cpu.textContent = Number(data.host.cpu_percent).toFixed(1); const cpu = Number(data.host.cpu_percent);
fields.memory.textContent = Number(data.host.memory_percent).toFixed(1); const memory = Number(data.host.memory_percent);
fields.uptime.textContent = formatUptime(data.host.uptime_seconds); const uptime = formatUptime(data.host.uptime_seconds);
fields.containers.textContent = String(data.containers.running); const containers = String(data.containers.running);
fields.targets.textContent = data.prometheus.up + "/" + data.prometheus.total; const targets = data.prometheus.up + "/" + data.prometheus.total;
fields.cpu.textContent = cpu.toFixed(1);
fields.memory.textContent = memory.toFixed(1);
fields.uptime.textContent = uptime;
fields.containers.textContent = containers;
fields.targets.textContent = targets;
fields.telemetry.textContent = "LIVE"; fields.telemetry.textContent = "LIVE";
fields.opsCpu.textContent = cpu.toFixed(1) + "%";
fields.opsMemory.textContent = memory.toFixed(1) + "%";
fields.opsUptime.textContent = uptime;
fields.opsContainers.textContent = containers;
fields.opsTargets.textContent = targets;
pushHistory(telemetryHistory.cpu, cpu);
pushHistory(telemetryHistory.memory, memory);
renderOpsBars("#ops-cpu-bars", telemetryHistory.cpu);
renderOpsBars("#ops-memory-bars", telemetryHistory.memory);
fields.telemetry.classList.remove("metric-error"); fields.telemetry.classList.remove("metric-error");
refreshElement.classList.remove("telemetry-error"); refreshElement.classList.remove("telemetry-error");
+101
View File
@@ -460,6 +460,94 @@ h1 {
border: 1px solid #20232a; border: 1px solid #20232a;
} }
.live-ops-panel {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
border: 1px solid #20232a;
background: var(--line);
}
.ops-chart,
.ops-status-grid {
background: #070a0f;
}
.ops-chart {
min-height: 150px;
padding: 16px;
}
.ops-chart-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.ops-chart-head span,
.ops-status-grid span {
color: var(--dim);
font: .56rem var(--mono);
letter-spacing: .08em;
}
.ops-chart-head strong {
color: var(--green);
font: 500 1.1rem var(--mono);
}
.ops-bars {
height: 86px;
margin-top: 18px;
display: flex;
align-items: flex-end;
gap: 4px;
border-bottom: 1px solid #26313c;
background:
linear-gradient(to top, transparent 24%, #121820 25%, transparent 26%, transparent 49%, #121820 50%, transparent 51%, transparent 74%, #121820 75%, transparent 76%);
}
.ops-bars i {
flex: 1 1 0;
min-width: 3px;
height: var(--bar-height, 4%);
background: var(--green);
opacity: .72;
transition: height .25s ease;
}
.ops-chart:nth-child(2) .ops-bars i {
background: var(--cyan);
}
.ops-status-grid {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1px;
background: var(--line);
}
.ops-status-grid div {
min-height: 70px;
padding: 13px 16px;
background: #080c12;
}
.ops-status-grid strong {
display: block;
margin-top: 9px;
color: var(--muted);
font: 500 .82rem var(--mono);
}
.window-bar small {
color: var(--dim);
font: .52rem var(--mono);
letter-spacing: .05em;
}
.activity-panel { .activity-panel {
padding: 24px 18px; padding: 24px 18px;
border-left: 1px solid var(--line); border-left: 1px solid var(--line);
@@ -1191,6 +1279,19 @@ footer {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
.live-ops-panel {
grid-template-columns: 1fr;
}
.ops-status-grid {
grid-column: 1;
grid-template-columns: 1fr 1fr;
}
.window-bar small {
display: none;
}
.metric { .metric {
min-height: 112px; min-height: 112px;
} }