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:
Dominic Gundel committed 2026-10-18 02:20:12 +02:00
1 parent 8e88efc094
commit 6e59df22e5
23 files changed
+741 -171

No files matched your search

+56 -1
View File
@@ -14,6 +14,20 @@
</div>
</div>
<section class="card bedarf-card" id="bedarf-card" hidden>
<div class="lib-head">
<h2 id="bedarf">Bedarf aus Warteschlange</h2>
<span class="count" id="bedarf-short-count">0</span>
</div>
<p class="hint" id="bedarf-hint">Fehlmenge = Queue-Gewicht minus Spulenrest (gleiche Sorte/Farbe).</p>
<div class="table-wrap">
<table>
<thead><tr><th>Sorte</th><th>Farbe</th><th class="num">Queue</th><th class="num">Rest</th><th class="num">Fehlt</th><th class="num">Jobs</th></tr></thead>
<tbody id="bedarf-rows"></tbody>
</table>
</div>
</section>
<div class="tabs mat-tabs" role="tablist">
<button type="button" class="on" data-view="spools" role="tab"><svg class="ico"><use href="#i-spool"/></svg>Spulen</button>
<button type="button" data-view="sorten" role="tab"><svg class="ico"><use href="#i-layers"/></svg>Sorten</button>
@@ -721,17 +735,58 @@ function render() {
renderSorten();
}
function renderBedarf(data) {
const card = document.getElementById("bedarf-card");
const shortfall = (data && data.shortfall) || [];
const needed = (data && data.needed) || [];
if (!needed.length && !shortfall.length) {
card.hidden = true;
return;
}
card.hidden = false;
document.getElementById("bedarf-short-count").textContent = shortfall.length
? shortfall.length + " Fehl"
: "ok";
document.getElementById("bedarf-hint").textContent = shortfall.length
? shortfall.length + " Sorten reichen nicht für die wartende Queue."
: "Queue hat Bedarf — Spulenrest reicht.";
const rows = (shortfall.length ? shortfall : needed).map((item) => {
const missing = item.shortfall_g != null ? grams(item.shortfall_g) : "—";
const rest = item.remaining_g != null
? grams(item.remaining_g)
: "—";
return `<tr class="${item.shortfall_g > 0 ? "warn" : ""}">
<td>${escapeHtml(item.material_type || "—")}</td>
<td>${escapeHtml(item.color || "—")}</td>
<td class="num">${grams(item.weight_g)}</td>
<td class="num">${rest}</td>
<td class="num">${missing}</td>
<td class="num">${item.jobs || 0}</td>
</tr>`;
}).join("");
document.getElementById("bedarf-rows").innerHTML = rows
|| '<tr><td class="empty" colspan="6">Kein Queue-Bedarf.</td></tr>';
}
async function load() {
[spools, printers, materials, palette] = await Promise.all([
const [spoolData, printerData, materialData, paletteData, bedarfData] = await Promise.all([
api("/spulen"),
api("/drucker?live=0"),
api("/materialien"),
api("/farben"),
api("/bedarf").catch(() => ({ needed: [], shortfall: [] })),
]);
spools = spoolData;
printers = printerData;
materials = materialData;
palette = paletteData;
render();
renderPalette();
renderBedarf(bedarfData);
}
window.onBedarfChanged = renderBedarf;
function setScanNote(text, ok) {
const note = document.getElementById("scan-note");
if (!text) {