Files
grotti/web/templates/wartung.html
T
Dominic Gundel 8e88efc094 Grotti 3.0.0: Aufträge, Wartung, SSE, Ausschuss-Dialog, UI-Umbau.
Migrationen 003–009, Kundaufträge mit Lieferschein, Abräumen mit Urteil,
ntfy/Backup/Zeitfenster, Live-Stream und Mobile-Mehr-Menü.
2026-10-18 00:54:51 +02:00

94 lines
3.7 KiB
HTML

{% extends "layout.html" %}
{% block body %}
<div class="page-head">
<div>
<h1>Wartung</h1>
<p class="tiny">Laufstunden und Filament je Drucker — Verschleißteile im Blick.</p>
</div>
</div>
<div id="wartung-root"></div>
<p id="wartung-note"></p>
{% endblock %}
{% block script %}
<script>
function partRow(part) {
const overdue = part.overdue_hours || part.overdue_meters;
return `<tr class="${overdue ? "warn" : ""}">
<td>${escapeHtml(part.label || part.part_key)}</td>
<td class="num">${(part.hours_since || 0).toLocaleString("de-DE", { maximumFractionDigits: 1 })} h</td>
<td class="num">${Math.round(part.meters_since || 0).toLocaleString("de-DE")} mm</td>
<td class="num">${part.life_hours ? part.life_hours + " h" : "—"}</td>
<td class="num">${part.life_meters ? Math.round(part.life_meters) + " mm" : "—"}</td>
<td><button type="button" class="ghost sm" data-reset="${part.id}">Zurücksetzen</button></td>
</tr>`;
}
function printerCard(block) {
const c = block.counters || {};
const parts = (block.parts || []).map(partRow).join("")
|| '<tr><td colspan="6" class="empty">Noch keine Teile.</td></tr>';
return `<section class="card">
<h2>${escapeHtml(block.name || block.host)}</h2>
<p class="hint">${escapeHtml(block.host)} · ${(c.hours || 0).toLocaleString("de-DE", { maximumFractionDigits: 1 })} h gesamt · ${Math.round(c.meters || 0).toLocaleString("de-DE")} mm · ${c.runs || 0} Läufe</p>
<div class="table-wrap"><table>
<thead><tr><th>Teil</th><th>Seit Reset</th><th>Seit Reset</th><th>Intervall h</th><th>Intervall mm</th><th></th></tr></thead>
<tbody>${parts}</tbody>
</table></div>
<form class="grid-2 wartung-add" data-host="${escapeHtml(block.host)}" style="margin-top:0.75rem">
<div class="field"><label>Schlüssel</label><input name="part_key" required placeholder="nozzle"></div>
<div class="field"><label>Bezeichnung</label><input name="label" placeholder="Düse 0.4"></div>
<div class="field"><label>Intervall h</label><input name="life_hours" type="number" step="1" min="0" value="0"></div>
<div class="field"><label>Intervall mm</label><input name="life_meters" type="number" step="1000" min="0" value="0"></div>
<div class="field" style="grid-column:1/-1"><label>Notiz</label><input name="note"></div>
<div class="actions"><button type="submit">Teil anlegen</button></div>
</form>
</section>`;
}
async function load() {
const data = await api("/wartung");
document.getElementById("wartung-root").innerHTML = (data || []).map(printerCard).join("")
|| '<p class="empty">Keine Drucker angelegt.</p>';
}
document.getElementById("wartung-root").addEventListener("submit", async (event) => {
const form = event.target.closest("form.wartung-add");
if (!form) return;
event.preventDefault();
const fd = new FormData(form);
try {
await api("/wartung/teile", {
method: "POST",
json: {
printer_host: form.dataset.host,
part_key: fd.get("part_key"),
label: fd.get("label"),
life_hours: Number(fd.get("life_hours") || 0),
life_meters: Number(fd.get("life_meters") || 0),
note: fd.get("note"),
},
});
form.reset();
showNote("wartung-note", null, "Teil gespeichert.");
load();
} catch (error) {
showNote("wartung-note", error);
}
});
document.getElementById("wartung-root").addEventListener("click", async (event) => {
const btn = event.target.closest("[data-reset]");
if (!btn) return;
try {
await api("/wartung/teile/" + btn.dataset.reset + "/reset", { method: "POST", json: {} });
showNote("wartung-note", null, "Teil zurückgesetzt.");
load();
} catch (error) {
showNote("wartung-note", error);
}
});
load();
</script>
{% endblock %}