forked from UKSOURCE/cms.lams
refactor(policies): migrate to block-based content editor
Replace the legacy section-based policy editor with a flexible block-based system. This transition enables real-time previews, a wider variety of content types (headings, quotes, callouts), and improved data normalization. - Implement `policies-block-editor.js` for dynamic content management - Add `normalizePoliciesDocument` and `validateContent` utilities to handle data migration and integrity - Update `policiesController.js` to support the new block structure and validation logic - Migrate `policies.json` from `sections` array to a `content.blocks` structure - Remove obsolete section editor views and partials - Update `policiesConfig.js` to reflect the new editor capabilities
This commit is contained in:
@@ -10,6 +10,11 @@ const writeAuditLog = require("../audit/writeAuditLog");
|
||||
const diffObject = require("../audit/diffObject");
|
||||
const jsonHelper = require("../utils/jsonHelper");
|
||||
const { ensureUniqueIds } = require("../utils/contentEditorIds");
|
||||
const {
|
||||
normalizePolicy,
|
||||
normalizePoliciesDocument,
|
||||
validateContent,
|
||||
} = require("../utils/policiesBlockContent");
|
||||
|
||||
function formatLastUpdated(date = new Date()) {
|
||||
return `Last updated: ${new Intl.DateTimeFormat("en-US", {
|
||||
@@ -29,10 +34,11 @@ function withLastUpdated(payload, { beforeData } = {}) {
|
||||
"policy",
|
||||
).map((policy) => {
|
||||
const existingPolicy = existingPolicies.find((item) => item.id === policy.id);
|
||||
const normalizedExistingPolicy = normalizePolicy(existingPolicy || policy);
|
||||
|
||||
return {
|
||||
...policy,
|
||||
sections: Array.isArray(existingPolicy?.sections) ? existingPolicy.sections : [],
|
||||
content: normalizedExistingPolicy.content,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -51,6 +57,8 @@ const baseController = createPageContentController({
|
||||
modelName: "PoliciesPage",
|
||||
auditAction: AUDIT_ACTIONS.UPDATE_POLICIES,
|
||||
editorConfig: policiesConfig,
|
||||
normalizeForEditor: normalizePoliciesDocument,
|
||||
normalizeForApi: normalizePoliciesDocument,
|
||||
preparePayload: withLastUpdated,
|
||||
});
|
||||
|
||||
@@ -73,7 +81,7 @@ module.exports = {
|
||||
async editSections(req, res) {
|
||||
try {
|
||||
const doc = await PoliciesPage.getSingle();
|
||||
const data = doc.toObject();
|
||||
const data = normalizePoliciesDocument(doc.toObject());
|
||||
const policy = (data.policies || []).find(
|
||||
(item) => item.id === req.params.policyId,
|
||||
);
|
||||
@@ -95,7 +103,10 @@ module.exports = {
|
||||
layout: "layouts/main",
|
||||
title: editorConfig.title,
|
||||
subtitle: editorConfig.subtitle,
|
||||
data: { sections: policy.sections || [] },
|
||||
data: {
|
||||
policy,
|
||||
content: policy.content,
|
||||
},
|
||||
editorConfig,
|
||||
activeTab: "sections",
|
||||
frontendUrl,
|
||||
@@ -128,9 +139,31 @@ module.exports = {
|
||||
return req.session.save(() => res.redirect("/admin/policies"));
|
||||
}
|
||||
|
||||
doc.policies[policyIndex].sections = Array.isArray(payload.sections)
|
||||
? payload.sections
|
||||
: [];
|
||||
const policyIds = (doc.policies || [])
|
||||
.map((item) => item.id)
|
||||
.filter(Boolean);
|
||||
const validation = validateContent(payload.content, policyIds);
|
||||
|
||||
if (validation.errors.length > 0) {
|
||||
req.flash("error_msg", validation.errors.join(" "));
|
||||
return req.session.save(() =>
|
||||
res.redirect(`/admin/policies/${req.params.policyId}/section`),
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedPolicies = (doc.policies || []).map((item) =>
|
||||
normalizePolicy(JSON.parse(JSON.stringify(item))),
|
||||
);
|
||||
const currentPolicy = normalizedPolicies[policyIndex];
|
||||
const updatedPolicy = {
|
||||
...currentPolicy,
|
||||
content: validation.content,
|
||||
};
|
||||
|
||||
delete updatedPolicy.sections;
|
||||
delete updatedPolicy.contentByLanguage;
|
||||
normalizedPolicies.splice(policyIndex, 1, updatedPolicy);
|
||||
doc.policies = normalizedPolicies;
|
||||
doc.hero = {
|
||||
...(doc.hero || {}),
|
||||
lastUpdated: formatLastUpdated(),
|
||||
@@ -154,14 +187,23 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
const finalData = await PoliciesPage.findOne()
|
||||
const finalData = normalizePoliciesDocument(
|
||||
(await PoliciesPage.findOne()
|
||||
.select("-_id -__v -createdAt -updatedAt")
|
||||
.lean();
|
||||
.lean()) || {},
|
||||
);
|
||||
jsonHelper.writeJsonFile(policiesConfig.dataFile, finalData);
|
||||
|
||||
req.flash("success_msg", "Policy sections updated successfully");
|
||||
const successMessage = validation.warnings.length
|
||||
? `Policy content updated with warnings: ${validation.warnings.join(" ")}`
|
||||
: "Policy content updated successfully";
|
||||
req.flash("success_msg", successMessage);
|
||||
const redirectUrl =
|
||||
req.body.intent === "save-back"
|
||||
? "/admin/policies?tab=policies"
|
||||
: `/admin/policies/${req.params.policyId}/section`;
|
||||
return req.session.save(() =>
|
||||
res.redirect(`/admin/policies/${req.params.policyId}/section`),
|
||||
res.redirect(redirectUrl),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("policies section update error:", error);
|
||||
|
||||
Reference in New Issue
Block a user