const SiteContent = require('../models/SiteContent'); const { asyncController } = require('../utils/asyncController'); const DEFAULT_SITE_CONTENT = { welcome: { title: 'ITS IT Department Kiosk', subtitle: "Hi I'm ITS IT Department Kiosk, I can help you explore our department, meet our faculty, and discover our achievements.", cta: 'Start Exploring', footerHint: 'Touch anywhere to begin', }, mainMenu: { title: 'Department of Information Technology', subtitle: 'What would you like to explore?', footerHint: 'Touch any card to explore that section', modules: [ { key: 'aboutus', label: 'About Us', subtitle: 'Learn about our department', bg: '#3b82f6', href: '/aboutus', iconImage: '', }, { key: 'profile', label: 'Faculty Profiles', subtitle: 'Meet our expert faculty', bg: '#8b5cf6', href: '/profile', iconImage: '', }, { key: 'admission', label: 'Admissions/Other Info', subtitle: 'Join our program', bg: '#06b6d4', href: '/admission', iconImage: '', }, { key: 'achievement', label: 'Achievements/News', subtitle: 'Our success stories', bg: '#f97316', href: '/achievement', iconImage: '', }, { key: 'announcement', label: 'Announcements', subtitle: 'Latest updates and news', bg: '#22c55e', href: '/announcement', iconImage: '', }, ], }, }; const sanitizeCopy = (value, fallback) => { if (!value || typeof value !== 'object') return fallback; return { title: String(value.title || fallback.title).trim(), subtitle: String(value.subtitle || fallback.subtitle).trim(), cta: String(value.cta || fallback.cta).trim(), footerHint: String(value.footerHint || fallback.footerHint).trim(), }; }; const toKeySlug = (value = '') => String(value) .toLowerCase() .trim() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-'); const sanitizeModule = (item = {}, fallback = {}) => ({ key: String(item.key || fallback.key || '').trim(), label: String(item.label || fallback.label || '').trim(), subtitle: String(item.subtitle || fallback.subtitle || '').trim(), bg: String(item.bg || fallback.bg || '#3b82f6').trim(), href: String(item.href || fallback.href || '').trim(), iconImage: String(item.iconImage || fallback.iconImage || '').trim(), }); const sanitizeMainMenu = (value) => { const fallback = DEFAULT_SITE_CONTENT.mainMenu; const modules = Array.isArray(value?.modules) && value.modules.length ? value.modules : fallback.modules; return { title: value?.title || fallback.title, subtitle: value?.subtitle || fallback.subtitle, footerHint: value?.footerHint || fallback.footerHint, modules: modules .map((item, index) => { const moduleItem = sanitizeModule(item, fallback.modules[index] || {}); const generatedKey = toKeySlug(moduleItem.key || moduleItem.label || moduleItem.href || `card-${index + 1}`); return { ...moduleItem, key: generatedKey || `card-${index + 1}`, }; }) .filter((item) => item.label), }; }; const normalizeSiteContent = (doc) => ({ welcome: sanitizeCopy(doc?.welcome?.EN || doc?.welcome, DEFAULT_SITE_CONTENT.welcome), mainMenu: sanitizeMainMenu(doc?.mainMenu), }); const getSiteContent = asyncController(async (req, res) => { const doc = await SiteContent.findOne({ slug: 'main' }).lean(); res.json(normalizeSiteContent(doc)); }); const updateSiteContent = asyncController(async (req, res) => { const { welcome, mainMenu } = req.body; const payload = {}; if (welcome && typeof welcome === 'object') { const source = welcome.EN && typeof welcome.EN === 'object' ? welcome.EN : welcome; payload.welcome = { title: String(source?.title || '').trim(), subtitle: String(source?.subtitle || '').trim(), cta: String(source?.cta || '').trim(), footerHint: String(source?.footerHint || '').trim(), }; } if (mainMenu && typeof mainMenu === 'object') { payload.mainMenu = { title: String(mainMenu?.title || '').trim(), subtitle: String(mainMenu?.subtitle || '').trim(), footerHint: String(mainMenu?.footerHint || '').trim(), modules: Array.isArray(mainMenu?.modules) ? mainMenu.modules .map((item, index) => { const moduleItem = sanitizeModule(item); const generatedKey = toKeySlug(moduleItem.key || moduleItem.label || moduleItem.href || `card-${index + 1}`); return { ...moduleItem, key: generatedKey || `card-${index + 1}`, }; }) .filter((item) => item.label) : [], }; } if (!Object.keys(payload).length) { return res.status(400).json({ message: 'No fields provided to update' }); } const doc = await SiteContent.findOneAndUpdate( { slug: 'main' }, { $set: payload }, { returnDocument: 'after', upsert: true, runValidators: true } ); if (!doc) { return res.status(500).json({ message: 'Failed to persist site content' }); } res.json(normalizeSiteContent(doc)); }, { defaultStatus: 400 }); module.exports = { getSiteContent, updateSiteContent };