Grotti 3.3.0: Projekte mit Ordner, Datei-Löschen/Verschieben, Material-Archiv, UI-Polish.
This commit is contained in:
1 parent
4ddffbbeb1
commit
67a89d1794
45 files changed
+2856
-320
No files matched your search
+118
-5
@@ -40,6 +40,11 @@
|
||||
<button type="button" class="fchip" data-filter="loaded">Am Drucker</button>
|
||||
<button type="button" class="fchip" data-filter="shelf">Regal</button>
|
||||
<button type="button" class="fchip" data-filter="low">Wenig Rest</button>
|
||||
<button type="button" class="fchip" data-filter="archive">Archiv</button>
|
||||
</div>
|
||||
<div class="mat-extra-filters">
|
||||
<select id="spool-filter-type" title="Sorte"><option value="">Alle Sorten</option></select>
|
||||
<select id="spool-filter-color" title="Farbe"><option value="">Alle Farben</option></select>
|
||||
</div>
|
||||
<div class="search-wrap">
|
||||
<svg class="ico"><use href="#i-search"/></svg>
|
||||
@@ -141,6 +146,9 @@
|
||||
<div class="actions">
|
||||
<button type="submit" class="btn-primary">Speichern</button>
|
||||
<button type="button" class="ghost" id="spool-shelf" hidden>Ins Regal</button>
|
||||
<button type="button" class="ghost" id="spool-empty" hidden title="Rest auf 0, Regal, archivieren">Leer</button>
|
||||
<button type="button" class="ghost" id="spool-archive" hidden>Archivieren</button>
|
||||
<button type="button" class="ghost" id="spool-restore" hidden>Wiederherstellen</button>
|
||||
<button type="button" class="ghost" id="spool-label" hidden>Etikett</button>
|
||||
<button type="button" class="warn" id="spool-delete" hidden><svg class="ico"><use href="#i-trash"/></svg>Löschen</button>
|
||||
</div>
|
||||
@@ -237,6 +245,8 @@ let picked = [];
|
||||
let colorLabel = "";
|
||||
let autoName = "";
|
||||
let spoolFilter = "all";
|
||||
let spoolTypeFilter = "";
|
||||
let spoolColorFilter = "";
|
||||
let spoolSearch = "";
|
||||
let sortenSearch = "";
|
||||
let view = "spools";
|
||||
@@ -556,6 +566,9 @@ function fill(spool) {
|
||||
document.getElementById("spool-delete").hidden = !spool;
|
||||
document.getElementById("spool-label").hidden = !spool;
|
||||
document.getElementById("spool-shelf").hidden = !spool || !(spool.printer_host);
|
||||
document.getElementById("spool-empty").hidden = !spool || spool.archived;
|
||||
document.getElementById("spool-archive").hidden = !spool || spool.archived;
|
||||
document.getElementById("spool-restore").hidden = !spool || !spool.archived;
|
||||
document.getElementById("color-box").open = !spool || !picked.length;
|
||||
showHint();
|
||||
renderPalette();
|
||||
@@ -583,12 +596,30 @@ function fillSorten(material) {
|
||||
renderSorten();
|
||||
}
|
||||
|
||||
function isSpoolArchived(spool) {
|
||||
return !!(spool && (spool.archived || spool.archived_at));
|
||||
}
|
||||
|
||||
function isSpoolEmpty(spool) {
|
||||
return (Number(spool.remaining_g) || 0) <= 0.5;
|
||||
}
|
||||
|
||||
function shownSpools() {
|
||||
const q = spoolSearch.trim().toLowerCase();
|
||||
const type = spoolTypeFilter.trim().toLowerCase();
|
||||
const color = spoolColorFilter.trim().toLowerCase();
|
||||
return spools.filter((spool) => {
|
||||
const archived = isSpoolArchived(spool) || isSpoolEmpty(spool);
|
||||
if (spoolFilter === "archive") {
|
||||
if (!archived) return false;
|
||||
} else if (archived) {
|
||||
return false;
|
||||
}
|
||||
if (spoolFilter === "loaded" && !spool.printer_host) return false;
|
||||
if (spoolFilter === "shelf" && spool.printer_host) return false;
|
||||
if (spoolFilter === "low" && (Number(spool.remaining_g) || 0) >= 100) return false;
|
||||
if (type && (spool.material_type || "").toLowerCase() !== type) return false;
|
||||
if (color && (spool.color || "").toLowerCase() !== color) return false;
|
||||
if (!q) return true;
|
||||
const hay = [
|
||||
spool.name, spool.chargen_nr, spool.code, spool.material_type, spool.color,
|
||||
@@ -598,6 +629,28 @@ function shownSpools() {
|
||||
});
|
||||
}
|
||||
|
||||
function fillSpoolMetaFilters() {
|
||||
const typeSel = document.getElementById("spool-filter-type");
|
||||
const colorSel = document.getElementById("spool-filter-color");
|
||||
if (!typeSel || !colorSel) return;
|
||||
const types = [...new Set(spools.map((s) => (s.material_type || "").trim()).filter(Boolean))]
|
||||
.sort((a, b) => a.localeCompare(b, "de"));
|
||||
const colors = [...new Set(spools.map((s) => (s.color || "").trim()).filter(Boolean))]
|
||||
.sort((a, b) => a.localeCompare(b, "de"));
|
||||
const curType = spoolTypeFilter;
|
||||
const curColor = spoolColorFilter;
|
||||
typeSel.innerHTML = '<option value="">Alle Sorten</option>' + types.map((t) =>
|
||||
`<option value="${escapeHtml(t)}">${escapeHtml(t)}</option>`
|
||||
).join("");
|
||||
colorSel.innerHTML = '<option value="">Alle Farben</option>' + colors.map((c) =>
|
||||
`<option value="${escapeHtml(c)}">${escapeHtml(c)}</option>`
|
||||
).join("");
|
||||
typeSel.value = types.includes(curType) ? curType : "";
|
||||
colorSel.value = colors.includes(curColor) ? curColor : "";
|
||||
spoolTypeFilter = typeSel.value;
|
||||
spoolColorFilter = colorSel.value;
|
||||
}
|
||||
|
||||
function shownSorten() {
|
||||
const q = sortenSearch.trim().toLowerCase();
|
||||
if (!q) return materials;
|
||||
@@ -635,11 +688,15 @@ function renderSpools() {
|
||||
btn.classList.toggle("on", (btn.dataset.filter || "all") === spoolFilter);
|
||||
});
|
||||
fillSelects();
|
||||
fillSpoolMetaFilters();
|
||||
|
||||
const body = document.getElementById("spool-rows");
|
||||
body.innerHTML = "";
|
||||
const shown = shownSpools();
|
||||
document.getElementById("spool-count").textContent = String(shown.length);
|
||||
const activeCount = spools.filter((s) => !isSpoolArchived(s) && !isSpoolEmpty(s)).length;
|
||||
document.getElementById("spool-count").textContent = String(
|
||||
spoolFilter === "archive" ? shown.length : activeCount
|
||||
);
|
||||
const note = document.getElementById("spool-list-note");
|
||||
if (!spools.length) {
|
||||
note.textContent = "";
|
||||
@@ -651,9 +708,11 @@ function renderSpools() {
|
||||
body.innerHTML = '<tr><td class="empty" colspan="6"><div class="empty-state">' + icon("search") + '<p>Keine Spule passt.</p></div></td></tr>';
|
||||
return;
|
||||
}
|
||||
note.textContent = shown.length === spools.length
|
||||
? (shown.length === 1 ? "1 Spule" : shown.length + " Spulen")
|
||||
: shown.length + " von " + spools.length;
|
||||
note.textContent = spoolFilter === "archive"
|
||||
? (shown.length === 1 ? "1 archivierte Spule" : shown.length + " archivierte Spulen")
|
||||
: shown.length === activeCount
|
||||
? (shown.length === 1 ? "1 Spule" : shown.length + " Spulen")
|
||||
: shown.length + " von " + activeCount;
|
||||
|
||||
const ordered = [...shown].sort((a, b) => {
|
||||
const ah = a.printer_host || "zzz";
|
||||
@@ -671,7 +730,7 @@ function renderSpools() {
|
||||
body.appendChild(head);
|
||||
}
|
||||
const tr = document.createElement("tr");
|
||||
tr.className = "pick";
|
||||
tr.className = "pick" + (isSpoolArchived(spool) || isSpoolEmpty(spool) ? " is-archived" : "");
|
||||
if (spoolPanelOpen && String(spool.id) === document.getElementById("spool-id").value) tr.classList.add("on");
|
||||
tr.innerHTML = "<td></td><td></td><td></td><td></td><td></td><td class=\"num\"></td>";
|
||||
tr.children[0].textContent = spool.name;
|
||||
@@ -876,6 +935,52 @@ document.getElementById("spool-shelf").addEventListener("click", async () => {
|
||||
showNote("spool-note", error);
|
||||
}
|
||||
});
|
||||
document.getElementById("spool-empty").addEventListener("click", () => {
|
||||
const id = document.getElementById("spool-id").value;
|
||||
if (!id) return;
|
||||
armButton(document.getElementById("spool-empty"), "Als leer markieren?", async () => {
|
||||
try {
|
||||
const saved = await api("/spulen/" + id + "/leer", { method: "POST", json: {} });
|
||||
toast("Spule als leer archiviert.");
|
||||
await load();
|
||||
fill(saved);
|
||||
} catch (error) {
|
||||
showNote("spool-note", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.getElementById("spool-archive").addEventListener("click", async () => {
|
||||
const id = document.getElementById("spool-id").value;
|
||||
if (!id) return;
|
||||
try {
|
||||
const saved = await api("/spulen/" + id, {
|
||||
method: "PATCH",
|
||||
json: { archived: true, printer_host: "", ace_slot: null },
|
||||
});
|
||||
toast("Spule archiviert.");
|
||||
await load();
|
||||
fill(saved);
|
||||
} catch (error) {
|
||||
showNote("spool-note", error);
|
||||
}
|
||||
});
|
||||
document.getElementById("spool-restore").addEventListener("click", async () => {
|
||||
const id = document.getElementById("spool-id").value;
|
||||
if (!id) return;
|
||||
try {
|
||||
const left = Number(document.getElementById("spool-left").value) || 0;
|
||||
const saved = await api("/spulen/" + id, {
|
||||
method: "PATCH",
|
||||
json: { archived: false, remaining_g: left > 0.5 ? left : 1000 },
|
||||
});
|
||||
toast("Spule wiederhergestellt.");
|
||||
spoolFilter = "all";
|
||||
await load();
|
||||
fill(saved);
|
||||
} catch (error) {
|
||||
showNote("spool-note", error);
|
||||
}
|
||||
});
|
||||
document.getElementById("spool-label").addEventListener("click", () => {
|
||||
const id = document.getElementById("spool-id").value;
|
||||
if (id) window.open("/etikett/" + id, "_blank", "noopener");
|
||||
@@ -890,6 +995,14 @@ document.getElementById("spool-delete").addEventListener("click", () => {
|
||||
closeSpoolPanel();
|
||||
});
|
||||
});
|
||||
document.getElementById("spool-filter-type")?.addEventListener("change", (event) => {
|
||||
spoolTypeFilter = event.target.value || "";
|
||||
renderSpools();
|
||||
});
|
||||
document.getElementById("spool-filter-color")?.addEventListener("change", (event) => {
|
||||
spoolColorFilter = event.target.value || "";
|
||||
renderSpools();
|
||||
});
|
||||
|
||||
document.getElementById("sorten-new").addEventListener("click", () => {
|
||||
fillSorten(null);
|
||||
|
||||
Reference in new issue
Block a user