forked from UKSOURCE/cms.lams
Improve the CMS administration interface across multiple pages (Accreditation, Admissions, History, Partnerships, and Policies) with a focus on usability and data integrity. Key changes include: - Implement `ensureUniqueIds` utility to automatically generate and maintain unique slugs for content items, removing the need for manual ID entry in the UI. - Refactor the Admissions calculator to support detailed per-option editing via a new dedicated view and routes. - Replace basic datalists with a custom, searchable icon combobox component for better visual selection. - Update `_renderSingletonPageView` to handle active tab persistence via query parameters. - Streamline editor configurations by removing redundant fields and improving help text. - Enhance the Admissions "Key Dates" editor with a dynamic table interface for managing columns and rows. - Normalize data payloads in controllers to ensure consistent API responses and internal linking.
126 lines
3.7 KiB
JavaScript
126 lines
3.7 KiB
JavaScript
const HistoryPage = require("../models/historyPage");
|
|
const AUDIT_ACTIONS = require("../constants/auditAction");
|
|
const historyConfig = require("../utils/contentEditors/historyConfig");
|
|
const createPageContentController = require("./_createPageContentController");
|
|
const createRenderSingletonPageView = require("./_renderSingletonPageView");
|
|
const { ensureUniqueIds } = require("../utils/contentEditorIds");
|
|
|
|
function getFrontendUrl(req) {
|
|
return (process.env.FRONTEND_URL || "http://localhost:3000").replace(/\/$/, "");
|
|
}
|
|
|
|
function normalizeInternalHistoryHref(rawHref, req) {
|
|
const href = String(rawHref || "").trim();
|
|
|
|
if (!href) {
|
|
return "";
|
|
}
|
|
|
|
if (href.startsWith("#")) {
|
|
return href;
|
|
}
|
|
|
|
if (href.startsWith("/")) {
|
|
return href;
|
|
}
|
|
|
|
const frontendUrl = getFrontendUrl(req);
|
|
|
|
try {
|
|
const url = new URL(href);
|
|
const frontendOrigin = new URL(frontendUrl).origin;
|
|
|
|
if (url.origin !== frontendOrigin) {
|
|
throw new Error("Highlight link only supports internal anchors or frontend paths.");
|
|
}
|
|
|
|
return `${url.pathname}${url.search}${url.hash}` || "/";
|
|
} catch (error) {
|
|
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
throw new Error("Highlight link only supports internal anchors or frontend paths.");
|
|
}
|
|
|
|
return href.startsWith("?") ? `/about/history${href}` : `/${href.replace(/^\/+/, "")}`;
|
|
}
|
|
}
|
|
|
|
function normalizeHistoryForApi(rawData, req) {
|
|
const data = JSON.parse(JSON.stringify(rawData || {}));
|
|
const href = data?.highlight?.href;
|
|
const frontendUrl = getFrontendUrl(req);
|
|
const backendUrl = `${req.protocol}://${req.get("host")}`.replace(/\/$/, "");
|
|
|
|
if (!href || href.startsWith("#")) {
|
|
return data;
|
|
}
|
|
|
|
if (/^https?:\/\//i.test(href)) {
|
|
try {
|
|
const url = new URL(href);
|
|
const frontendOrigin = new URL(frontendUrl).origin;
|
|
const backendOrigin = new URL(backendUrl).origin;
|
|
|
|
if (url.origin === frontendOrigin) {
|
|
data.highlight.href = `${frontendUrl}${url.pathname}${url.search}${url.hash}`;
|
|
return data;
|
|
}
|
|
|
|
if (url.origin === backendOrigin) {
|
|
data.highlight.href = `${frontendUrl}/about/history${url.hash || ""}`;
|
|
return data;
|
|
}
|
|
} catch {
|
|
data.highlight.href = `${frontendUrl}/about/history`;
|
|
return data;
|
|
}
|
|
|
|
data.highlight.href = `${frontendUrl}/about/history`;
|
|
return data;
|
|
}
|
|
|
|
data.highlight.href = `${frontendUrl}${href.startsWith("/") ? href : `/${href}`}`;
|
|
return data;
|
|
}
|
|
|
|
const controller = createPageContentController({
|
|
model: HistoryPage,
|
|
modelName: "HistoryPage",
|
|
auditAction: AUDIT_ACTIONS.UPDATE_HISTORY,
|
|
editorConfig: historyConfig,
|
|
preparePayload(rawPayload, { req }) {
|
|
const payload = JSON.parse(JSON.stringify(rawPayload || {}));
|
|
|
|
if (payload.highlight) {
|
|
payload.highlight.href = normalizeInternalHistoryHref(payload.highlight.href, req);
|
|
}
|
|
|
|
if (payload.timeline && Array.isArray(payload.timeline.items)) {
|
|
payload.timeline.items = ensureUniqueIds(
|
|
payload.timeline.items,
|
|
(item) => item.id,
|
|
(item, index) => item.title || item.year || `milestone-${index + 1}`,
|
|
"milestone",
|
|
);
|
|
}
|
|
|
|
return payload;
|
|
},
|
|
normalizeForApi: normalizeHistoryForApi,
|
|
});
|
|
|
|
controller.index = async function index(req, res) {
|
|
try {
|
|
return await createRenderSingletonPageView({
|
|
model: HistoryPage,
|
|
editorConfig: historyConfig,
|
|
view: "admin/history/index",
|
|
})(req, res);
|
|
} catch (error) {
|
|
console.error("history index error:", error);
|
|
req.flash("error_msg", "Error loading History Management");
|
|
return req.session.save(() => res.redirect("/admin/dashboard"));
|
|
}
|
|
};
|
|
|
|
module.exports = controller;
|