add(ui/ux): search all open requests from the import dialog

This commit is contained in:
Steppenstreuner
2026-08-29 10:43:20 +02:00
parent 78d726daaf
commit acaf7c2068
3 changed files with 42 additions and 15 deletions
+37 -14
View File
@@ -1037,6 +1037,7 @@ function openQuickDialog(i) {
$("#quick-episode").textContent = number != null ? `Folge ${number} — nicht mitgesucht` : "";
$("#quick-q").value = cleanFileName(rest);
quickPicked = null;
$("#quick-req-filter").value = "";
$("#quick-m-title").value = cleanFileName(rest);
$("#quick-m-authors").value = "";
$("#quick-m-volume").value = number ?? "";
@@ -1052,31 +1053,49 @@ function openQuickDialog(i) {
runQuickSearch();
}
// offer linking to similar already-open requests instead of creating a duplicate
// offer linking to an already-open request instead of creating a duplicate.
// Without a filter these are the best guesses from the file name; typing turns
// the box into a search over every open request, because the guess can be wrong.
const QUICK_OPEN_LIMIT = 20;
function renderQuickOpenRequests(item) {
const box = $("#quick-open-requests");
const tokens = cleanFileName(item.name).toLowerCase().split(" ").filter((t) => t.length > 2);
const matches = missingReqs
.filter((r) => r.media_type === item.media_type)
.map((r) => {
const hay = `${r.title} ${r.authors} ${r.series} ${r.narrator}`.toLowerCase();
return { r, hits: tokens.filter((t) => hay.includes(t)).length };
})
.filter((m) => m.hits >= 2)
.sort((a, b) => b.hits - a.hits)
.slice(0, 3);
if (!matches.length) { box.innerHTML = ""; return; }
const query = $("#quick-req-filter").value.trim().toLowerCase();
const open = missingReqs.filter((r) => r.media_type === item.media_type);
const hay = (r) => `${r.title} ${r.authors} ${r.series} ${r.narrator} ${r.volume ?? ""}`.toLowerCase();
let matches;
let heading;
if (query) {
const terms = query.split(/\s+/);
matches = open.filter((r) => terms.every((t) => hay(r).includes(t)));
heading = matches.length
? `${Math.min(matches.length, QUICK_OPEN_LIMIT)} von ${matches.length} Treffern (${open.length} offen)`
: `Kein Treffer unter ${open.length} offenen Anfragen`;
matches = matches.slice(0, QUICK_OPEN_LIMIT);
} else {
const tokens = cleanFileName(item.name).toLowerCase().split(" ").filter((t) => t.length > 2);
matches = open
.map((r) => ({ r, hits: tokens.filter((t) => hay(r).includes(t)).length }))
.filter((m) => m.hits >= 2)
.sort((a, b) => b.hits - a.hits)
.slice(0, 3)
.map((m) => m.r);
heading = matches.length ? "Passende offene Requests:" : "";
}
box.innerHTML =
'<p class="muted">Passende offene Requests:</p>' +
(heading ? `<p class="muted">${esc(heading)}</p>` : "") +
matches
.map(
({ r }) => `<div class="row open-req">
(r) => `<div class="row open-req">
<span>${esc(r.title)}${r.volume != null ? " · Band " + r.volume : ""}${esc(r.authors)} (${esc(r.library_name)})</span>
<button class="secondary" data-link-req="${r.id}">Verbinden</button>
</div>`
)
.join("");
box.querySelectorAll("[data-link-req]").forEach((b) =>
b.addEventListener("click", () => {
const i = quickItemIndex;
scanItems[i].request_id = parseInt(b.dataset.linkReq);
@@ -1137,6 +1156,10 @@ async function runQuickSearch() {
}
}
$("#quick-req-filter").addEventListener("input", () => {
if (quickItemIndex != null) renderQuickOpenRequests(scanItems[quickItemIndex]);
});
$("#quick-search-form").addEventListener("submit", (e) => {
e.preventDefault();
runQuickSearch();
+4
View File
@@ -232,6 +232,10 @@
<label class="muted"
>Ziel-Library <select id="quick-library"></select
></label>
<label class="muted"
>Offene Anfragen
<input id="quick-req-filter" placeholder="nach Titel, Autor, Serie filtern…" />
</label>
<div id="quick-scroll">
<div id="quick-open-requests"></div>
<div id="quick-results" class="cards view-list"></div>