add(ui/ux): select all for bulk delete and edit for libraries
This commit is contained in:
+52
-4
@@ -77,10 +77,16 @@ async function loadLibraries() {
|
|||||||
(l) => `<tr>
|
(l) => `<tr>
|
||||||
<td>${esc(l.name)}</td><td>${esc(l.media_type)}</td><td>${esc(l.root_path)}</td>
|
<td>${esc(l.name)}</td><td>${esc(l.media_type)}</td><td>${esc(l.root_path)}</td>
|
||||||
<td class="mono">${esc(l.folder_template)}</td><td class="mono">${esc(l.file_template)}</td>
|
<td class="mono">${esc(l.folder_template)}</td><td class="mono">${esc(l.file_template)}</td>
|
||||||
<td><button class="danger" data-del-lib="${l.id}">Löschen</button></td>
|
<td class="row">
|
||||||
|
<button class="secondary" data-edit-lib="${l.id}">Bearbeiten</button>
|
||||||
|
<button class="danger" data-del-lib="${l.id}">Löschen</button>
|
||||||
|
</td>
|
||||||
</tr>`
|
</tr>`
|
||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
|
tbody.querySelectorAll("[data-edit-lib]").forEach((b) =>
|
||||||
|
b.addEventListener("click", () => startLibraryEdit(parseInt(b.dataset.editLib)))
|
||||||
|
);
|
||||||
tbody.querySelectorAll("[data-del-lib]").forEach((b) =>
|
tbody.querySelectorAll("[data-del-lib]").forEach((b) =>
|
||||||
b.addEventListener("click", async () => {
|
b.addEventListener("click", async () => {
|
||||||
if (!confirm("Library löschen?")) return;
|
if (!confirm("Library löschen?")) return;
|
||||||
@@ -96,16 +102,48 @@ async function loadLibraries() {
|
|||||||
libraries.map((l) => `<option value="${l.id}">${esc(l.name)}</option>`).join("");
|
libraries.map((l) => `<option value="${l.id}">${esc(l.name)}</option>`).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startLibraryEdit(id) {
|
||||||
|
const lib = libraries.find((l) => l.id === id);
|
||||||
|
if (!lib) return;
|
||||||
|
const form = $("#lib-form");
|
||||||
|
form.name.value = lib.name;
|
||||||
|
form.media_type.value = lib.media_type;
|
||||||
|
form.root_path.value = lib.root_path;
|
||||||
|
form.folder_template.value = lib.folder_template;
|
||||||
|
form.file_template.value = lib.file_template;
|
||||||
|
form.dataset.editId = lib.id;
|
||||||
|
$("#lib-form-title").textContent = `Library „${lib.name}“ bearbeiten`;
|
||||||
|
$("#lib-form-submit").textContent = "Speichern";
|
||||||
|
$("#lib-form-cancel").hidden = false;
|
||||||
|
form.scrollIntoView({ behavior: "smooth" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function endLibraryEdit() {
|
||||||
|
const form = $("#lib-form");
|
||||||
|
form.reset();
|
||||||
|
delete form.dataset.editId;
|
||||||
|
$("#lib-form-title").textContent = "Neue Library";
|
||||||
|
$("#lib-form-submit").textContent = "Anlegen";
|
||||||
|
$("#lib-form-cancel").hidden = true;
|
||||||
|
}
|
||||||
|
$("#lib-form-cancel").addEventListener("click", endLibraryEdit);
|
||||||
|
|
||||||
$("#lib-form").addEventListener("submit", async (e) => {
|
$("#lib-form").addEventListener("submit", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data = Object.fromEntries(new FormData(e.target));
|
const data = Object.fromEntries(new FormData(e.target));
|
||||||
if (!data.folder_template) delete data.folder_template;
|
if (!data.folder_template) delete data.folder_template;
|
||||||
if (!data.file_template) delete data.file_template;
|
if (!data.file_template) delete data.file_template;
|
||||||
|
const editId = e.target.dataset.editId;
|
||||||
try {
|
try {
|
||||||
await api("/api/libraries", { method: "POST", body: JSON.stringify(data) });
|
if (editId) {
|
||||||
e.target.reset();
|
await api("/api/libraries/" + editId, { method: "PUT", body: JSON.stringify(data) });
|
||||||
|
toast("Library gespeichert — gilt für künftige Importe");
|
||||||
|
} else {
|
||||||
|
await api("/api/libraries", { method: "POST", body: JSON.stringify(data) });
|
||||||
|
toast("Library angelegt");
|
||||||
|
}
|
||||||
|
endLibraryEdit();
|
||||||
loadLibraries();
|
loadLibraries();
|
||||||
toast("Library angelegt");
|
|
||||||
} catch (err) { toast(err.message, true); }
|
} catch (err) { toast(err.message, true); }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -360,6 +398,16 @@ async function bulkDeleteRequests(status) {
|
|||||||
|
|
||||||
$("#imported-bulk-delete").addEventListener("click", () => bulkDeleteRequests("imported"));
|
$("#imported-bulk-delete").addEventListener("click", () => bulkDeleteRequests("imported"));
|
||||||
|
|
||||||
|
function selectAllRequests(status) {
|
||||||
|
const box = status === "missing" ? $("#missing-list") : $("#imported-list");
|
||||||
|
const checks = [...box.querySelectorAll("[data-sel-req]")];
|
||||||
|
const allChecked = checks.length && checks.every((c) => c.checked);
|
||||||
|
checks.forEach((c) => (c.checked = !allChecked));
|
||||||
|
if (checks.length) checks[0].dispatchEvent(new Event("change"));
|
||||||
|
}
|
||||||
|
$("#missing-select-all").addEventListener("click", () => selectAllRequests("missing"));
|
||||||
|
$("#imported-select-all").addEventListener("click", () => selectAllRequests("imported"));
|
||||||
|
|
||||||
$("#missing-filter-type").addEventListener("change", () => loadRequests("missing"));
|
$("#missing-filter-type").addEventListener("change", () => loadRequests("missing"));
|
||||||
$("#missing-filter-library").addEventListener("change", () => loadRequests("missing"));
|
$("#missing-filter-library").addEventListener("change", () => loadRequests("missing"));
|
||||||
$("#missing-search").addEventListener("input", () => renderRequests("missing"));
|
$("#missing-search").addEventListener("input", () => renderRequests("missing"));
|
||||||
|
|||||||
+7
-2
@@ -87,6 +87,7 @@
|
|||||||
<option value="">Alle Libraries</option>
|
<option value="">Alle Libraries</option>
|
||||||
</select>
|
</select>
|
||||||
<input id="missing-search" placeholder="Filtern (Titel/Autor/Serie)…" />
|
<input id="missing-search" placeholder="Filtern (Titel/Autor/Serie)…" />
|
||||||
|
<button id="missing-select-all" class="secondary">Alle auswählen</button>
|
||||||
<button id="missing-bulk-delete" class="danger" hidden>
|
<button id="missing-bulk-delete" class="danger" hidden>
|
||||||
Ausgewählte entfernen
|
Ausgewählte entfernen
|
||||||
</button>
|
</button>
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
<section id="view-imported" hidden>
|
<section id="view-imported" hidden>
|
||||||
<div class="bar wrap">
|
<div class="bar wrap">
|
||||||
<input id="imported-search" placeholder="Filtern (Titel/Autor/Serie)…" />
|
<input id="imported-search" placeholder="Filtern (Titel/Autor/Serie)…" />
|
||||||
|
<button id="imported-select-all" class="secondary">Alle auswählen</button>
|
||||||
<button id="imported-bulk-delete" class="danger" hidden>
|
<button id="imported-bulk-delete" class="danger" hidden>
|
||||||
Ausgewählte entfernen
|
Ausgewählte entfernen
|
||||||
</button>
|
</button>
|
||||||
@@ -203,7 +205,7 @@
|
|||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<h4>Neue Library</h4>
|
<h4 id="lib-form-title">Neue Library</h4>
|
||||||
<form id="lib-form" class="bar wrap">
|
<form id="lib-form" class="bar wrap">
|
||||||
<input name="name" placeholder="Name (z.B. Kids)" required />
|
<input name="name" placeholder="Name (z.B. Kids)" required />
|
||||||
<select name="media_type">
|
<select name="media_type">
|
||||||
@@ -227,7 +229,10 @@
|
|||||||
name="file_template"
|
name="file_template"
|
||||||
placeholder="Datei-Schema (optional)"
|
placeholder="Datei-Schema (optional)"
|
||||||
class="wide" />
|
class="wide" />
|
||||||
<button type="submit">Anlegen</button>
|
<button type="submit" id="lib-form-submit">Anlegen</button>
|
||||||
|
<button type="button" id="lib-form-cancel" class="secondary" hidden>
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<p class="muted">
|
<p class="muted">
|
||||||
Platzhalter: {Author} {Authors} {Narrator} {Title} {Year} {Series}
|
Platzhalter: {Author} {Authors} {Narrator} {Title} {Year} {Series}
|
||||||
|
|||||||
Reference in New Issue
Block a user