forked from UKSOURCE/cms.lams
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:
@@ -0,0 +1,221 @@
|
||||
const {
|
||||
text,
|
||||
hidden,
|
||||
textarea,
|
||||
image,
|
||||
icon,
|
||||
checkbox,
|
||||
object,
|
||||
stringList,
|
||||
objectList,
|
||||
linkFields,
|
||||
} = require("./sharedFields");
|
||||
|
||||
module.exports = {
|
||||
key: "admissions",
|
||||
title: "Admissions Management",
|
||||
subtitle: "Edit content displayed on the admissions page",
|
||||
routeBase: "/admin/admissions",
|
||||
apiPath: "/api/admissions",
|
||||
previewPath: "/admissions",
|
||||
dataFile: "admissions",
|
||||
imageType: "admissions",
|
||||
tabs: [
|
||||
{
|
||||
key: "hero",
|
||||
label: "Hero",
|
||||
icon: "fas fa-image",
|
||||
schema: object("hero", "Hero", [
|
||||
text("badge", "Eyebrow label", { maxLength: 40 }),
|
||||
text("title", "Headline", { maxLength: 80 }),
|
||||
textarea("description", "Supporting text", { maxLength: 240, rows: 4 }),
|
||||
object("primaryCta", "Primary button", linkFields("Primary button")),
|
||||
object("secondaryCta", "Secondary button", linkFields("Secondary button")),
|
||||
image("image", "Hero image", {
|
||||
imageHint: "Recommended 720x646 px",
|
||||
helpText: "Large image rendered in the right hero panel.",
|
||||
}),
|
||||
text("imageAlt", "Hero image alt text", { maxLength: 120 }),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "process",
|
||||
label: "Admissions Process",
|
||||
icon: "fas fa-list-ol",
|
||||
schema: object("process", "Admissions process", [
|
||||
text("id", "Section key", { maxLength: 40 }),
|
||||
text("title", "Section title", { maxLength: 60 }),
|
||||
textarea("description", "Section description", {
|
||||
maxLength: 180,
|
||||
rows: 3,
|
||||
}),
|
||||
objectList(
|
||||
"steps",
|
||||
"Steps",
|
||||
[
|
||||
hidden("number", { autoSequence: { padLength: 2 } }),
|
||||
text("title", "Step title", { maxLength: 50 }),
|
||||
textarea("description", "Step description", {
|
||||
maxLength: 180,
|
||||
rows: 3,
|
||||
}),
|
||||
checkbox("active", "Highlight this step"),
|
||||
],
|
||||
{
|
||||
itemLabel: "Step",
|
||||
sortable: true,
|
||||
itemTitleKey: "title",
|
||||
itemSubtitleKey: "number",
|
||||
emptyText: "No process steps yet.",
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "eligibility",
|
||||
label: "Eligibility",
|
||||
icon: "fas fa-check-circle",
|
||||
schema: object("eligibility", "Eligibility", [
|
||||
text("id", "Section key", { maxLength: 40 }),
|
||||
text("title", "Section title", { maxLength: 60 }),
|
||||
objectList(
|
||||
"cards",
|
||||
"Eligibility cards",
|
||||
[
|
||||
text("title", "Card title", { maxLength: 50 }),
|
||||
icon("icon", "Card icon"),
|
||||
stringList("items", "Checklist items", {
|
||||
itemLabel: "Checklist item",
|
||||
maxLength: 120,
|
||||
sortable: true,
|
||||
}),
|
||||
],
|
||||
{
|
||||
itemLabel: "Card",
|
||||
sortable: true,
|
||||
itemTitleKey: "title",
|
||||
emptyText: "No eligibility cards yet.",
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "tuition",
|
||||
label: "Tuition",
|
||||
icon: "fas fa-chart-column",
|
||||
schema: object("tuition", "Tuition", [
|
||||
text("id", "Section key", { maxLength: 40 }),
|
||||
text("title", "Section title", { maxLength: 60 }),
|
||||
text("chartTitle", "Chart title", { maxLength: 60 }),
|
||||
textarea("chartDescription", "Chart description", {
|
||||
maxLength: 140,
|
||||
rows: 3,
|
||||
}),
|
||||
objectList(
|
||||
"series",
|
||||
"Chart series",
|
||||
[
|
||||
text("label", "Series label", { maxLength: 40 }),
|
||||
{ key: "color", label: "Series color", type: "color" },
|
||||
stringList("values", "Data points", {
|
||||
itemLabel: "Point",
|
||||
fieldType: "number",
|
||||
sortable: true,
|
||||
}),
|
||||
],
|
||||
{
|
||||
itemLabel: "Series",
|
||||
sortable: true,
|
||||
itemTitleKey: "label",
|
||||
emptyText: "No chart series yet.",
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "keyDates",
|
||||
label: "Key Dates",
|
||||
icon: "fas fa-calendar-days",
|
||||
schema: object("keyDates", "Key dates", [
|
||||
text("id", "Section key", { maxLength: 40 }),
|
||||
text("title", "Section title", { maxLength: 60 }),
|
||||
stringList("columns", "Table columns", {
|
||||
itemLabel: "Column",
|
||||
maxLength: 40,
|
||||
sortable: true,
|
||||
}),
|
||||
objectList(
|
||||
"rows",
|
||||
"Table rows",
|
||||
[
|
||||
text("term", "Term", { maxLength: 40 }),
|
||||
text("applicationDeadline", "Application deadline", {
|
||||
maxLength: 40,
|
||||
}),
|
||||
text("classesStart", "Classes start", { maxLength: 40 }),
|
||||
],
|
||||
{
|
||||
itemLabel: "Row",
|
||||
sortable: true,
|
||||
itemTitleKey: "term",
|
||||
emptyText: "No key date rows yet.",
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "calculator",
|
||||
label: "Calculator",
|
||||
icon: "fas fa-calculator",
|
||||
schema: object("calculator", "Calculator", [
|
||||
text("title", "Card title", { maxLength: 60 }),
|
||||
textarea("description", "Card description", {
|
||||
maxLength: 120,
|
||||
rows: 3,
|
||||
}),
|
||||
stringList("modelOptions", "Model options", {
|
||||
itemLabel: "Option",
|
||||
maxLength: 30,
|
||||
sortable: true,
|
||||
}),
|
||||
text("paceLabel", "Pace label", { maxLength: 30 }),
|
||||
text("minPaceLabel", "Minimum pace label", { maxLength: 20 }),
|
||||
text("maxPaceLabel", "Maximum pace label", { maxLength: 20 }),
|
||||
text("resultLabel", "Result label", { maxLength: 40 }),
|
||||
text("monthlyAmount", "Monthly amount", { maxLength: 20 }),
|
||||
text("monthlySuffix", "Monthly suffix", { maxLength: 10 }),
|
||||
icon("noteIcon", "Note icon"),
|
||||
text("note", "Note text", { maxLength: 60 }),
|
||||
object("cta", "Button", linkFields("Button")),
|
||||
]),
|
||||
},
|
||||
{
|
||||
key: "scholarships",
|
||||
label: "Scholarships",
|
||||
icon: "fas fa-award",
|
||||
schema: object("scholarships", "Scholarships", [
|
||||
text("title", "Card title", { maxLength: 50 }),
|
||||
icon("icon", "Card icon"),
|
||||
objectList(
|
||||
"items",
|
||||
"Scholarship items",
|
||||
[
|
||||
text("title", "Title", { maxLength: 50 }),
|
||||
text("amount", "Amount", { maxLength: 24 }),
|
||||
textarea("description", "Description", {
|
||||
maxLength: 160,
|
||||
rows: 3,
|
||||
}),
|
||||
],
|
||||
{
|
||||
itemLabel: "Scholarship item",
|
||||
sortable: true,
|
||||
itemTitleKey: "title",
|
||||
itemSubtitleKey: "amount",
|
||||
emptyText: "No scholarship items yet.",
|
||||
},
|
||||
),
|
||||
]),
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user