/* Archived CMap administration view. */ class ArchivedCmapsAdmin { /** * goal : Create the archived-CMap administration controller. * pre : formatDate formats database timestamps; reloadConceptMaps refreshes * ordinary CMap navigation after a restore. * post : The controller is ready to load and restore archived maps. * result : A directly usable archived-CMap controller. */ constructor(api, translate, formatDate, reloadConceptMaps) { this.api = api; this.translate = translate; this.formatDate = formatDate; this.reloadConceptMaps = reloadConceptMaps; this.list = document.getElementById("archived-cmaps-list"); this.status = document.getElementById("archived-cmaps-status"); } /** * goal : Load and render all archived concept maps. * pre : The current session has administrator permission. * post : The archived-CMap list represents the current server state. * result : A promise that resolves when all map rows have been rendered. */ async load() { this.status.textContent = ""; const result = await this.api("/api/admin/cmaps/archived"); const conceptMaps = Array.isArray(result.conceptMaps) ? result.conceptMaps : []; this.list.replaceChildren(); if (conceptMaps.length === 0) { const empty = document.createElement("p"); empty.className = "muted"; empty.textContent = this.translate( "no-archived-concept-maps", "No archived CMaps."); this.list.append(empty); return; } for (const conceptMap of conceptMaps) { this.list.append(this.#renderConceptMap(conceptMap)); } } /** Build one archived map row and its restore action. */ #renderConceptMap(conceptMap) { const row = document.createElement("article"); row.className = "archived-cmap-row"; const title = document.createElement("strong"); title.textContent = conceptMap.title; const archivedAt = conceptMap.archivedAt ? this.formatDate(conceptMap.archivedAt) : ""; const meta = document.createElement("div"); meta.className = "muted"; meta.textContent = [ `cmap:${conceptMap.slug}`, archivedAt ? `${this.translate("archived", "Archived")} ${archivedAt}` : this.translate("archived", "Archived"), conceptMap.archivedBy ? `${this.translate("by", "by")} ${conceptMap.archivedBy}` : "" ].filter(Boolean).join(" ยท "); const restore = document.createElement("button"); restore.type = "button"; restore.textContent = this.translate("restore-concept-map", "Restore CMap"); restore.addEventListener("click", () => { this.#restore(conceptMap, restore).catch((error) => console.error(error)); }); row.append(title, meta, restore); return row; } /** Confirm and restore one CMap, then refresh both CMap catalogues. */ async #restore(conceptMap, button) { const question = this.translate( "restore-concept-map-confirm", "Restore concept map \"{title}\"?") .replace("{title}", conceptMap.title); if (!window.confirm(question)) return; button.disabled = true; try { await this.api( `/api/admin/cmaps/archived/${encodeURIComponent(conceptMap.slug)}/restore`, { method: "POST" }); await this.reloadConceptMaps(); await this.load(); this.status.textContent = this.translate( "concept-map-restored", "CMap restored."); } catch (error) { button.disabled = false; this.status.textContent = error.message; } } } export { ArchivedCmapsAdmin };