Grotti 3.6.0: Lauf-Identität nach Filament-Pause, robustere Queue, Projekte/Aufträge list-first und API-Polish.

This commit is contained in:
Dominic Gundel committed 2026-10-25 08:24:24 +01:00
1 parent d1eb87ba25
commit 2b1e36634a
53 files changed
+4254 -636

No files matched your search

+47 -7
View File
@@ -111,7 +111,7 @@ function render() {
if (job.direct) {
return `<tr data-direct="${escapeHtml(job.host)}">
<td>${chipHtml("printing", "läuft")}</td>
<td><span class="cell-file" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</span></td>
<td>${job.file_id ? `<a class="cell-file" href="/dateien?file=${encodeURIComponent(job.file_id)}" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</a>` : `<span class="cell-file" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</span>`}</td>
<td><a href="/#${encodeURIComponent(job.host)}">${escapeHtml(targetOf(job))}</a></td>
<td>—</td>
<td class="tiny">${escapeHtml(job.message || "")}</td>
@@ -125,16 +125,24 @@ function render() {
const rep = repeatText(job);
const tools = [];
if (st === "waiting" && waitIdx > 0) {
tools.push(`<button type="button" class="icon-btn" data-prio="${job.id}" data-dir="-1" title="Höher">${icon("up")}</button>`);
tools.push(`<button type="button" class="icon-btn" data-prio="${job.id}" data-dir="-1" title="Höher" aria-label="Höher">${icon("up")}</button>`);
}
if (st === "waiting" && waitIdx >= 0 && waitIdx < waiting.length - 1) {
tools.push(`<button type="button" class="icon-btn" data-prio="${job.id}" data-dir="1" title="Tiefer">${icon("down")}</button>`);
tools.push(`<button type="button" class="icon-btn" data-prio="${job.id}" data-dir="1" title="Tiefer" aria-label="Tiefer">${icon("down")}</button>`);
}
if (st === "error" && job.is_ablauf) {
tools.push(`<button type="button" class="icon-btn" data-resume="${job.id}" title="Fortsetzen">${icon("play")}</button>`);
tools.push(`<button type="button" class="icon-btn danger" data-dismiss="${job.id}" title="Verwerfen">${icon("x")}</button>`);
tools.push(`<button type="button" class="ghost tiny-btn with-label" data-resume="${job.id}" title="Fortsetzen" aria-label="Fortsetzen">${icon("play")}<span class="act-label">Fortsetzen</span></button>`);
tools.push(`<button type="button" class="ghost tiny-btn with-label danger" data-dismiss="${job.id}" title="Verwerfen" aria-label="Verwerfen">${icon("x")}<span class="act-label">Verwerfen</span></button>`);
} else if (st === "error") {
if (job.host) {
tools.push(`<a class="ghost tiny-btn with-label" href="/#${encodeURIComponent(job.host)}" title="Zur Wand" aria-label="Zur Wand">${icon("printer")}<span class="act-label">Wand</span></a>`);
}
if (job.file_id) {
tools.push(`<button type="button" class="ghost tiny-btn with-label" data-requeue="${job.id}" title="Erneut einreihen" aria-label="Erneut einreihen">${icon("refresh")}<span class="act-label">Nochmal</span></button>`);
}
tools.push(`<button type="button" class="ghost tiny-btn with-label danger" data-dismiss="${job.id}" title="Verwerfen" aria-label="Verwerfen">${icon("x")}<span class="act-label">Verwerfen</span></button>`);
} else if (st === "waiting" || st === "running") {
tools.push(`<button type="button" class="icon-btn danger" data-cancel="${job.id}" title="Abbrechen">${icon("x")}</button>`);
tools.push(`<button type="button" class="ghost tiny-btn with-label danger" data-cancel="${job.id}" title="Abbrechen" aria-label="Abbrechen">${icon("x")}<span class="act-label">Abbrechen</span></button>`);
}
const phase = String(job.phase || "").toLowerCase();
const starting = st === "running" && (phase === "starting" || phase === "await_print");
@@ -142,7 +150,7 @@ function render() {
const chipLabel = starting ? "startet" : (STATUS_LABEL[st] || st);
return `<tr data-id="${job.id}">
<td>${chipHtml(chipState, chipLabel)}</td>
<td><span class="cell-file" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</span>${rep ? " · " + escapeHtml(rep) : ""}</td>
<td>${job.file_id ? `<a class="cell-file" href="/dateien?file=${encodeURIComponent(job.file_id)}" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</a>` : `<span class="cell-file" title="${escapeHtml(job.filename || "")}">${escapeHtml(jobLabel(job.filename))}</span>`}${rep ? " · " + escapeHtml(rep) : ""}</td>
<td>${job.host ? `<a href="/#${encodeURIComponent(job.host)}">${escapeHtml(targetOf(job))}</a>` : escapeHtml(targetOf(job))}</td>
<td>${steps || "—"}</td>
<td class="tiny">${escapeHtml(job.message || "")}</td>
@@ -217,6 +225,38 @@ document.getElementById("q-rows").addEventListener("click", async (event) => {
});
return;
}
const requeue = event.target.closest("[data-requeue]");
if (requeue) {
const job = jobs.find((j) => j.id === Number(requeue.dataset.requeue));
if (!job || !job.file_id) return;
armButton(requeue, "Erneut einreihen?", async () => {
try {
await api("/druck-queue/" + job.id, {
method: "PATCH", json: { status: "waiting", undo: true },
});
toast("Wieder in der Warteschlange.");
load();
if (typeof refreshPrintQueue === "function") refreshPrintQueue();
} catch (error) {
try {
await api("/senden", {
method: "POST",
json: {
file_id: job.file_id,
hosts: job.host ? [job.host] : [],
confirm: true,
when_free: true,
ace_maps: job.host && (job.ace_map || []).length ? { [job.host]: job.ace_map } : {},
bed_leveling: !!job.bed_leveling,
},
});
toast("Erneut eingereiht.");
load();
} catch (err2) { toast(err2.message, "bad"); }
}
});
return;
}
const prio = event.target.closest("[data-prio]");
if (prio) {
const id = Number(prio.dataset.prio);