Grotti 3.1.0: Bedarf-UI, Zeitfenster, HT/NT, Theme, Chrome-Fixes.
Bedarf in Sidebar/Material, Drucker-Zeitfenster und Tarif in Einstellungen, helles Theme; Legacy-Token und /api/drucker entfernt; SSE/SW-Fixes gegen hängende Navigation in Chrome.
This commit is contained in:
1 parent
8e88efc094
commit
6e59df22e5
23 files changed
+741
-171
No files matched your search
@@ -110,6 +110,21 @@ function fillPrinters(rows) {
|
||||
if ([...select.options].some((o) => o.value === current)) select.value = current;
|
||||
}
|
||||
|
||||
function barChart(items, valueKey, labelFn) {
|
||||
const rows = (items || []).filter((item) => Number(item[valueKey]) > 0);
|
||||
if (!rows.length) return '<p class="empty">Keine Werte fürs Diagramm.</p>';
|
||||
const max = Math.max(...rows.map((item) => Number(item[valueKey]) || 0)) || 1;
|
||||
return `<div class="bar-chart" role="img">${rows.map((item) => {
|
||||
const value = Number(item[valueKey]) || 0;
|
||||
const pct = Math.max(4, Math.round(100 * value / max));
|
||||
return `<div class="bar-row">
|
||||
<span class="bar-label">${escapeHtml(labelFn(item))}</span>
|
||||
<span class="bar-track"><span class="bar-fill" style="width:${pct}%"></span></span>
|
||||
<span class="bar-val">${escapeHtml(String(item._barLabel || value))}</span>
|
||||
</div>`;
|
||||
}).join("")}</div>`;
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
fillPrinters(data.by_printer);
|
||||
const o = data.overview || {};
|
||||
@@ -120,6 +135,16 @@ function render(data) {
|
||||
? (data.estimate_ratio * 100).toFixed(0) + " %"
|
||||
: "—";
|
||||
|
||||
if (!jobs) {
|
||||
document.getElementById("stat").innerHTML = `
|
||||
<div class="empty-state">
|
||||
<svg class="ico"><use href="#i-grid"/></svg>
|
||||
<p>Keine Läufe im gewählten Zeitraum.</p>
|
||||
<a class="ghost" href="/protokoll">Protokoll →</a>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const printers = (data.by_printer || []).map((p) => `
|
||||
<tr>
|
||||
<td>${escapeHtml(p.printer_name || p.printer_host)}</td>
|
||||
@@ -145,6 +170,15 @@ function render(data) {
|
||||
<td class="num">${m.jobs}</td>
|
||||
</tr>`).join("") || '<tr><td class="empty" colspan="5">Kein Spulenabzug im Zeitraum.</td></tr>';
|
||||
|
||||
const chartPrinters = (data.by_printer || []).map((p) => ({
|
||||
...p,
|
||||
_barLabel: hours(p.duration_s),
|
||||
}));
|
||||
const chartMaterials = (data.by_material || []).map((m) => ({
|
||||
...m,
|
||||
_barLabel: grams(m.weight_g),
|
||||
}));
|
||||
|
||||
document.getElementById("stat").innerHTML = `
|
||||
<div class="kpi-row">
|
||||
${kpi("Läufe", String(jobs))}
|
||||
@@ -155,6 +189,16 @@ function render(data) {
|
||||
${kpi("Ausschuss", scrap + " %", (o.scrap || 0) + " mit Urteil Ausschuss")}
|
||||
${kpi("Schätztreue", ratio, data.estimate_count ? data.estimate_count + " Läufe mit Soll-Zeit" : "keine Soll-Zeit")}
|
||||
</div>
|
||||
<div class="split">
|
||||
<div class="card">
|
||||
<h2>Druckzeit je Drucker</h2>
|
||||
${barChart(chartPrinters, "duration_s", (p) => p.printer_name || p.printer_host || "—")}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Verbrauch je Material</h2>
|
||||
${barChart(chartMaterials, "weight_g", (m) => [m.material_type, m.brand].filter(Boolean).join(" · ") || "—")}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Nach Drucker</h2>
|
||||
<div class="table-wrap"><table>
|
||||
@@ -222,6 +266,17 @@ document.getElementById("reload").addEventListener("click", () => {
|
||||
document.getElementById("host").addEventListener("change", () => {
|
||||
load().catch((error) => showNote("stat-note", error));
|
||||
});
|
||||
["material", "brand"].forEach((id) => {
|
||||
const node = document.getElementById(id);
|
||||
if (!node) return;
|
||||
node.addEventListener("change", () => load().catch((error) => showNote("stat-note", error)));
|
||||
node.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
load().catch((error) => showNote("stat-note", error));
|
||||
}
|
||||
});
|
||||
});
|
||||
load().catch((error) => showNote("stat-note", error));
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user