const PoliciesPage = require("../models/policiesPage"); const AUDIT_ACTIONS = require("../constants/auditAction"); const { baseConfig: policiesConfig, createPoliciesSectionEditorConfig, } = require("../utils/contentEditors/policiesConfig"); const createPageContentController = require("./_createPageContentController"); const createRenderSingletonPageView = require("./_renderSingletonPageView"); 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", { year: "numeric", month: "long", day: "numeric", }).format(date)}`; } function withLastUpdated(payload, { beforeData } = {}) { const existingPolicies = Array.isArray(beforeData?.policies) ? beforeData.policies : []; const policies = ensureUniqueIds( Array.isArray(payload.policies) ? payload.policies : [], (policy) => policy.id, (policy, index) => policy.navLabel || policy.title || `policy-${index + 1}`, "policy", ).map((policy) => { const existingPolicy = existingPolicies.find((item) => item.id === policy.id); const normalizedExistingPolicy = normalizePolicy(existingPolicy || policy); return { ...policy, content: normalizedExistingPolicy.content, }; }); return { ...payload, policies, hero: { ...(payload.hero || {}), lastUpdated: formatLastUpdated(), }, }; } const baseController = createPageContentController({ model: PoliciesPage, modelName: "PoliciesPage", auditAction: AUDIT_ACTIONS.UPDATE_POLICIES, editorConfig: policiesConfig, normalizeForEditor: normalizePoliciesDocument, normalizeForApi: normalizePoliciesDocument, preparePayload: withLastUpdated, }); module.exports = { ...baseController, async index(req, res) { try { return await createRenderSingletonPageView({ model: PoliciesPage, editorConfig: policiesConfig, view: "admin/policies/index", })(req, res); } catch (error) { console.error("policies index error:", error); req.flash("error_msg", "Error loading Policies Management"); return req.session.save(() => res.redirect("/admin/dashboard")); } }, async editSections(req, res) { try { const doc = await PoliciesPage.getSingle(); const data = normalizePoliciesDocument(doc.toObject()); const policy = (data.policies || []).find( (item) => item.id === req.params.policyId, ); if (!policy) { req.flash("error_msg", "Policy not found"); return req.session.save(() => res.redirect("/admin/policies")); } const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000"; const backendUrl = process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`; const editorConfig = createPoliciesSectionEditorConfig( policy, data.policies || [], ); return res.render("admin/policies/sections", { layout: "layouts/main", title: editorConfig.title, subtitle: editorConfig.subtitle, data: { policy, content: policy.content, }, editorConfig, activeTab: "sections", frontendUrl, backendUrl, previewUrl: `${frontendUrl}${editorConfig.previewPath}`, currentPath: req.path, user: req.session.user, }); } catch (error) { console.error("policies section index error:", error); req.flash("error_msg", "Error loading policy sections"); return req.session.save(() => res.redirect("/admin/policies")); } }, async updateSections(req, res) { try { const payload = typeof req.body.pageJson === "string" ? JSON.parse(req.body.pageJson) : req.body.pageJson || {}; const doc = await PoliciesPage.getSingle(); const beforeData = JSON.parse(JSON.stringify(doc.toObject())); const policyIndex = (doc.policies || []).findIndex( (item) => item.id === req.params.policyId, ); if (policyIndex === -1) { req.flash("error_msg", "Policy not found"); return req.session.save(() => res.redirect("/admin/policies")); } 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); const nextHero = { ...(doc.hero || {}), lastUpdated: formatLastUpdated(), }; await PoliciesPage.updateOne( { _id: doc._id }, { $set: { policies: normalizedPolicies, hero: nextHero, }, }, ); const reloadedDoc = await PoliciesPage.findById(doc._id); const afterData = JSON.parse(JSON.stringify(reloadedDoc.toObject())); const changes = diffObject(beforeData, afterData); if (changes.length > 0) { await writeAuditLog({ model: "PoliciesPage", documentId: doc._id, action: AUDIT_ACTIONS.UPDATE_POLICIES, before: beforeData, after: afterData, changes, req, }); } const finalData = normalizePoliciesDocument( (await PoliciesPage.findOne() .select("-_id -__v -createdAt -updatedAt") .lean()) || {}, ); jsonHelper.writeJsonFile(policiesConfig.dataFile, finalData); 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(redirectUrl), ); } catch (error) { console.error("policies section update error:", error); req.flash("error_msg", `Error updating policy sections: ${error.message}`); return req.session.save(() => res.redirect(`/admin/policies/${req.params.policyId}/section`), ); } }, };