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", ]; const text = (key, label, options = {}) => ({ key, label, type: "text", ...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 object = (key, label, fields, options = {}) => ({ key, label, type: "object", fields, ...options, }); const stringList = (key, label, options = {}) => ({ key, label, type: "array", itemLabel: options.itemLabel || "Item", 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", itemSchema: { type: "object", fields, }, ...options, }); const variantList = (key, label, variants, options = {}) => ({ key, label, type: "array", itemLabel: options.itemLabel || "Item", 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 = { partnerships: { key: "partnerships", title: "Partnerships Management", subtitle: "Edit content displayed on the partnerships page", routeBase: "/admin/partnerships", apiPath: "/api/partnerships", previewPath: "/about/partnerships", dataFile: "partnerships", imageType: "partnerships", tabs: [ { key: "hero", label: "Hero", icon: "fas fa-image", schema: object("hero", "Hero", [ text("badge", "Badge", { maxLength: 40 }), text("title", "Title", { maxLength: 90 }), textarea("description", "Description", { maxLength: 220, rows: 4 }), text("linkLabel", "Link Label", { maxLength: 40 }), image("image", "Hero Image", { imageHint: "Recommended 720x630 px", helpText: "Upload a landscape hero image for the right panel.", }), text("imageAlt", "Hero Image Alt Text", { maxLength: 120 }), ]), }, { key: "directory", label: "Directory", icon: "fas fa-handshake", schema: object("directory", "Directory", [ text("heading", "Heading", { maxLength: 70 }), textarea("description", "Description", { maxLength: 180, rows: 3 }), stringList("tabs", "Tabs", { itemLabel: "Tab", maxLength: 30, placeholder: "Industry", }), text("loadMoreLabel", "Load More Label", { maxLength: 40 }), objectList( "partners", "Partners", [ text("id", "Partner ID", { maxLength: 50, helpText: "Stable ID used by the frontend filtering state.", }), text("name", "Name", { maxLength: 90 }), text("category", "Category", { maxLength: 30 }), textarea("summary", "Summary", { maxLength: 130, rows: 3, helpText: "The card preview uses a 130 character cap in the frontend.", }), image("logo", "Logo", { imageHint: "Recommended 105x80 px minimum visible ratio", helpText: "Use a clean logo with transparent or simple background.", }), text("logoAlt", "Logo Alt Text", { maxLength: 120 }), textarea("about", "About", { maxLength: 600, rows: 5 }), text("collabType", "Collaboration Type", { maxLength: 40 }), textarea("benefits", "Benefits", { maxLength: 240, rows: 4 }), ], { itemLabel: "Partner" }, ), ]), }, { key: "cta", label: "CTA", icon: "fas fa-bullhorn", schema: object("cta", "CTA", [ text("heading", "Heading", { maxLength: 80 }), textarea("description", "Description", { maxLength: 220, rows: 4 }), text("buttonLabel", "Button Label", { maxLength: 40 }), ]), }, { key: "inquiryForm", label: "Inquiry Form", icon: "fas fa-envelope", schema: object("inquiryForm", "Inquiry Form", [ text("title", "Title", { maxLength: 60 }), object("fields", "Fields", [ object("firstName", "First Name Field", [ text("label", "Label", { maxLength: 30 }), text("placeholder", "Placeholder", { maxLength: 40 }), ]), object("lastName", "Last Name Field", [ text("label", "Label", { maxLength: 30 }), text("placeholder", "Placeholder", { maxLength: 40 }), ]), object("organization", "Organization Field", [ text("label", "Label", { maxLength: 40 }), text("placeholder", "Placeholder", { maxLength: 60 }), ]), object("partnershipType", "Partnership Type Field", [ text("label", "Label", { maxLength: 40 }), stringList("options", "Options", { itemLabel: "Option", maxLength: 50, }), ]), object("message", "Message Field", [ text("label", "Label", { maxLength: 30 }), text("placeholder", "Placeholder", { maxLength: 80 }), ]), ]), text("submitLabel", "Submit Label", { maxLength: 40 }), ]), }, ], }, history: { key: "history", title: "History Management", subtitle: "Edit content displayed on the history page", routeBase: "/admin/history", apiPath: "/api/history", previewPath: "/about/history", dataFile: "history", imageType: "history", tabs: [ { key: "highlight", label: "Highlight", icon: "fas fa-star", schema: object("highlight", "Highlight Banner", [ icon("icon", "Icon"), text("text", "Text", { maxLength: 110 }), text("linkLabel", "Link Label", { maxLength: 30 }), url("href", "Link URL", { maxLength: 255 }), ]), }, { key: "hero", label: "Hero", icon: "fas fa-image", schema: object("hero", "Hero", [ text("badge", "Badge", { maxLength: 40 }), text("title", "Title", { maxLength: 90 }), textarea("description", "Description", { maxLength: 220, rows: 4 }), ]), }, { key: "filters", label: "Filters", icon: "fas fa-filter", schema: object("filters", "Filters", [ text("yearLabel", "Year Label", { maxLength: 30 }), text("categoryLabel", "Category Label", { maxLength: 30 }), text("buttonLabel", "Button Label", { maxLength: 30 }), stringList("yearOptions", "Year Options", { itemLabel: "Year Option", maxLength: 30, }), stringList("categoryOptions", "Category Options", { itemLabel: "Category Option", maxLength: 40, }), ]), }, { key: "timeline", label: "Timeline", icon: "fas fa-clock-rotate-left", schema: object("timeline", "Timeline", [ text("loadMoreLabel", "Load More Label", { maxLength: 40 }), objectList( "items", "Milestones", [ text("id", "Milestone ID", { maxLength: 60 }), text("year", "Year", { maxLength: 10 }), text("yearRange", "Year Range", { maxLength: 30 }), text("category", "Category", { maxLength: 40 }), text("categoryLabel", "Category Label", { maxLength: 30 }), text("title", "Title", { maxLength: 90 }), textarea("description", "Description", { maxLength: 260, rows: 4, }), image("image", "Milestone Image", { imageHint: "Recommended 436x190 px", helpText: "Wide image used inside the milestone card.", }), text("imageAlt", "Image Alt Text", { maxLength: 120 }), objectList( "stats", "Stats", [ text("value", "Value", { maxLength: 24 }), text("label", "Label", { maxLength: 50 }), ], { itemLabel: "Stat" }, ), checkbox("featured", "Featured"), ], { itemLabel: "Milestone" }, ), ]), }, ], }, accreditation: { key: "accreditation", title: "Accreditation Management", subtitle: "Edit content displayed on the accreditation page", routeBase: "/admin/accreditation", apiPath: "/api/accreditation", previewPath: "/about/accreditation", dataFile: "accreditation", imageType: "accreditation", tabs: [ { key: "trustBanner", label: "Trust Banner", icon: "fas fa-shield-check", schema: object("trustBanner", "Trust Banner", [ icon("icon", "Icon"), text("text", "Text", { maxLength: 120 }), objectList( "links", "Links", [ text("label", "Label", { maxLength: 40 }), url("href", "URL", { maxLength: 255 }), icon("icon", "Icon"), ], { itemLabel: "Link" }, ), ]), }, { key: "hero", label: "Hero", icon: "fas fa-image", schema: object("hero", "Hero", [ text("badge", "Badge", { maxLength: 40 }), text("title", "Title", { maxLength: 60 }), textarea("description", "Description", { maxLength: 420, rows: 5 }), ]), }, { key: "grid", label: "Grid", icon: "fas fa-table-cells-large", schema: object("grid", "Grid", [ stringList("tabs", "Tabs", { itemLabel: "Tab", maxLength: 30, }), objectList( "items", "Items", [ text("id", "Item ID", { maxLength: 50 }), icon("icon", "Icon"), image("image", "Image", { imageHint: "Recommended 118x58 px minimum visible ratio", helpText: "Logo or badge used at the top of the card.", }), text("status", "Status", { maxLength: 20 }), text("category", "Category", { maxLength: 30 }), text("title", "Title", { maxLength: 17, helpText: "Frontend title space is capped at 17 characters.", }), textarea("description", "Description", { maxLength: 300, rows: 5, helpText: "Frontend description preview is capped at 300 characters.", }), text("scopeLabel", "Scope Label", { maxLength: 20 }), text("scope", "Scope", { maxLength: 60 }), text("validUntilLabel", "Valid Until Label", { maxLength: 24 }), text("validUntil", "Valid Until", { maxLength: 40 }), text("buttonLabel", "Button Label", { maxLength: 30 }), url("certificateHref", "Certificate URL", { maxLength: 255 }), ], { itemLabel: "Accreditation Item" }, ), ]), }, ], }, admissions: { 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", "Badge", { maxLength: 40 }), text("title", "Title", { maxLength: 80 }), textarea("description", "Description", { maxLength: 240, rows: 4 }), object("primaryCta", "Primary CTA", linkFields("Primary CTA")), object("secondaryCta", "Secondary CTA", linkFields("Secondary CTA")), 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: "Process", icon: "fas fa-list-ol", schema: object("process", "Process", [ text("id", "Section ID", { maxLength: 40 }), text("title", "Title", { maxLength: 60 }), textarea("description", "Description", { maxLength: 180, rows: 3 }), objectList( "steps", "Steps", [ text("number", "Number", { maxLength: 8 }), text("title", "Title", { maxLength: 50 }), textarea("description", "Description", { maxLength: 180, rows: 3 }), checkbox("active", "Active"), ], { itemLabel: "Step" }, ), ]), }, { key: "eligibility", label: "Eligibility", icon: "fas fa-check-circle", schema: object("eligibility", "Eligibility", [ text("id", "Section ID", { maxLength: 40 }), text("title", "Title", { maxLength: 60 }), objectList( "cards", "Cards", [ text("title", "Title", { maxLength: 50 }), icon("icon", "Icon"), stringList("items", "Items", { itemLabel: "Bullet Item", maxLength: 120, }), ], { itemLabel: "Card" }, ), ]), }, { key: "tuition", label: "Tuition", icon: "fas fa-chart-column", schema: object("tuition", "Tuition", [ text("id", "Section ID", { maxLength: 40 }), text("title", "Title", { maxLength: 60 }), text("chartTitle", "Chart Title", { maxLength: 60 }), textarea("chartDescription", "Chart Description", { maxLength: 140, rows: 3, }), objectList( "series", "Chart Series", [ text("label", "Label", { maxLength: 40 }), { key: "color", label: "Color", type: "color" }, stringList("values", "Values", { itemLabel: "Point", fieldType: "number", }), ], { itemLabel: "Series" }, ), ]), }, { key: "keyDates", label: "Key Dates", icon: "fas fa-calendar-days", schema: object("keyDates", "Key Dates", [ text("id", "Section ID", { maxLength: 40 }), text("title", "Title", { maxLength: 60 }), stringList("columns", "Columns", { itemLabel: "Column", maxLength: 40, }), objectList( "rows", "Rows", [ text("term", "Term", { maxLength: 40 }), text("applicationDeadline", "Application Deadline", { maxLength: 40, }), text("classesStart", "Classes Start", { maxLength: 40 }), ], { itemLabel: "Row" }, ), ]), }, { key: "calculator", label: "Calculator", icon: "fas fa-calculator", schema: object("calculator", "Calculator", [ text("title", "Title", { maxLength: 60 }), textarea("description", "Description", { maxLength: 120, rows: 3 }), stringList("modelOptions", "Model Options", { itemLabel: "Option", maxLength: 30, }), text("paceLabel", "Pace Label", { maxLength: 30 }), text("minPaceLabel", "Min Pace Label", { maxLength: 20 }), text("maxPaceLabel", "Max 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", { maxLength: 60 }), object("cta", "CTA", linkFields("CTA")), ]), }, { key: "scholarships", label: "Scholarships", icon: "fas fa-award", schema: object("scholarships", "Scholarships", [ text("title", "Title", { maxLength: 50 }), icon("icon", "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" }, ), ]), }, ], }, policies: { key: "policies", title: "Policies Management", subtitle: "Edit content displayed on the policies page", routeBase: "/admin/policies", apiPath: "/api/policies", previewPath: "/policies", dataFile: "policies", imageType: "policies", tabs: [ { key: "hero", label: "Hero", icon: "fas fa-scale-balanced", schema: object("hero", "Hero", [ text("badge", "Badge", { maxLength: 40 }), icon("icon", "Icon", { helpText: "Policies uses icon-only controls and does not require image upload.", }), text("titlePrefix", "Title Prefix", { maxLength: 50 }), text("titleHighlight", "Title Highlight", { maxLength: 40 }), textarea("description", "Description", { maxLength: 220, rows: 4 }), text("lastUpdated", "Last Updated Label", { maxLength: 50 }), ]), }, { key: "sidebar", label: "Sidebar", icon: "fas fa-bars", schema: object("sidebar", "Sidebar", [ text("heading", "Heading", { maxLength: 30 }), text("helperText", "Helper Text", { maxLength: 60 }), text("contactLabel", "Contact Label", { maxLength: 40 }), url("contactHref", "Contact URL", { maxLength: 255 }), ]), }, { key: "policies", label: "Policies", icon: "fas fa-file-lines", schema: objectList( "policies", "Policies", [ text("id", "Policy ID", { maxLength: 40 }), text("navLabel", "Navigation Label", { maxLength: 40 }), text("title", "Title", { maxLength: 70 }), text("effectiveDate", "Effective Date", { maxLength: 50 }), textarea("intro", "Intro", { maxLength: 260, rows: 4 }), variantList( "sections", "Sections", { text: { label: "Text Section", schema: object("section", "Text Section", [ text("heading", "Heading", { maxLength: 60 }), objectList( "paragraphs", "Paragraphs", [ textarea("text", "Text", { maxLength: 500, rows: 4, }), objectList( "links", "Links", [ text("label", "Label", { maxLength: 50 }), url("href", "URL", { maxLength: 255 }), text("tabId", "Target Policy ID", { maxLength: 40, helpText: "Use this when the link should switch to another policy tab.", }), ], { itemLabel: "Link" }, ), ], { itemLabel: "Paragraph" }, ), ]), }, list: { label: "List Section", schema: object("section", "List Section", [ text("heading", "Heading", { maxLength: 60 }), textarea("intro", "Intro", { maxLength: 220, rows: 3 }), stringList("items", "Items", { itemLabel: "List Item", maxLength: 180, fieldType: "textarea", }), ]), }, cards: { label: "Cards Section", schema: object("section", "Cards Section", [ objectList( "cards", "Cards", [ icon("icon", "Icon"), text("title", "Title", { maxLength: 50 }), textarea("description", "Description", { maxLength: 220, rows: 4, }), object("link", "Link", [ text("label", "Label", { maxLength: 50 }), url("href", "URL", { maxLength: 255 }), text("tabId", "Target Policy ID", { maxLength: 40 }), ]), ], { itemLabel: "Card" }, ), ]), }, }, { itemLabel: "Section" }, ), ], { itemLabel: "Policy" }, ), }, ], }, };