104 lines
3.5 KiB
JavaScript
104 lines
3.5 KiB
JavaScript
/* 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 };
|