Grotti 2.0.0: eigenes Repo, Umbenennung von Farm.
This commit is contained in:
commit
b2e6443c68
69 files changed
+18719
No files matched your search
@@ -0,0 +1,221 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Protokoll</h1>
|
||||
<p class="tiny">Abgeschlossene und laufende Drucke mit Dauer, Material und Kosten.</p>
|
||||
</div>
|
||||
<div class="page-head-side">
|
||||
<button type="button" id="sync"><svg class="ico"><use href="#i-refresh"/></svg>Läufe holen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-tools">
|
||||
<select id="run-filter-printer" title="Drucker">
|
||||
<option value="">Alle Drucker</option>
|
||||
</select>
|
||||
<select id="run-filter-verdict" title="Urteil">
|
||||
<option value="">Alle Urteile</option>
|
||||
<option value="offen">offen</option>
|
||||
<option value="gut">gut</option>
|
||||
<option value="ausschuss">Ausschuss</option>
|
||||
</select>
|
||||
<label class="check"><input type="checkbox" id="run-filter-spool"> Spule fehlt</label>
|
||||
</div>
|
||||
<p id="run-note"></p>
|
||||
<div class="card table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>Drucker</th><th>Datei</th><th>Zustand</th><th>Dauer</th><th>Gramm</th><th>Kosten</th><th>Urteil</th><th>Spule</th><th></th></tr></thead>
|
||||
<tbody id="run-rows"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
<script>
|
||||
const states = {
|
||||
completed: "fertig", cancelled: "abgebrochen", error: "Fehler",
|
||||
in_progress: "druckt", interrupted: "unterbrochen",
|
||||
};
|
||||
let runs = [];
|
||||
let spools = [];
|
||||
|
||||
function fillPrinterFilter() {
|
||||
const select = document.getElementById("run-filter-printer");
|
||||
const current = select.value;
|
||||
const names = [...new Set(runs.map((run) => run.printer_name).filter(Boolean))].sort((a, b) => a.localeCompare(b, "de"));
|
||||
select.innerHTML = '<option value="">Alle Drucker</option>';
|
||||
names.forEach((name) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = name;
|
||||
option.textContent = name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
if ([...select.options].some((o) => o.value === current)) select.value = current;
|
||||
}
|
||||
|
||||
function filteredRuns() {
|
||||
const printer = document.getElementById("run-filter-printer").value;
|
||||
const verdict = document.getElementById("run-filter-verdict").value;
|
||||
const needSpool = document.getElementById("run-filter-spool").checked;
|
||||
return runs.filter((run) => {
|
||||
if (printer && run.printer_name !== printer) return false;
|
||||
if (verdict === "offen" && run.verdict) return false;
|
||||
if (verdict && verdict !== "offen" && run.verdict !== verdict) return false;
|
||||
if (needSpool && !(run.needs_spool || (!run.deducted && run.weight_g > 0))) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function render() {
|
||||
fillPrinterFilter();
|
||||
const body = document.getElementById("run-rows");
|
||||
body.innerHTML = "";
|
||||
const shown = filteredRuns();
|
||||
if (!runs.length) {
|
||||
body.innerHTML = '<tr><td class="empty" colspan="9"><div class="empty-state">' + icon("list") + '<p>Noch kein Lauf.</p><p class="tiny">„Läufe holen“ liest die Drucker aus.</p></div></td></tr>';
|
||||
return;
|
||||
}
|
||||
if (!shown.length) {
|
||||
body.innerHTML = '<tr><td class="empty" colspan="9"><div class="empty-state">' + icon("filter") + '<p>Kein Lauf passt zum Filter.</p></div></td></tr>';
|
||||
return;
|
||||
}
|
||||
shown.forEach((run) => {
|
||||
const tr = document.createElement("tr");
|
||||
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[1].textContent = (run.filename || "").split("/").pop();
|
||||
tr.children[2].textContent = states[run.state] || run.state;
|
||||
tr.children[3].textContent = hours(run.duration_s);
|
||||
tr.children[4].textContent = run.weight_g ? grams(run.weight_g) : "";
|
||||
const cost = Number(run.filament_cost || 0) + Number(run.energy_cost || 0);
|
||||
tr.children[5].textContent = cost ? euro(cost) : "";
|
||||
const verdict = document.createElement("select");
|
||||
[["", "offen"], ["gut", "gut"], ["ausschuss", "Ausschuss"]].forEach(([value, label]) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = label;
|
||||
verdict.appendChild(option);
|
||||
});
|
||||
verdict.value = run.verdict || "";
|
||||
verdict.addEventListener("change", async () => {
|
||||
try {
|
||||
await api("/laeufe/" + run.id, { method: "PATCH", json: { verdict: verdict.value } });
|
||||
} catch (error) {
|
||||
showNote("run-note", error);
|
||||
}
|
||||
});
|
||||
tr.children[6].appendChild(verdict);
|
||||
if (run.needs_spool || !run.deducted) {
|
||||
const select = document.createElement("select");
|
||||
const blank = document.createElement("option");
|
||||
blank.value = "";
|
||||
blank.textContent = run.needs_spool ? "Spule wählen" : "keine";
|
||||
select.appendChild(blank);
|
||||
const mine = spools.filter((s) => s.printer_host === run.printer_host);
|
||||
const others = spools.filter((s) => s.printer_host !== run.printer_host);
|
||||
const addOpt = (spool, prefix) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = spool.id;
|
||||
option.textContent = (prefix ? prefix + " " : "") + spool.name;
|
||||
select.appendChild(option);
|
||||
};
|
||||
mine.forEach((s) => addOpt(s, ""));
|
||||
if (mine.length && others.length) {
|
||||
const sep = document.createElement("option");
|
||||
sep.disabled = true;
|
||||
sep.textContent = "— andere —";
|
||||
select.appendChild(sep);
|
||||
}
|
||||
others.forEach((s) => addOpt(s, s.printer_host ? "·" : "Regal ·"));
|
||||
if (run.spool_id) select.value = String(run.spool_id);
|
||||
select.addEventListener("change", async () => {
|
||||
if (!select.value) return;
|
||||
try {
|
||||
await api("/laeufe/" + run.id, { method: "PATCH", json: { spool_id: Number(select.value) } });
|
||||
load();
|
||||
} catch (error) {
|
||||
showNote("run-note", error);
|
||||
}
|
||||
});
|
||||
tr.children[7].appendChild(select);
|
||||
} else {
|
||||
const spool = spools.find((item) => item.id === run.spool_id);
|
||||
tr.children[7].textContent = spool ? spool.name : "abgezogen";
|
||||
}
|
||||
if (run.file_id) {
|
||||
const again = document.createElement("button");
|
||||
again.type = "button";
|
||||
again.className = "ghost tiny-btn";
|
||||
again.textContent = "Nochmal";
|
||||
again.title = "Denselben Druck in „wenn frei“ einreihen";
|
||||
again.addEventListener("click", () => {
|
||||
armButton(again, "Nochmal einreihen?", async () => {
|
||||
again.disabled = true;
|
||||
try {
|
||||
let aceMap = [];
|
||||
let bedLevel = false;
|
||||
try {
|
||||
const last = await api(
|
||||
"/senden-zuletzt?host=" + encodeURIComponent(run.printer_host || "")
|
||||
+ "&file_id=" + encodeURIComponent(run.file_id)
|
||||
);
|
||||
if (last) {
|
||||
aceMap = last.ace_map || [];
|
||||
bedLevel = !!last.bed_leveling;
|
||||
}
|
||||
} catch (error) { /* ohne Map weiter */ }
|
||||
const payload = await api("/senden", {
|
||||
method: "POST",
|
||||
json: {
|
||||
file_id: run.file_id,
|
||||
hosts: run.printer_host ? [run.printer_host] : [],
|
||||
spool_id: run.spool_id || null,
|
||||
confirm: true,
|
||||
when_free: true,
|
||||
ace_maps: run.printer_host && aceMap.length
|
||||
? { [run.printer_host]: aceMap }
|
||||
: {},
|
||||
bed_leveling: bedLevel,
|
||||
},
|
||||
});
|
||||
toast((payload.count || (payload.jobs || []).length || 1) + " in Warteschlange „wenn frei“.");
|
||||
if (typeof refreshPrintQueue === "function") refreshPrintQueue();
|
||||
} catch (error) {
|
||||
toast(error.message, "bad");
|
||||
} finally {
|
||||
again.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
tr.children[8].appendChild(again);
|
||||
}
|
||||
body.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
async function load() {
|
||||
[runs, spools] = await Promise.all([api("/laeufe"), api("/spulen")]);
|
||||
render();
|
||||
}
|
||||
|
||||
["run-filter-printer", "run-filter-verdict", "run-filter-spool"].forEach((id) => {
|
||||
document.getElementById(id).addEventListener("change", render);
|
||||
document.getElementById(id).addEventListener("input", render);
|
||||
});
|
||||
|
||||
document.getElementById("sync").addEventListener("click", async () => {
|
||||
const button = document.getElementById("sync");
|
||||
button.disabled = true;
|
||||
try {
|
||||
const result = await api("/laeufe/abgleich", { method: "POST" });
|
||||
document.getElementById("run-note").textContent = result.gelesen + " Läufe, " + result.abgezogen + " abgezogen.";
|
||||
document.getElementById("run-note").className = "ok";
|
||||
await load();
|
||||
} catch (error) {
|
||||
showNote("run-note", error);
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
load().catch((error) => showNote("run-note", error));
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user