87 lines
4.0 KiB
JavaScript
87 lines
4.0 KiB
JavaScript
export function renderStorage(state) {
|
|
const storage = state.storage || {};
|
|
const grid = document.getElementById("storage-grid");
|
|
if (!grid) return;
|
|
|
|
const root = storage.root || storage.disks?.[0] || null;
|
|
const disks = storage.disks || [];
|
|
const rootPct = root?.usage_percent ?? 0;
|
|
const rootTone = pickTone(rootPct);
|
|
const warningCount = disks.filter((disk) => between(disk.usage_percent, 70, 85)).length;
|
|
const criticalCount = disks.filter((disk) => (disk.usage_percent ?? 0) > 85).length;
|
|
const highest = disks.length
|
|
? disks.reduce((current, disk) => ((disk.usage_percent ?? 0) > (current.usage_percent ?? 0) ? disk : current), disks[0])
|
|
: null;
|
|
const matrixPill = criticalCount > 0 ? "pill-offline" : warningCount > 0 ? "pill-degraded" : "pill-online";
|
|
const strip = disks.length
|
|
? disks.slice(0, 4).map((disk) => {
|
|
const pct = disk.usage_percent ?? 0;
|
|
return `<span class="storage-chip" style="color:${pickColor(pct)}">${disk.name || disk.mount} ${pct.toFixed(0)}%</span>`;
|
|
}).join("")
|
|
: '<span class="storage-chip">No disk data</span>';
|
|
|
|
grid.innerHTML = `
|
|
<div class="card service-card storage-card storage-primary">
|
|
<div class="card-title">
|
|
<div class="card-title-left">
|
|
<span class="service-icon icon-storage"><span class="icon-glyph glyph-storage"></span></span>
|
|
<span class="service-name">ROOT STORAGE</span>
|
|
</div>
|
|
<span class="status-pill ${statusPill(root?.status)}">${(root?.status || "stable").toUpperCase()}</span>
|
|
</div>
|
|
<div class="stats-grid">
|
|
<div class="stat-block"><div class="stat-num ${rootTone}">${root ? `${rootPct.toFixed(1)}%` : "\u2014"}</div><div class="stat-label">Usage</div></div>
|
|
<div class="stat-block"><div class="stat-num dim">${root ? fmtNum(root.used_gb) : "\u2014"}</div><div class="stat-label">Used GB</div></div>
|
|
<div class="stat-block"><div class="stat-num dim">${root ? fmtNum(root.free_gb) : "\u2014"}</div><div class="stat-label">Free GB</div></div>
|
|
</div>
|
|
<div class="progress-wrap">
|
|
<div class="progress-meta"><span>${root?.mount || "/"}</span><span>${root ? `${rootPct.toFixed(1)}%` : "0%"}</span></div>
|
|
<div class="progress-bar"><div class="progress-fill ${rootTone}" style="width:${Math.min(100, rootPct)}%"></div></div>
|
|
</div>
|
|
</div>
|
|
<div class="card service-card storage-card storage-matrix-card">
|
|
<div class="card-title">
|
|
<div class="card-title-left">
|
|
<span class="service-icon icon-matrix"><span class="icon-glyph glyph-matrix"></span></span>
|
|
<span class="service-name">DISK MATRIX</span>
|
|
</div>
|
|
<span class="status-pill ${matrixPill}">${disks.length}</span>
|
|
</div>
|
|
<div class="stats-grid">
|
|
<div class="stat-block"><div class="stat-num">${disks.length || "\u2014"}</div><div class="stat-label">Volumes</div></div>
|
|
<div class="stat-block"><div class="stat-num warn">${warningCount}</div><div class="stat-label">Warning</div></div>
|
|
<div class="stat-block"><div class="stat-num danger">${criticalCount}</div><div class="stat-label">Critical</div></div>
|
|
<div class="stat-block"><div class="stat-num ${pickTone(highest?.usage_percent ?? 0)}">${highest ? `${(highest.usage_percent ?? 0).toFixed(0)}%` : "\u2014"}</div><div class="stat-label">Peak</div></div>
|
|
</div>
|
|
<div class="service-footer"><span>Mounted Volumes</span><div class="storage-strip">${strip}</div></div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function fmtNum(value) {
|
|
return value == null ? "\u2014" : Number(value).toFixed(1);
|
|
}
|
|
|
|
function between(value, min, max) {
|
|
const num = value ?? 0;
|
|
return num > min && num <= max;
|
|
}
|
|
|
|
function pickTone(pct) {
|
|
if (pct > 85) return "danger";
|
|
if (pct > 70) return "warn";
|
|
return "";
|
|
}
|
|
|
|
function pickColor(pct) {
|
|
if (pct > 85) return "var(--red)";
|
|
if (pct > 70) return "var(--yellow)";
|
|
return "var(--teal-bright)";
|
|
}
|
|
|
|
function statusPill(status) {
|
|
if (status === "critical") return "pill-offline";
|
|
if (status === "warning") return "pill-degraded";
|
|
return "pill-online";
|
|
}
|