feat(cms): refactor page content management and implement detailed editors

Refactor the CMS page content system to use a more modular configuration-driven approach. This replaces the monolithic `pageContentConfig.js` with individual configuration files for each page and introduces a shared field utility to standardize editor components.
Key changes:
- Implement a generic `_renderSingletonPageView` helper to reduce duplication across controllers.
- Enhance `_createPageContentController` with `normalizeForEditor`, `normalizeForApi`, and `preparePayload` hooks for custom data transformation.
- Create dedicated configuration and view structures for Partnerships, History, Accreditation, Admissions, and Policies pages.
- Add a specialized section editor for Policies to manage complex nested content.
- Improve the frontend `page-content-editor.js` with support for visibility logic, auto-sequencing for arrays, and URL synchronization for active tabs.
- Update data JSON files to align with the new schema.
This commit is contained in:
Tống Thành Đạt
2026-04-20 19:43:28 +07:00
parent 8b6bf5fe6e
commit 7df3a6f6bf
58 changed files with 7501 additions and 844 deletions
+182
View File
@@ -0,0 +1,182 @@
const ICON_OPTIONS = [
"fa-scale-balanced",
"fa-shield-check",
"fa-magnifying-glass",
"fa-graduation-cap",
"fa-check-double",
"fa-building-columns",
"fa-award",
"fa-trophy",
"fa-arrow-down",
"fa-arrow-right",
"fa-check-circle",
"fa-exchange-alt",
"fa-bolt",
"fa-book-open",
"fa-credit-card",
"fa-calendar-days",
"fa-envelope",
"fa-file-lines",
"fa-globe",
"fa-briefcase",
"fa-handshake",
"fa-circle-info",
"fa-star",
"fa-list-check",
];
const text = (key, label, options = {}) => ({
key,
label,
type: "text",
...options,
});
const hidden = (key, options = {}) => ({
key,
type: "hidden",
...options,
});
const textarea = (key, label, options = {}) => ({
key,
label,
type: "textarea",
rows: options.rows || 4,
...options,
});
const image = (key, label, options = {}) => ({
key,
label,
type: "image",
...options,
});
const icon = (key, label, options = {}) => ({
key,
label,
type: "icon",
options: options.options || ICON_OPTIONS,
...options,
});
const checkbox = (key, label, options = {}) => ({
key,
label,
type: "checkbox",
...options,
});
const url = (key, label, options = {}) => ({
key,
label,
type: "url",
...options,
});
const select = (key, label, options = [], extra = {}) => ({
key,
label,
type: "select",
options,
...extra,
});
const combobox = (key, label, options = {}) => ({
key,
label,
type: "combobox",
...options,
});
const object = (key, label, fields, options = {}) => ({
key,
label,
type: "object",
fields,
...options,
});
const stringList = (key, label, options = {}) => ({
key,
label,
type: "array",
itemLabel: options.itemLabel || "Item",
sortable: options.sortable,
addLabel: options.addLabel,
helpText: options.helpText,
emptyText: options.emptyText,
itemSchema: {
type: "primitive",
fieldType: options.fieldType || "text",
label: options.itemLabel || "Item",
maxLength: options.maxLength,
placeholder: options.placeholder,
helpText: options.itemHelpText,
},
...options,
});
const objectList = (key, label, fields, options = {}) => ({
key,
label,
type: "array",
itemLabel: options.itemLabel || "Item",
sortable: options.sortable,
addLabel: options.addLabel,
helpText: options.helpText,
emptyText: options.emptyText,
itemTitleKey: options.itemTitleKey,
itemSubtitleKey: options.itemSubtitleKey,
itemActions: options.itemActions,
itemSchema: {
type: "object",
fields,
},
...options,
});
const variantList = (key, label, variants, options = {}) => ({
key,
label,
type: "array",
itemLabel: options.itemLabel || "Item",
sortable: options.sortable,
addLabel: options.addLabel,
helpText: options.helpText,
emptyText: options.emptyText,
itemSchema: {
type: "variant",
discriminator: "type",
options: Object.keys(variants).map((value) => ({
value,
label: variants[value].label,
})),
variants,
},
...options,
});
const linkFields = (prefix = "Link") => [
text("label", `${prefix} label`, { maxLength: 60 }),
url("href", `${prefix} URL`, { maxLength: 255 }),
];
module.exports = {
ICON_OPTIONS,
text,
hidden,
textarea,
image,
icon,
checkbox,
url,
select,
combobox,
object,
stringList,
objectList,
variantList,
linkFields,
};