add(ui): adding loading circle

This commit is contained in:
Steppenstreuner
2026-08-28 16:01:35 +02:00
parent 90a50e365f
commit 077b75bbb4
3 changed files with 26 additions and 1 deletions
+14 -1
View File
@@ -257,7 +257,9 @@ let scanItems = [];
let missingReqs = [];
$("#scan-btn").addEventListener("click", async () => {
$("#scan-info").textContent = "Scanne…";
const scanBtn = $("#scan-btn");
scanBtn.disabled = true;
$("#scan-info").innerHTML = '<span class="spinner"></span> Scanne…';
try {
const split = $("#split-dirs").checked;
const [scan, reqs] = await Promise.all([
@@ -277,6 +279,8 @@ $("#scan-btn").addEventListener("click", async () => {
} catch (err) {
$("#scan-info").textContent = "";
toast(err.message, true);
} finally {
scanBtn.disabled = false;
}
});
@@ -440,6 +444,9 @@ $("#import-btn").addEventListener("click", async () => {
request_id: item.request_id,
}));
if (!items.length) { toast("Nichts ausgewählt", true); return; }
const btn = $("#import-btn");
btn.disabled = true;
btn.innerHTML = `<span class="spinner"></span> Importiere ${items.length} Einträge…`;
try {
const res = await api("/api/import", { method: "POST", body: JSON.stringify({ items }) });
$("#import-results").innerHTML = res.results
@@ -449,8 +456,14 @@ $("#import-btn").addEventListener("click", async () => {
: `<p class="error">✗ ${esc(r.path)}: ${esc(r.error)}</p>`
)
.join("");
const failed = res.results.filter((r) => !r.ok).length;
toast(failed ? `Import fertig, ${failed} Fehler` : "Import fertig", failed > 0);
$("#scan-btn").click();
} catch (err) { toast(err.message, true); }
finally {
btn.disabled = false;
btn.textContent = "Ausgewählte importieren";
}
});
// ---- init ----
+6
View File
@@ -39,6 +39,12 @@ 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); }
.spinner {
display: inline-block; width: 0.9em; height: 0.9em; vertical-align: -0.12em;
border: 2px solid rgba(255,255,255,0.35); border-top-color: #fff;
border-radius: 50%; animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#quick-dialog { min-width: 480px; max-width: 640px; }
#quick-results { max-height: 50vh; overflow-y: auto; margin-top: 0.6rem; }
.ok { color: #6fbf73; }
+6
View File
@@ -31,6 +31,7 @@ class BookRequest(Base):
library_id: Mapped[int] = mapped_column(ForeignKey("libraries.id"))
title: Mapped[str] = mapped_column(String)
authors: Mapped[str] = mapped_column(String, default="") # comma-separated
narrator: Mapped[str] = mapped_column(String, default="")
external_id: Mapped[str] = mapped_column(String, default="") # ISBN / ASIN / AniList id
year: Mapped[int | None] = mapped_column(default=None)
series: Mapped[str] = mapped_column(String, default="")
@@ -55,6 +56,11 @@ def init_db(db_path=None):
path.parent.mkdir(parents=True, exist_ok=True)
_engine = create_engine(f"sqlite:///{path}", connect_args={"check_same_thread": False})
Base.metadata.create_all(_engine)
# lightweight migration: create_all doesn't add new columns to existing tables
with _engine.begin() as conn:
cols = [row[1] for row in conn.exec_driver_sql("PRAGMA table_info(requests)")]
if "narrator" not in cols:
conn.exec_driver_sql("ALTER TABLE requests ADD COLUMN narrator VARCHAR NOT NULL DEFAULT ''")
SessionLocal = sessionmaker(bind=_engine, expire_on_commit=False)
return _engine