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:
1 parent
d1eb87ba25
commit
2b1e36634a
53 files changed
+4254
-636
No files matched your search
@@ -4,6 +4,10 @@
|
||||
<div>
|
||||
<h1>Protokoll</h1>
|
||||
<p class="tiny">Abgeschlossene und laufende Drucke mit Dauer, Material und Kosten.</p>
|
||||
<details class="tiny proto-help">
|
||||
<summary>Filament-Pause / doppelte Zeilen</summary>
|
||||
<p>Nach Runout kann der Drucker kurz zwei History-Zeilen erzeugen. „Läufe holen“ legt sie zusammen; sonst „Verbinden“.</p>
|
||||
</details>
|
||||
</div>
|
||||
<div class="page-head-side">
|
||||
<button type="button" id="sync"><svg class="ico"><use href="#i-refresh"/></svg>Läufe holen</button>
|
||||
@@ -96,7 +100,6 @@ function render() {
|
||||
runPage = paged.page;
|
||||
paged.items.forEach((run) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.className = "pick";
|
||||
tr.innerHTML = "<td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>";
|
||||
tr.children[0].textContent = run.printer_name;
|
||||
tr.children[0].className = "proto-printer";
|
||||
@@ -109,19 +112,54 @@ function render() {
|
||||
link.title = fullName;
|
||||
link.textContent = shortRunName(run.filename) || fullName;
|
||||
fileCell.appendChild(link);
|
||||
if (run.file_id) {
|
||||
const fileLink = document.createElement("a");
|
||||
fileLink.href = "/dateien?file=" + encodeURIComponent(run.file_id);
|
||||
fileLink.className = "tiny";
|
||||
fileLink.textContent = "Datei";
|
||||
fileLink.style.marginLeft = "0.35rem";
|
||||
fileCell.appendChild(fileLink);
|
||||
}
|
||||
if (run.project_id) {
|
||||
const projLink = document.createElement("a");
|
||||
projLink.href = "/projekte#" + encodeURIComponent(run.project_id);
|
||||
projLink.className = "tiny";
|
||||
projLink.textContent = "Projekt";
|
||||
projLink.style.marginLeft = "0.35rem";
|
||||
fileCell.appendChild(projLink);
|
||||
}
|
||||
if (run.chargen_nr) {
|
||||
const code = document.createElement("div");
|
||||
code.className = "mono-badge";
|
||||
code.textContent = run.chargen_nr;
|
||||
fileCell.appendChild(code);
|
||||
}
|
||||
if ((run.spool_names || []).length > 1 || (run.segment_count || 0) > 1) {
|
||||
const swap = document.createElement("div");
|
||||
swap.className = "tiny warn-text";
|
||||
swap.textContent = "Rollenwechsel: " + (run.spool_names || []).join(" → ");
|
||||
fileCell.appendChild(swap);
|
||||
}
|
||||
if (run.merge_with_id) {
|
||||
const hint = document.createElement("div");
|
||||
hint.className = "tiny warn-text";
|
||||
hint.textContent = "Vermutlich Fortsetzung von/zu #" + run.merge_with_id;
|
||||
fileCell.appendChild(hint);
|
||||
}
|
||||
const stateKey = run.state === "in_progress" || run.state === "printing" ? "printing"
|
||||
: run.state === "paused" ? "paused"
|
||||
: run.state === "completed" ? "complete"
|
||||
: run.state === "error" ? "error"
|
||||
: run.state === "cancelled" || run.state === "interrupted" ? "cancelled"
|
||||
: "idle";
|
||||
tr.children[2].innerHTML = chipHtml(stateKey, states[run.state] || run.state);
|
||||
tr.children[3].textContent = hours(run.duration_s);
|
||||
let stateLabel = states[run.state] || run.state;
|
||||
if (run.state === "cancelled" && run.merge_with_id) stateLabel = "Teilabbruch (Runout?)";
|
||||
tr.children[2].innerHTML = chipHtml(stateKey, stateLabel);
|
||||
let durationText = hours(run.duration_s);
|
||||
if (run.pause_s && run.pause_s > 60) {
|
||||
durationText += " (Pause " + hours(run.pause_s) + ")";
|
||||
}
|
||||
tr.children[3].textContent = durationText;
|
||||
tr.children[3].className = "num";
|
||||
const used = Number(run.weight_used_g) || Number(run.weight_g) || 0;
|
||||
const planned = Number(run.weight_planned_g) || Number(run.weight_g) || 0;
|
||||
@@ -134,7 +172,11 @@ function render() {
|
||||
}
|
||||
const cost = Number(run.filament_cost || 0) + Number(run.energy_cost || 0);
|
||||
tr.children[5].textContent = cost ? euro(cost) : "";
|
||||
const verdictLabel = document.createElement("label");
|
||||
verdictLabel.className = "sr-only";
|
||||
verdictLabel.textContent = "Urteil";
|
||||
const verdict = document.createElement("select");
|
||||
verdict.setAttribute("aria-label", "Urteil");
|
||||
[["", "offen"], ["gut", "gut"], ["ausschuss", "Ausschuss"]].forEach(([value, label]) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = value;
|
||||
@@ -145,10 +187,12 @@ function render() {
|
||||
verdict.addEventListener("change", async () => {
|
||||
try {
|
||||
await api("/laeufe/" + run.id, { method: "PATCH", json: { verdict: verdict.value } });
|
||||
toast("Urteil gespeichert.");
|
||||
} catch (error) {
|
||||
showNote("run-note", error);
|
||||
}
|
||||
});
|
||||
tr.children[6].appendChild(verdictLabel);
|
||||
tr.children[6].appendChild(verdict);
|
||||
if (run.needs_spool || !run.deducted) {
|
||||
const select = document.createElement("select");
|
||||
@@ -183,14 +227,49 @@ function render() {
|
||||
}
|
||||
});
|
||||
tr.children[7].appendChild(select);
|
||||
} else if ((run.spool_names || []).length > 1) {
|
||||
tr.children[7].className = "proto-spool";
|
||||
tr.children[7].textContent = run.spool_names.join(" → ");
|
||||
tr.children[7].title = run.spool_names.join(" → ");
|
||||
} else {
|
||||
const spool = spools.find((item) => item.id === run.spool_id);
|
||||
tr.children[7].textContent = spool ? spool.name : "abgezogen";
|
||||
tr.children[7].title = spool ? spool.name : "";
|
||||
tr.children[7].className = "proto-spool";
|
||||
if (spool) {
|
||||
tr.children[7].innerHTML = linkTo("spule", spool.id, spool.name);
|
||||
tr.children[7].title = spool.name;
|
||||
} else if ((run.spool_names || []).length) {
|
||||
tr.children[7].textContent = run.spool_names[0];
|
||||
} else {
|
||||
tr.children[7].textContent = "abgezogen";
|
||||
}
|
||||
}
|
||||
if (!tr.children[7].className) tr.children[7].className = "proto-spool";
|
||||
if (run.file_id) {
|
||||
const actions = tr.children[8];
|
||||
actions.className = "proto-actions";
|
||||
if (run.merge_with_id) {
|
||||
const mergeBtn = document.createElement("button");
|
||||
mergeBtn.type = "button";
|
||||
mergeBtn.className = "ghost tiny-btn";
|
||||
mergeBtn.textContent = "Verbinden";
|
||||
mergeBtn.title = "Mit Lauf #" + run.merge_with_id + " verbinden";
|
||||
mergeBtn.addEventListener("click", () => {
|
||||
armButton(mergeBtn, "Mit #" + run.merge_with_id + " verbinden?", async () => {
|
||||
try {
|
||||
await api("/laeufe/" + run.id + "/verbinden", {
|
||||
method: "POST",
|
||||
json: { other_id: run.merge_with_id, confirm: true },
|
||||
});
|
||||
toast("Läufe verbunden.");
|
||||
await load();
|
||||
} catch (error) {
|
||||
toast(error.message, "bad");
|
||||
}
|
||||
});
|
||||
});
|
||||
actions.appendChild(mergeBtn);
|
||||
}
|
||||
const running = ["in_progress", "printing", "paused"].includes(run.state);
|
||||
if (run.file_id && !running) {
|
||||
const again = document.createElement("button");
|
||||
again.type = "button";
|
||||
again.className = "ghost tiny-btn";
|
||||
@@ -235,7 +314,7 @@ function render() {
|
||||
}
|
||||
});
|
||||
});
|
||||
tr.children[8].appendChild(again);
|
||||
actions.appendChild(again);
|
||||
}
|
||||
body.appendChild(tr);
|
||||
});
|
||||
@@ -250,6 +329,8 @@ async function load() {
|
||||
runPage = 1;
|
||||
render();
|
||||
}
|
||||
window.onDruckerLive = () => { load().catch(() => {}); };
|
||||
window.onQueueChanged = () => { load().catch(() => {}); };
|
||||
|
||||
["run-filter-printer", "run-filter-verdict", "run-filter-spool"].forEach((id) => {
|
||||
document.getElementById(id).addEventListener("change", () => { runPage = 1; render(); });
|
||||
|
||||
Reference in new issue
Block a user