refactor: massive UX and beginner refactoring (Cookbook, Dashboard, Layout, Wording)
This commit is contained in:
+89
-14
@@ -1,20 +1,20 @@
|
||||
// jobs.js — Aktivitaets-Stream ("Incident Stream"): Hintergrund-Jobs mit Live-Log.
|
||||
// Exportiert track(id), damit andere Panels einen frisch gestarteten Job auto-aufklappen.
|
||||
// jobs.js (Aktivität) — System-Metriken & Hintergrund-Jobs
|
||||
|
||||
import { $, esc } from "../core/ui.js";
|
||||
import { $, esc, icon } from "../core/ui.js";
|
||||
|
||||
const tracked = new Set();
|
||||
let JOBS = [];
|
||||
let SYS = null;
|
||||
|
||||
export function track(id) {
|
||||
tracked.add(id);
|
||||
render();
|
||||
renderJobs();
|
||||
}
|
||||
|
||||
function statusBadge(state) {
|
||||
if (state === "done") return '<span class="badge b-run">fertig</span>';
|
||||
if (state === "failed") return '<span class="badge b-err">fehler</span>';
|
||||
return '<span class="badge b-load">läuft…</span>';
|
||||
return '<span class="badge b-load">lädt…</span>';
|
||||
}
|
||||
function dotClass(state) {
|
||||
if (state === "done") return "on";
|
||||
@@ -22,26 +22,95 @@ function dotClass(state) {
|
||||
return "load";
|
||||
}
|
||||
|
||||
function kpi(cls, title, ic, value, sub) {
|
||||
return `<div class="kpi ${cls}">
|
||||
<div class="k-h"><span class="k-t">${title}</span><span class="k-ic">${icon(ic)}</span></div>
|
||||
<div class="k-v">${value}</div>
|
||||
<div class="k-s">${sub}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function kvRow(k, v, cls = "") {
|
||||
return `<div class="kv-row"><span class="kv-k">${k}</span><span class="kv-v ${cls}">${v}</span></div>`;
|
||||
}
|
||||
|
||||
// Chart-Daten (letzte 60 Sekunden, je 3s Polling = 20 Punkte)
|
||||
const hist = { cpu: [], ram: [], gpu: [] };
|
||||
const MAX_HIST = 20;
|
||||
|
||||
function renderSys() {
|
||||
if (!SYS) return;
|
||||
const sysV = `${SYS.cpu.percent.toFixed(0)}<small>% CPU</small>`;
|
||||
const sysS = `${SYS.ram.percent.toFixed(0)}% RAM, ${SYS.gpu_temp ? SYS.gpu_temp.toFixed(0)+'°C' : SYS.cpu.temp ? SYS.cpu.temp.toFixed(0)+'°C' : '–'}`;
|
||||
|
||||
// History aktualisieren
|
||||
hist.cpu.push(SYS.cpu.percent);
|
||||
hist.ram.push(SYS.ram.percent);
|
||||
let gpuP = 0;
|
||||
if (SYS.gpu && SYS.gpu.vram.total) {
|
||||
gpuP = ((SYS.gpu.vram.used + SYS.gpu.gtt.used) / (SYS.gpu.vram.total + SYS.gpu.gtt.total)) * 100;
|
||||
}
|
||||
hist.gpu.push(gpuP);
|
||||
if (hist.cpu.length > MAX_HIST) hist.cpu.shift();
|
||||
if (hist.ram.length > MAX_HIST) hist.ram.shift();
|
||||
if (hist.gpu.length > MAX_HIST) hist.gpu.shift();
|
||||
|
||||
// Mini-Sparklines generieren
|
||||
const makeBars = (arr, color) => {
|
||||
return '<div style="display:flex; align-items:flex-end; gap:2px; height:40px; margin-top:12px;">' +
|
||||
arr.map(v => `<div style="width:4px; background:var(--${color}); opacity:0.6; height:${Math.max(2, v)}%; border-radius:2px;"></div>`).join("") +
|
||||
'</div>';
|
||||
};
|
||||
|
||||
$("#act-kpis").innerHTML =
|
||||
kpi("blue", "CPU Last", "gauge", sysV, sysS) +
|
||||
kpi("purple", "RAM", "monitor", `${SYS.ram.percent.toFixed(0)}<small>%</small>`, "Arbeitsspeicher") +
|
||||
kpi("green", "GPU VRAM", "layers", `${gpuP.toFixed(0)}<small>%</small>`, "Grafikspeicher");
|
||||
|
||||
const gb = b => (b / 1024 / 1024 / 1024).toFixed(1);
|
||||
const ramStr = `${gb(SYS.ram.used)} GB / ${gb(SYS.ram.total)} GB`;
|
||||
const gpuStr = (SYS.gpu && SYS.gpu.vram.total) ? `${gb(SYS.gpu.vram.used + SYS.gpu.gtt.used)} GB / ${gb(SYS.gpu.vram.total + SYS.gpu.gtt.total)} GB` : "–";
|
||||
const diskStr = `${SYS.disk.percent.toFixed(0)}% belegt`;
|
||||
|
||||
$("#act-sys").innerHTML = `
|
||||
<div class="card-h"><h3>System-Metriken (Bosgame)</h3></div>
|
||||
<div class="kv" style="margin-bottom: 24px;">
|
||||
${kvRow("Arbeitsspeicher (RAM)", ramStr)}
|
||||
${kvRow("Grafikspeicher (VRAM+GTT)", gpuStr)}
|
||||
${kvRow("Speicherplatz (Disk)", diskStr)}
|
||||
${kvRow("Temperatur (GPU / CPU)", `${SYS.gpu_temp?.toFixed(1) || '–'}°C / ${SYS.cpu.temp?.toFixed(1) || '–'}°C`)}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-3">
|
||||
<div><div class="meta">CPU Historie</div>${makeBars(hist.cpu, "act")}</div>
|
||||
<div><div class="meta">RAM Historie</div>${makeBars(hist.ram, "purple")}</div>
|
||||
<div><div class="meta">VRAM Historie</div>${makeBars(hist.gpu, "on")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function mount() {
|
||||
$("#v-activity").innerHTML = `
|
||||
<div class="card-h"><h3>Aktivität</h3><span class="meta" id="job-count"></span></div>
|
||||
<div class="card-h"><h3>Hintergrund-Aktivitäten</h3><span class="meta" id="job-count"></span></div>
|
||||
<div id="jobs"></div>
|
||||
<div id="jobs-empty" class="empty-c">
|
||||
<div class="e-t">Noch nichts losgemacht.</div>
|
||||
<div class="e-t">Keine laufenden Jobs.</div>
|
||||
<div class="e-s">Downloads, Updates & Co. erscheinen hier mit Live-Log.</div>
|
||||
</div>`;
|
||||
|
||||
// Klicks auf Job-Kopf -> auf/zuklappen (Event-Delegation)
|
||||
$("#jobs").addEventListener("click", e => {
|
||||
// Klicks auf Job-Kopf -> auf/zuklappen
|
||||
$("#v-activity").addEventListener("click", e => {
|
||||
const h = e.target.closest(".job-h");
|
||||
if (!h) return;
|
||||
const id = h.getAttribute("data-id");
|
||||
tracked.has(id) ? tracked.delete(id) : tracked.add(id);
|
||||
render();
|
||||
renderJobs();
|
||||
});
|
||||
|
||||
if (SYS) renderSys();
|
||||
}
|
||||
|
||||
function render() {
|
||||
function renderJobs() {
|
||||
const c = $("#jobs");
|
||||
if (!c) return;
|
||||
$("#jobs-empty").style.display = JOBS.length ? "none" : "flex";
|
||||
@@ -50,7 +119,7 @@ function render() {
|
||||
|
||||
c.innerHTML = JOBS.map(j => {
|
||||
const open = tracked.has(j.id);
|
||||
const log = open ? `<div class="log">${esc((j.log || []).join("\n"))}</div>` : "";
|
||||
const log = open ? `<div class="log">${esc((j.log || []).join("\\n"))}</div>` : "";
|
||||
return `<div class="job">
|
||||
<div class="job-h" data-id="${esc(j.id)}">
|
||||
<span class="li-dot ${dotClass(j.state)}"></span>
|
||||
@@ -61,7 +130,13 @@ function render() {
|
||||
|
||||
function onJobs(jobs) {
|
||||
JOBS = jobs || [];
|
||||
render();
|
||||
renderJobs();
|
||||
}
|
||||
|
||||
export default { id: "jobs", mount, onJobs };
|
||||
function onSystem(sys) {
|
||||
SYS = sys;
|
||||
const c = $("#act-sys");
|
||||
if (c) renderSys();
|
||||
}
|
||||
|
||||
export default { id: "jobs", mount, onJobs, onSystem };
|
||||
|
||||
Reference in New Issue
Block a user