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:
Tống Thành Đạt
2026-04-21 12:37:19 +07:00
parent 122df1a46e
commit 5d0fae6d51
30 changed files with 2174 additions and 477 deletions
+3 -16
View File
@@ -24,7 +24,7 @@ const widthOptions = [
module.exports = {
key: "partnerships",
title: "Partnerships Management",
subtitle: "Edit content displayed on the partnerships page",
subtitle: "Manage the content for the partnerships page",
routeBase: "/admin/partnerships",
apiPath: "/api/partnerships",
previewPath: "/about/partnerships",
@@ -62,19 +62,12 @@ module.exports = {
maxLength: 30,
placeholder: "Industry",
sortable: true,
helpText:
"The frontend shows up to 3 direct tabs. Additional tabs move into a More dropdown.",
helpText: "Add and reorder the category tabs shown on the page.",
}),
text("loadMoreLabel", "Load more button label", { maxLength: 40 }),
objectList(
"partners",
"Partners",
[
text("id", "Partner key", {
maxLength: 50,
helpText:
"Use a short unique key. It keeps each card state separate on the frontend.",
}),
text("name", "Partner name", { maxLength: 90 }),
combobox("category", "Category", {
maxLength: 30,
@@ -83,7 +76,7 @@ module.exports = {
textarea("summary", "Card summary", {
maxLength: 130,
rows: 3,
helpText: "The card preview is capped at 130 characters.",
helpText: "Keep this short. Around 130 characters works best in the card layout.",
}),
image("logo", "Partner logo", {
imageHint: "Recommended 105x80 px minimum visible ratio",
@@ -127,11 +120,6 @@ module.exports = {
"fields",
"Form fields",
[
text("id", "Field key", {
maxLength: 40,
helpText:
"Use a short unique key such as firstName or organization.",
}),
text("label", "Field label", { maxLength: 40 }),
text("placeholder", "Placeholder text", { maxLength: 80 }),
select("type", "Field type", inquiryFieldOptions),
@@ -153,7 +141,6 @@ module.exports = {
emptyText: "No inquiry fields yet.",
},
),
text("submitLabel", "Submit button label", { maxLength: 40 }),
]),
},
],