const PartnershipsPage = require("../models/partnerships"); const AUDIT_ACTIONS = require("../constants/auditAction"); const partnershipsConfig = require("../utils/contentEditors/partnershipsConfig"); const createPageContentController = require("./_createPageContentController"); const { ensureUniqueIds } = require("../utils/contentEditorIds"); const FIXED_INQUIRY_FIELDS = [ { id: "firstName", type: "text", width: "half", label: "First name", placeholder: "First name", required: true }, { id: "lastName", type: "text", width: "half", label: "Last name", placeholder: "Last name", required: true }, { id: "organization", type: "text", width: "full", label: "Organization name", placeholder: "Company or Institution", required: true }, { id: "partnershipType", type: "select", width: "full", label: "Partnership type", placeholder: "Select partnership type", required: true }, { id: "message", type: "textarea", width: "full", label: "Message", placeholder: "Tell us how you'd like to collaborate...", required: true }, ]; function getTabConfig(tabKey) { return (partnershipsConfig.tabs || []).find((tab) => tab.key === tabKey) || {}; } function getObjectField(tabKey, fieldKey) { const fields = getTabConfig(tabKey)?.schema?.fields || []; return fields.find((field) => field.key === fieldKey) || {}; } function getArrayItemField(tabKey, arrayKey, fieldKey) { const arrayField = getObjectField(tabKey, arrayKey); const itemFields = arrayField?.itemSchema?.fields || []; return itemFields.find((field) => field.key === fieldKey) || {}; } function getPartnershipsEditorUi() { return { hero: { badge: getObjectField("hero", "badge"), title: getObjectField("hero", "title"), description: getObjectField("hero", "description"), linkLabel: getObjectField("hero", "linkLabel"), image: getObjectField("hero", "image"), imageAlt: getObjectField("hero", "imageAlt"), }, directory: { heading: getObjectField("directory", "heading"), description: getObjectField("directory", "description"), tabs: getObjectField("directory", "tabs"), partnerFields: { name: getArrayItemField("directory", "partners", "name"), category: getArrayItemField("directory", "partners", "category"), summary: getArrayItemField("directory", "partners", "summary"), logo: getArrayItemField("directory", "partners", "logo"), logoAlt: getArrayItemField("directory", "partners", "logoAlt"), about: getArrayItemField("directory", "partners", "about"), collabType: getArrayItemField("directory", "partners", "collabType"), benefits: getArrayItemField("directory", "partners", "benefits"), }, tabsFrontendHint: partnershipsConfig.editorUi?.directory?.tabsFrontendHint || "", partnersHelpText: partnershipsConfig.editorUi?.directory?.partnersHelpText || "", }, cta: { heading: getObjectField("cta", "heading"), description: getObjectField("cta", "description"), buttonLabel: getObjectField("cta", "buttonLabel"), }, inquiryForm: { title: getObjectField("inquiryForm", "title"), fields: getObjectField("inquiryForm", "fields"), fieldFields: { label: getArrayItemField("inquiryForm", "fields", "label"), placeholder: getArrayItemField("inquiryForm", "fields", "placeholder"), type: getArrayItemField("inquiryForm", "fields", "type"), width: getArrayItemField("inquiryForm", "fields", "width"), options: getArrayItemField("inquiryForm", "fields", "options"), required: getArrayItemField("inquiryForm", "fields", "required"), }, fieldsHelpText: partnershipsConfig.editorUi?.inquiryForm?.fieldsHelpText || "", }, }; } function toInquiryField(id, field, type, width) { return { id, label: field?.label || "", placeholder: field?.placeholder || "", type, width, required: true, options: Array.isArray(field?.options) ? field.options : [], }; } function normalizeInquiryForm(data) { if (!data?.inquiryForm) { return data; } if (Array.isArray(data.inquiryForm.fields)) { const fieldsById = new Map(data.inquiryForm.fields.map((field) => [field.id, field])); return { ...data, inquiryForm: { ...data.inquiryForm, fields: FIXED_INQUIRY_FIELDS.map((fixedField) => { const field = fieldsById.get(fixedField.id) || {}; return { id: fixedField.id, label: field.label || fixedField.label, placeholder: field.placeholder || fixedField.placeholder, type: fixedField.type, width: ["half", "full"].includes(field.width) ? field.width : fixedField.width, required: field.required === undefined ? fixedField.required : Boolean(field.required), options: Array.isArray(field.options) ? field.options.filter(Boolean) : [], }; }), }, }; } const legacyFields = data.inquiryForm.fields || {}; return { ...data, inquiryForm: { ...data.inquiryForm, fields: [ toInquiryField("firstName", legacyFields.firstName, "text", "half"), toInquiryField("lastName", legacyFields.lastName, "text", "half"), toInquiryField("organization", legacyFields.organization, "text", "full"), toInquiryField( "partnershipType", legacyFields.partnershipType, "select", "full", ), toInquiryField("message", legacyFields.message, "textarea", "full"), ].filter((field) => field.label || field.placeholder || field.id), }, }; } function prepareInquiryPayload(payload, context = {}) { const normalized = normalizeInquiryForm(payload); const fields = Array.isArray(normalized?.inquiryForm?.fields) ? normalized.inquiryForm.fields : []; const tabs = Array.isArray(normalized?.directory?.tabs) ? normalized.directory.tabs : []; const partners = Array.isArray(normalized?.directory?.partners) ? normalized.directory.partners : []; const seenTabs = new Set(); const duplicateTabs = []; tabs.forEach((tab) => { const normalizedTab = String(tab || "").trim().toLowerCase(); if (!normalizedTab) { return; } if (seenTabs.has(normalizedTab)) { duplicateTabs.push(String(tab || "").trim()); return; } seenTabs.add(normalizedTab); }); if (duplicateTabs.length > 0) { throw new Error( `Category tab already exists: ${duplicateTabs[0]}. Please use unique tab names.`, ); } const fieldsById = new Map(fields.map((field) => [field.id, field])); const beforeData = context.beforeData || {}; const beforeNormalized = normalizeInquiryForm(beforeData || {}); const beforeFields = Array.isArray(beforeNormalized?.inquiryForm?.fields) ? beforeNormalized.inquiryForm.fields : []; const beforeFieldsById = new Map(beforeFields.map((field) => [field.id, field])); return { ...normalized, directory: { ...normalized.directory, partners: ensureUniqueIds( partners, (partner) => partner.id, (partner, index) => partner.name || partner.category || `partner-${index + 1}`, "partner", ), }, inquiryForm: { ...normalized.inquiryForm, fields: FIXED_INQUIRY_FIELDS.map((fixedField) => { const field = fieldsById.get(fixedField.id) || beforeFieldsById.get(fixedField.id) || {}; return { id: fixedField.id, label: field.label || fixedField.label, placeholder: field.placeholder || fixedField.placeholder, type: fixedField.type, width: ["half", "full"].includes(field.width) ? field.width : fixedField.width, required: field.required === undefined ? fixedField.required : Boolean(field.required), options: Array.isArray(field.options) ? field.options.filter(Boolean) : [], }; }), }, }; } const controller = createPageContentController({ model: PartnershipsPage, modelName: "PartnershipsPage", auditAction: AUDIT_ACTIONS.UPDATE_PARTNERSHIPS, editorConfig: partnershipsConfig, normalizeForEditor: normalizeInquiryForm, normalizeForApi: normalizeInquiryForm, preparePayload: prepareInquiryPayload, }); controller.index = async function index(req, res) { try { const doc = await PartnershipsPage.getSingle(); const rawData = doc.toObject(); const data = normalizeInquiryForm(rawData); const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000"; const backendUrl = process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`; const defaultTab = partnershipsConfig.tabs[0]?.key; const requestedTab = req.query.tab; if (requestedTab === "submissions") { return res.redirect("/admin/submissions?tab=partnership"); } const activeTab = partnershipsConfig.tabs.some((tab) => tab.key === requestedTab) ? requestedTab : defaultTab; return res.render("admin/partnerships/index", { layout: "layouts/main", title: partnershipsConfig.title, subtitle: partnershipsConfig.subtitle, data, editorConfig: partnershipsConfig, editorUi: getPartnershipsEditorUi(), activeTab, frontendUrl, backendUrl, previewUrl: `${frontendUrl}${partnershipsConfig.previewPath}`, currentPath: req.path, user: req.session.user, }); } catch (error) { console.error("partnerships index error:", error); req.flash("error_msg", "Error loading Partnerships Management"); return req.session.save(() => res.redirect("/admin/dashboard")); } }; module.exports = controller;