refactoring
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { pageRoute } from "../routes.js";
|
||||
|
||||
/* Orphaned-upload administration view. */
|
||||
|
||||
/** Format a byte count for the administrative upload list. */
|
||||
function formatFileSize(size) {
|
||||
const bytes = Number(size || 0);
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
}
|
||||
|
||||
class OrphanedUploadsAdmin {
|
||||
/**
|
||||
* goal : Create the controller for uploads without current references.
|
||||
* pre : api performs authenticated requests and translate resolves UI keys.
|
||||
* post : The controller is connected to the orphaned-upload list.
|
||||
* result : A directly usable orphaned-upload administration controller.
|
||||
*/
|
||||
constructor(api, translate) {
|
||||
this.api = api;
|
||||
this.translate = translate;
|
||||
this.list = document.getElementById("orphaned-uploads-list");
|
||||
}
|
||||
|
||||
/**
|
||||
* goal : Load orphaned uploads and their last historical uses.
|
||||
* pre : The current session has administrator permission.
|
||||
* post : The upload list represents the current server state.
|
||||
* result : A promise that resolves when all upload rows have been rendered.
|
||||
*/
|
||||
async load() {
|
||||
const result = await this.api("/api/admin/uploads/orphaned");
|
||||
const uploads = result.uploads || [];
|
||||
this.list.replaceChildren();
|
||||
|
||||
if (uploads.length === 0) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "muted";
|
||||
empty.textContent = this.translate("no-orphaned-uploads", "No orphaned uploads.");
|
||||
this.list.append(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const upload of uploads) this.list.append(this.#renderUpload(upload));
|
||||
}
|
||||
|
||||
/** Build one upload row with history, view link and deletion action. */
|
||||
#renderUpload(upload) {
|
||||
const row = document.createElement("article");
|
||||
row.className = "orphaned-upload-row";
|
||||
|
||||
const title = document.createElement("strong");
|
||||
title.textContent = upload.originalName;
|
||||
|
||||
const meta = document.createElement("div");
|
||||
meta.className = "muted";
|
||||
meta.textContent = `${upload.mimeType} · ${formatFileSize(upload.size)} · ` +
|
||||
`${this.translate("uploaded-by", "uploaded by")} ${upload.uploadedBy}`;
|
||||
|
||||
const history = this.#uploadHistory(upload);
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "orphaned-upload-actions";
|
||||
|
||||
const view = document.createElement("a");
|
||||
view.href = `/uploads/${encodeURIComponent(upload.ownerSlug)}/` +
|
||||
encodeURIComponent(upload.storedName);
|
||||
view.target = "_blank";
|
||||
view.rel = "noopener";
|
||||
view.textContent = this.translate("view", "View");
|
||||
|
||||
const remove = document.createElement("button");
|
||||
remove.className = "danger";
|
||||
remove.textContent = this.translate("delete", "Delete");
|
||||
remove.addEventListener("click", () => {
|
||||
this.#deleteUpload(upload).catch((error) => console.error(error));
|
||||
});
|
||||
|
||||
actions.append(view, remove);
|
||||
row.append(title, meta, history, actions);
|
||||
return row;
|
||||
}
|
||||
|
||||
/** Build the historical-use portion of one upload row. */
|
||||
#uploadHistory(upload) {
|
||||
const history = document.createElement("div");
|
||||
const lastUses = Array.isArray(upload.lastUses) ? upload.lastUses : [];
|
||||
if (lastUses.length === 0) {
|
||||
history.textContent = this.translate(
|
||||
"never-referenced", "Never referenced by a saved page.");
|
||||
return history;
|
||||
}
|
||||
|
||||
const label = document.createElement("div");
|
||||
label.textContent = `${this.translate("last-used", "Last used")}:`;
|
||||
const uses = document.createElement("ul");
|
||||
uses.className = "orphaned-upload-uses";
|
||||
for (const use of lastUses) {
|
||||
const item = document.createElement("li");
|
||||
const link = document.createElement("a");
|
||||
link.href = pageRoute(use.slug);
|
||||
link.textContent = use.title || use.slug;
|
||||
item.append(link);
|
||||
if (use.version) {
|
||||
item.append(document.createTextNode(
|
||||
` · ${this.translate("version", "Version")} ${use.version}`));
|
||||
}
|
||||
uses.append(item);
|
||||
}
|
||||
history.append(label, uses);
|
||||
return history;
|
||||
}
|
||||
|
||||
/** Confirm and remove one still-orphaned upload, then refresh the list. */
|
||||
async #deleteUpload(upload) {
|
||||
const question = this.translate(
|
||||
"delete-orphaned-upload-confirm", "Delete this orphaned upload?");
|
||||
if (!window.confirm(question)) return;
|
||||
await this.api(`/api/admin/uploads/orphaned/${upload.id}`, { method: "DELETE" });
|
||||
await this.load();
|
||||
}
|
||||
}
|
||||
|
||||
export { OrphanedUploadsAdmin };
|
||||
Reference in New Issue
Block a user