diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-09 19:17:29 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-09 19:17:29 +0100 |
| commit | 67e64e964a85562300b463378f0e260d6978867f (patch) | |
| tree | cb706f2142823ca436d301d8b7a2af27e88231c1 /static/modules | |
| parent | 796407233383e7bf4825f671d84fbe71279488a6 (diff) | |
| download | deck-it-67e64e964a85562300b463378f0e260d6978867f.tar.gz deck-it-67e64e964a85562300b463378f0e260d6978867f.zip | |
feat: build VM card shows the real stats dashboard
Diffstat (limited to 'static/modules')
| -rw-r--r-- | static/modules/sbbuilder.js | 96 |
1 files changed, 55 insertions, 41 deletions
diff --git a/static/modules/sbbuilder.js b/static/modules/sbbuilder.js index f1c574d..02493af 100644 --- a/static/modules/sbbuilder.js +++ b/static/modules/sbbuilder.js @@ -1,58 +1,72 @@ -function chipClass(status) { - if (status === "ok") return "good"; - if (status === "failed" || status === "lost" || status === "killed") return "bad"; - if (status === "running" || status === "busy" || status === "starting") return "warn"; - return "muted"; -} +const gib = (b) => (b == null ? "?" : (b / 1073741824).toFixed(1)); +const pct = (n) => (n == null ? "?" : `${Math.round(n)}%`); +const up = (s) => { + if (s == null) return "?"; + const d = Math.floor(s / 86400); + const h = Math.floor((s % 86400) / 3600); + return d ? `${d}d ${h}h` : `${h}h`; +}; +const ago = (ts) => { + if (!ts) return "—"; + const d = Math.max(0, Math.floor(Date.now() / 1000 - ts)); + if (d < 3600) return `${Math.floor(d / 60)}m`; + if (d < 86400) return `${Math.floor(d / 3600)}h`; + return `${Math.floor(d / 86400)}d`; +}; -function when(ts) { - if (!ts) return ""; - return new Date(ts * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); -} +const ACTIVE = ["running", "busy", "starting"]; -function summary(jobs) { - const running = jobs.filter((j) => ["running", "busy", "starting"].includes(j.status)).length; - const failed = jobs.filter((j) => ["failed", "lost", "killed"].includes(j.status)).length; - const parts = []; - if (running) parts.push(`${running} running`); - if (failed) parts.push(`${failed} failed`); - if (!parts.length) parts.push(jobs.length ? `${jobs.length} recent` : "idle"); - return parts.join(" · "); +function metric(label, value) { + const row = document.createElement("div"); + row.className = "metric"; + const k = document.createElement("span"); + k.className = "k"; + k.textContent = label; + const v = document.createElement("span"); + v.className = "v num"; + v.textContent = value; + row.append(k, v); + return row; } export default { title: "Build VM", - interval: 5, + interval: 10, mount(el) { - el.innerHTML = `<div class="sbb"><div class="sbb-summary muted"></div><div class="sbb-list"></div></div>`; + el.innerHTML = `<div class="sbb"></div>`; }, update(state, el) { - const sum = el.querySelector(".sbb-summary"); - const list = el.querySelector(".sbb-list"); + const box = el.querySelector(".sbb"); + box.textContent = ""; if (state.offline) { - sum.textContent = ""; - list.textContent = "build VM unreachable"; + box.textContent = "build VM unreachable"; return; } + const s = state.stats ?? {}; + const p = s.packages ?? {}; + const sr = s.slackrepo ?? {}; const jobs = state.jobs ?? []; - sum.textContent = summary(jobs); - list.textContent = ""; - for (const job of jobs.slice(0, 8)) { - const row = document.createElement("div"); - row.className = "row job"; - const kind = document.createElement("span"); - kind.className = "kind"; - kind.textContent = job.kind || "job"; - const time = document.createElement("span"); - time.className = "muted num"; - time.textContent = when(job.created); - const status = document.createElement("span"); - status.className = chipClass(job.status); - status.textContent = job.status; - row.append(kind, time, status); - list.append(row); - } + + const running = jobs.find((j) => ACTIVE.includes(j.status)); + const now = document.createElement("div"); + now.className = "sbb-now"; + now.textContent = running ? `● ${running.kind || "job"} running` : "idle"; + now.classList.toggle("warn", Boolean(running)); + box.append(now); + + const overlays = Object.entries(p).filter(([k]) => k !== "total" && k !== "shadowed"); + const results = Object.entries(sr.results ?? {}); + box.append( + metric("CPU · load", `${pct(s.cpu)} · ${s.load ? s.load[0].toFixed(2) : "?"}`), + metric("Uptime", up(s.uptime)), + metric("Memory", `${gib((s.mem?.total ?? 0) - (s.mem?.available ?? 0))} / ${gib(s.mem?.total)} GiB`), + metric("Disk free", `${gib(s.disk?.free)} / ${gib(s.disk?.total)} GiB`), + metric("Packages", `${p.total ?? "?"}${overlays.length ? " · " + overlays.map(([k, v]) => `${k} ${v}`).join(" · ") : ""}`), + metric("Shadowed · orphans", `${p.shadowed ?? "?"} · ${s.orphans ?? "?"}`), + metric("Built · results", `${sr.built ?? "?"}${results.length ? " · " + results.map(([k, v]) => `${v} ${k}`).join(" · ") : ""}`), + metric("Overlay pulls", Object.entries(s.sync ?? {}).map(([k, v]) => `${k} ${ago(v)}`).join(" · ") || "—"), + ); }, }; |
