add(core): show less entries in download

This commit is contained in:
Steppenstreuner
2026-08-28 14:30:00 +02:00
parent c4e181b05b
commit 90a50e365f
2 changed files with 62 additions and 25 deletions
+57 -25
View File
@@ -265,7 +265,13 @@ $("#scan-btn").addEventListener("click", async () => {
api("/api/requests?status=missing"), api("/api/requests?status=missing"),
]); ]);
scanItems = scan.items; scanItems = scan.items;
// selection state lives here, not in the DOM, so it survives paging
scanItems.forEach((item) => {
item.request_id = item.suggested_request_id;
item.checked = !!item.suggested_request_id;
});
missingReqs = reqs; missingReqs = reqs;
importPage = 0;
$("#scan-info").textContent = `${scan.items.length} Kandidat(en) in ${scan.download_dir}`; $("#scan-info").textContent = `${scan.items.length} Kandidat(en) in ${scan.download_dir}`;
renderImportTable(); renderImportTable();
} catch (err) { } catch (err) {
@@ -274,22 +280,36 @@ $("#scan-btn").addEventListener("click", async () => {
} }
}); });
let importPage = 0;
const IMPORT_PAGE_SIZE = 25;
function renderImportTable() { function renderImportTable() {
const table = $("#import-table"); const table = $("#import-table");
const tbody = table.querySelector("tbody"); const tbody = table.querySelector("tbody");
if (!scanItems.length) { table.hidden = true; $("#import-btn").hidden = true; return; } if (!scanItems.length) {
tbody.innerHTML = scanItems table.hidden = true;
.map((item, i) => { $("#import-btn").hidden = true;
$("#import-pager").hidden = true;
return;
}
const pages = Math.ceil(scanItems.length / IMPORT_PAGE_SIZE);
importPage = Math.min(importPage, pages - 1);
const start = importPage * IMPORT_PAGE_SIZE;
const pageItems = scanItems.slice(start, start + IMPORT_PAGE_SIZE);
tbody.innerHTML = pageItems
.map((item, k) => {
const i = start + k;
const opts = missingReqs const opts = missingReqs
.filter((r) => r.media_type === item.media_type) .filter((r) => r.media_type === item.media_type)
.map( .map(
(r) => (r) =>
`<option value="${r.id}" ${r.id === item.suggested_request_id ? "selected" : ""}> `<option value="${r.id}" ${r.id === item.request_id ? "selected" : ""}>
${esc(r.title)}${esc(r.authors)} (${esc(r.library_name)})</option>` ${esc(r.title)}${esc(r.authors)} (${esc(r.library_name)})</option>`
) )
.join(""); .join("");
return `<tr> return `<tr>
<td><input type="checkbox" data-check="${i}" ${item.suggested_request_id ? "checked" : ""}></td> <td><input type="checkbox" data-check="${i}" ${item.checked ? "checked" : ""}></td>
<td class="mono">${esc(item.name)}${item.is_dir ? " 📁" : ""}</td> <td class="mono">${esc(item.name)}${item.is_dir ? " 📁" : ""}</td>
<td>${esc(item.media_type)}</td> <td>${esc(item.media_type)}</td>
<td class="row"> <td class="row">
@@ -302,11 +322,34 @@ function renderImportTable() {
.join(""); .join("");
table.hidden = false; table.hidden = false;
$("#import-btn").hidden = false; $("#import-btn").hidden = false;
tbody.querySelectorAll("[data-quick]").forEach((b) => tbody.querySelectorAll("[data-quick]").forEach((b) =>
b.addEventListener("click", () => openQuickDialog(parseInt(b.dataset.quick))) b.addEventListener("click", () => openQuickDialog(parseInt(b.dataset.quick)))
); );
tbody.querySelectorAll("[data-check]").forEach((c) =>
c.addEventListener("change", () => (scanItems[c.dataset.check].checked = c.checked))
);
tbody.querySelectorAll("[data-select]").forEach((s) =>
s.addEventListener("change", () => {
const item = scanItems[s.dataset.select];
item.request_id = s.value ? parseInt(s.value) : null;
item.checked = !!item.request_id;
const check = tbody.querySelector(`[data-check="${s.dataset.select}"]`);
if (check) check.checked = item.checked;
})
);
const selected = scanItems.filter((it) => it.checked && it.request_id).length;
$("#import-pager").hidden = false;
$("#import-page-info").textContent =
`Seite ${importPage + 1}/${pages} · ${scanItems.length} Einträge · ${selected} ausgewählt`;
$("#import-prev").disabled = importPage === 0;
$("#import-next").disabled = importPage >= pages - 1;
} }
$("#import-prev").addEventListener("click", () => { importPage--; renderImportTable(); });
$("#import-next").addEventListener("click", () => { importPage++; renderImportTable(); });
// ---- quick request from a scanned file ---- // ---- quick request from a scanned file ----
let quickItemIndex = null; let quickItemIndex = null;
@@ -381,32 +424,21 @@ async function pickQuickResult(r) {
}), }),
}); });
missingReqs.push(req); missingReqs.push(req);
// add the new request to every dropdown of the same media type, then link this row scanItems[i].request_id = req.id;
const item = scanItems[i]; scanItems[i].checked = true;
scanItems.forEach((it, j) => { renderImportTable();
if (it.media_type !== item.media_type) return;
const sel = document.querySelector(`[data-select="${j}"]`);
const opt = document.createElement("option");
opt.value = req.id;
opt.textContent = `${req.title}${req.authors} (${req.library_name})`;
sel.appendChild(opt);
});
document.querySelector(`[data-select="${i}"]`).value = req.id;
document.querySelector(`[data-check="${i}"]`).checked = true;
$("#quick-dialog").close(); $("#quick-dialog").close();
toast(`${req.title}" angefragt und verbunden`); toast(`${req.title}" angefragt und verbunden`);
} catch (err) { toast(err.message, true); } } catch (err) { toast(err.message, true); }
} }
$("#import-btn").addEventListener("click", async () => { $("#import-btn").addEventListener("click", async () => {
const items = []; const items = scanItems
scanItems.forEach((item, i) => { .filter((item) => item.checked && item.request_id)
const checked = document.querySelector(`[data-check="${i}"]`).checked; .map((item) => ({
const reqId = document.querySelector(`[data-select="${i}"]`).value; path: item.path, is_dir: item.is_dir, files: item.files,
if (checked && reqId) { request_id: item.request_id,
items.push({ path: item.path, is_dir: item.is_dir, files: item.files, request_id: parseInt(reqId) }); }));
}
});
if (!items.length) { toast("Nichts ausgewählt", true); return; } if (!items.length) { toast("Nichts ausgewählt", true); return; }
try { try {
const res = await api("/api/import", { method: "POST", body: JSON.stringify({ items }) }); const res = await api("/api/import", { method: "POST", body: JSON.stringify({ items }) });
+5
View File
@@ -109,6 +109,11 @@
</thead> </thead>
<tbody></tbody> <tbody></tbody>
</table> </table>
<div class="bar" id="import-pager" hidden>
<button id="import-prev" class="secondary">← Zurück</button>
<span id="import-page-info" class="muted"></span>
<button id="import-next" class="secondary">Weiter →</button>
</div>
<div class="bar"> <div class="bar">
<button id="import-btn" hidden>Ausgewählte importieren</button> <button id="import-btn" hidden>Ausgewählte importieren</button>
</div> </div>