forked from UKSOURCE/cms.lams
feat(cms): enhance content editors and implement automatic ID generation
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.
This commit is contained in:
@@ -3,12 +3,109 @@ 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) {
|
||||
|
||||
Reference in New Issue
Block a user