diff --git a/static/app.js b/static/app.js
index fef8863..154b23c 100644
--- a/static/app.js
+++ b/static/app.js
@@ -292,13 +292,110 @@ function renderImportTable() {
|
${esc(item.name)}${item.is_dir ? " đ" : ""} |
${esc(item.media_type)} |
- |
+
+
+
+ |
${item.suggested_request_id ? item.score : "â"} |
`;
})
.join("");
table.hidden = false;
$("#import-btn").hidden = false;
+ tbody.querySelectorAll("[data-quick]").forEach((b) =>
+ b.addEventListener("click", () => openQuickDialog(parseInt(b.dataset.quick)))
+ );
+}
+
+// ---- quick request from a scanned file ----
+let quickItemIndex = null;
+
+function cleanFileName(name) {
+ return name
+ .replace(/[._\-\[\]()]+/g, " ")
+ .replace(/\b(mp3|m4b|flac|epub|pdf|cbz|cbr|retail|unabridged|kompl.*)\b/gi, " ")
+ .replace(/\s+/g, " ")
+ .trim();
+}
+
+function openQuickDialog(i) {
+ quickItemIndex = i;
+ const item = scanItems[i];
+ $("#quick-file").textContent = item.name;
+ $("#quick-q").value = cleanFileName(item.name);
+ $("#quick-library").innerHTML =
+ libOptions(item.media_type) || "";
+ $("#quick-results").innerHTML = "";
+ $("#quick-dialog").showModal();
+ runQuickSearch();
+}
+
+async function runQuickSearch() {
+ const item = scanItems[quickItemIndex];
+ const box = $("#quick-results");
+ box.innerHTML = "Suche läuftâŚ
";
+ try {
+ const results = await api(
+ `/api/search?media_type=${item.media_type}&q=${encodeURIComponent($("#quick-q").value)}`
+ );
+ if (!results.length) { box.innerHTML = "Keine Treffer â Suchbegriff anpassen.
"; return; }
+ box.innerHTML = results
+ .map(
+ (r, j) => `
+ ${r.cover_url ? `
})
` : '
?
'}
+
+
${esc(r.title)}
+
${esc(r.authors)}
+
${r.year ?? ""}
+
+
+
`
+ )
+ .join("");
+ box.querySelectorAll("[data-pick]").forEach((btn) =>
+ btn.addEventListener("click", () => pickQuickResult(results[btn.dataset.pick]))
+ );
+ } catch (err) {
+ box.innerHTML = "";
+ toast(err.message, true);
+ }
+}
+
+$("#quick-search-form").addEventListener("submit", (e) => {
+ e.preventDefault();
+ runQuickSearch();
+});
+$("#quick-close").addEventListener("click", () => $("#quick-dialog").close());
+
+async function pickQuickResult(r) {
+ const libId = $("#quick-library").value;
+ if (!libId) { toast("Erst eine Library fĂźr diesen Typ anlegen", true); return; }
+ const i = quickItemIndex;
+ try {
+ const req = await api("/api/requests", {
+ method: "POST",
+ body: JSON.stringify({
+ library_id: parseInt(libId),
+ title: r.title, authors: r.authors, external_id: r.external_id,
+ year: r.year, series: r.series, cover_url: r.cover_url,
+ }),
+ });
+ missingReqs.push(req);
+ // add the new request to every dropdown of the same media type, then link this row
+ const item = scanItems[i];
+ scanItems.forEach((it, j) => {
+ 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();
+ toast(`â${req.title}" angefragt und verbunden`);
+ } catch (err) { toast(err.message, true); }
}
$("#import-btn").addEventListener("click", async () => {
diff --git a/static/index.html b/static/index.html
index 57190c3..216288a 100644
--- a/static/index.html
+++ b/static/index.html
@@ -113,6 +113,24 @@
+
+
diff --git a/static/style.css b/static/style.css
index 7c88e61..6ebd688 100644
--- a/static/style.css
+++ b/static/style.css
@@ -39,6 +39,8 @@ td select { max-width: 340px; }
dialog { background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 1.2rem; min-width: 340px; }
dialog::backdrop { background: rgba(0,0,0,0.6); }
dialog label { display: flex; flex-direction: column; gap: 0.2rem; margin: 0.5rem 0; font-size: 0.9rem; color: var(--muted); }
+#quick-dialog { min-width: 480px; max-width: 640px; }
+#quick-results { max-height: 50vh; overflow-y: auto; margin-top: 0.6rem; }
.ok { color: #6fbf73; }
.error { color: #e08585; }
#toast { position: fixed; bottom: 1.2rem; right: 1.2rem; background: var(--accent); color: #fff; padding: 0.7rem 1.1rem; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.4); }