refactoring

This commit is contained in:
2026-09-02 08:38:03 +02:00
parent 649ff0d7c5
commit 38f255c1a4
48 changed files with 6998 additions and 5577 deletions
@@ -0,0 +1,103 @@
/* 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 };