diff --git a/static/app.js b/static/app.js index 63096e9..efce031 100644 --- a/static/app.js +++ b/static/app.js @@ -93,7 +93,7 @@ async function refreshMissingBadge() { // ---- navigation ---- // the view lives in the hash so reload, back button and links keep it -const VIEWS = ["search", "missing", "imported", "import", "settings"]; +const VIEWS = ["search", "missing", "imported", "import", "activity", "settings"]; function showView() { const hash = location.hash.slice(1); @@ -101,6 +101,7 @@ function showView() { $$("nav button[data-view]").forEach((b) => b.classList.toggle("active", b.dataset.view === name)); $$("main > section").forEach((s) => (s.hidden = s.id !== "view-" + name)); if (name === "missing" || name === "imported") loadRequests(name); + if (name === "activity") loadHistory(); if (name === "settings") loadLibraries().catch((err) => toast(err.message, true)); } @@ -622,6 +623,39 @@ $("#imported-search").addEventListener("input", () => renderRequests("imported") $("#missing-bulk-delete").addEventListener("click", () => bulkDeleteRequests("missing")); +// ---- activity ---- +let historyRows = []; +const HISTORY_STATE = { imported: "done", appended: "done", failed: "failed" }; + +async function loadHistory() { + try { + historyRows = await api("/api/history"); + } catch (err) { + toast(err.message, true); + } + renderHistory(); +} + +function renderHistory() { + const tbody = $("#history-table tbody"); + if (!historyRows.length) { + tbody.innerHTML = `${t("common.nothingHere")}`; + return; + } + const locale = lang === "de" ? "de-DE" : "en-GB"; + tbody.innerHTML = historyRows + .map((h) => { + const where = h.message || (h.source && h.dest ? `${h.source} → ${h.dest}` : h.dest || h.source); + return ` + ${esc(new Date(h.at).toLocaleString(locale))} + ${esc(t("history." + h.event))} + ${esc(h.title)} ${esc(h.media_type)} + ${esc(where)} + `; + }) + .join(""); +} + // ---- request detail dialog ---- let detailRequest = null; @@ -1758,6 +1792,7 @@ function onLangChange() { renderSearchResults(); renderRequests("missing"); renderRequests("imported"); + renderHistory(); if (libraries.length) loadLibraries(); const editId = $("#lib-form").dataset.editId; editId ? startLibraryEdit(parseInt(editId)) : endLibraryEdit(); diff --git a/static/i18n.js b/static/i18n.js index 5920450..f0755a7 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -46,7 +46,16 @@ const STRINGS = { "nav.imported": "Importiert", "nav.import": "Import", "nav.settings": "Libraries", + "nav.activity": "Aktivität", "nav.langToggle": "Switch to English", + "history.time": "Zeit", + "history.event": "Ereignis", + "history.imported": "importiert", + "history.appended": "ergänzt", + "history.relocated": "verschoben", + "history.retagged": "neu getaggt", + "history.deleted": "gelöscht", + "history.failed": "fehlgeschlagen", "search.languageTitle": "Sprache der Ausgabe — filtert Ebook- und Audiobook-Suche", "search.allLanguages": "Alle Sprachen", @@ -270,7 +279,16 @@ const STRINGS = { "nav.imported": "Imported", "nav.import": "Import", "nav.settings": "Libraries", + "nav.activity": "Activity", "nav.langToggle": "Auf Deutsch umschalten", + "history.time": "Time", + "history.event": "Event", + "history.imported": "imported", + "history.appended": "appended", + "history.relocated": "relocated", + "history.retagged": "retagged", + "history.deleted": "deleted", + "history.failed": "failed", "search.languageTitle": "Edition language — filters the ebook and audiobook search", "search.allLanguages": "All languages", diff --git a/static/index.html b/static/index.html index 7de74ab..43cb977 100644 --- a/static/index.html +++ b/static/index.html @@ -20,6 +20,7 @@ + @@ -269,6 +270,22 @@ + +