Merge branch 'main' of https://gits.techvanguard.vn/UKSOURCE/cms.hailearning.edu.vn into fea/thanh-02022026-news

This commit is contained in:
Wini_Fy
2026-02-05 10:54:30 +07:00
33 changed files with 3889 additions and 1729 deletions

View File

@@ -1,347 +1,367 @@
const { addBaseUrlToImages } = require('../utils/imageHelper');
const Header = require('../models/header');
const Menu = require('../models/menuHeader');
const Header = require("../models/header");
const HeaderMenu = require("../models/HeaderMenu");
// Helper function để thêm title và url cho programmes
const addProgrammeDetails = (programmes, menuUrl) => {
return programmes.map(prog => ({
...prog,
title: prog.name,
url: `${menuUrl}${prog.code}/`
}));
};
/**
* Helper function to build a tree structure (Mirroring logic in headerMenuController)
*/
const buildTree = (items, parentId = null) => {
const branch = [];
const children = items.filter(item =>
String(item.parentId) === String(parentId) || (item.parentId === null && parentId === null)
);
// Helper function để xử lý menu tree cho API (đơn giản hóa, map menuid thành id)
const processMenuTreeForAPI = (menuTree) => {
return menuTree.map(item => {
const processedItem = {
id: item.menuid, // Map menuid to id for frontend
title: item.title,
url: item.url,
order: item.order,
parent: item.parent || null,
type: item.type,
children: []
};
// Đệ quy cho children
if (item.children && item.children.length > 0) {
processedItem.children = processMenuTreeForAPI(item.children);
for (const child of children) {
const item = child.toObject ? child.toObject() : { ...child };
const subChildren = buildTree(items, item._id);
item.children = subChildren.length > 0 ? subChildren : [];
branch.push(item);
}
return processedItem;
});
return branch.sort((a, b) => a.order - b.order);
};
// Helper function để xử lý menu tree và thêm programme details (cho admin)
const processMenuTree = (menuTree) => {
return menuTree.map(item => {
const processedItem = { ...item };
// Nếu có programmes, thêm title và url
if (item.programmes && item.programmes.length > 0) {
processedItem.programmes = addProgrammeDetails(item.programmes, item.url);
}
// Đệ quy cho children
if (item.children && item.children.length > 0) {
processedItem.children = processMenuTree(item.children);
}
return processedItem;
});
};
// Get header data from MongoDB
const getHeaderData = async () => {
const header = await Header.findOne({ name: 'default' });
if (!header) {
return {
topbar: {
contactInfo: {
phone: '',
email: ''
},
links: []
},
mainMenu: [],
logo: ''
};
}
// Convert to plain object to allow modifications
const headerData = header.toObject();
// Lấy menu tree từ collection menuHeader (đơn giản, không có programmes)
try {
const menuTree = await Menu.getMenuTree();
// Xử lý menu tree để map menuid thành id cho frontend
headerData.mainMenu = processMenuTreeForAPI(menuTree);
} catch (error) {
console.error('Error getting menu tree:', error);
headerData.mainMenu = [];
}
return headerData;
};
// API to get header data
exports.api = async (req, res) => {
try {
// Lấy header data
const header = await getHeaderData();
// Xử lý URL cho logo và các hình ảnh khác
const processedData = addBaseUrlToImages(header);
res.json(processedData);
} catch (err) {
console.error('API Error:', err);
res.status(500).json({ error: 'Error loading header data' });
}
};
// API để lấy menu tree cho frontend (public API)
exports.getMenuTreeAPI = async (req, res) => {
try {
const menuTree = await Menu.getMenuTree();
// Xử lý menu tree để map menuid thành id cho frontend
const processedMenuTree = processMenuTreeForAPI(menuTree);
res.json(processedMenuTree);
} catch (error) {
console.error('Error getting menu tree:', error);
res.status(500).json({ error: 'Error loading menu tree' });
}
};
// API để lấy menu tree (cho admin)
exports.getMenuTree = async (req, res) => {
try {
const menuTree = await Menu.getMenuTree();
res.json(menuTree);
} catch (error) {
console.error('Error getting menu tree:', error);
res.status(500).json({ error: 'Error loading menu tree' });
}
};
// API để lấy programmes theo menu ID
exports.getProgrammesByMenuId = async (req, res) => {
try {
const { menuId } = req.params;
const programmes = await Header.getProgrammesByMenuId(menuId);
// Lấy menu item để có URL
const menuItem = await Menu.findOne({ menuid: menuId });
if (menuItem) {
const programmesWithDetails = addProgrammeDetails(programmes, menuItem.url);
res.json(programmesWithDetails);
} else {
res.json(programmes);
}
} catch (error) {
console.error('Error getting programmes by menu ID:', error);
res.status(500).json({ error: 'Error loading programmes' });
}
};
// API để lấy toàn bộ header data
exports.getHeaderData = async (req, res) => {
try {
const headerData = await getHeaderData();
res.json(headerData);
} catch (error) {
console.error('Error getting header data:', error);
res.status(500).json({ error: 'Error loading header data' });
}
};
// API để lấy menu item theo ID
exports.getMenuItem = async (req, res) => {
try {
const { menuId } = req.params;
const Menu = require('../models/menuHeader');
const menuItem = await Menu.findOne({ menuid: menuId });
if (!menuItem) {
return res.status(404).json({ error: 'Menu item not found' });
}
// Nếu là level type và có fetch = true, lấy programmes
if (menuItem.type === 'level' && menuItem.fetch) {
const programmes = await Menu.getProgrammesByMenuId(menuItem.menuid);
menuItem.programmes = addProgrammeDetails(programmes, menuItem.url);
}
res.json(menuItem);
} catch (error) {
console.error('Error getting menu item:', error);
res.status(500).json({ error: 'Error loading menu item' });
}
};
// Render admin view
// Admin: Render header management page
exports.index = async (req, res) => {
try {
const data = await getHeaderData();
try {
const header = await Header.findOne().sort({ order: 1 });
res.render('admin/header/index', {
title: 'Header Management',
data
});
} catch (error) {
console.error('Error in header index:', error);
req.flash('error_msg', 'An error occurred while loading the page');
res.redirect('/admin/dashboard');
}
};
// Update header (chỉ cập nhật topbar và logo)
exports.update = async (req, res) => {
try {
const headerData = req.body;
// Tìm và cập nhật header
const header = await Header.findOne({ name: 'default' });
if (!header) {
req.flash('error_msg', 'Header not found');
return res.redirect('/admin/header');
}
// Cập nhật từng phần
if (headerData.topbarJson) {
header.topbar = JSON.parse(headerData.topbarJson);
}
if (headerData.logo) {
header.logo = headerData.logo;
}
header.updatedAt = new Date();
await header.save();
// Process menu updates if any
let menuUpdateCount = 0;
let menuErrorCount = 0;
if (headerData.menuUpdates) {
try {
const updates = JSON.parse(headerData.menuUpdates);
if (Array.isArray(updates) && updates.length > 0) {
const Menu = require('../models/menuHeader');
for (const update of updates) {
try {
const { menuid, title, order, type, parent, fetch, isActive } = update;
const updateData = {
title: title,
order: order,
type: type,
parent: parent
// Prepare data for view
const data = header
? {
topbar: {
contactInfo: {
phone: header.top?.phone || "",
email: header.top?.email || "",
location: header.top?.location || "",
},
socialLinks: header.top?.socialLinks || [],
},
logo: header.logo?.light || "",
}
: {
topbar: {
contactInfo: {
phone: "",
email: "",
location: "",
},
socialLinks: [],
},
logo: "",
};
// Add fetch field if provided
if (fetch !== undefined) {
updateData.fetch = fetch;
}
const activeTab = req.query.tab || "topbar";
// Always fetch menu items to ensure they are available even if the user
// switches tabs client-side
const items = await HeaderMenu.find().sort({ order: 1 });
const menuData = {
flat: items,
tree: buildTree(items)
};
// Add isActive field if provided
if (isActive !== undefined) {
updateData.isActive = isActive;
}
const result = await Menu.findOneAndUpdate(
{ menuid: menuid },
updateData,
{ new: true }
);
if (result) {
menuUpdateCount++;
} else {
console.warn(`Menu item not found for update: ${menuid}`);
menuErrorCount++;
}
} catch (innerErr) {
console.error(`Error updating menu item ${update.menuid}:`, innerErr);
menuErrorCount++;
}
}
}
} catch (err) {
console.error('Error processing menu updates:', err);
}
res.render("admin/header/index", {
layout: "layouts/main",
title: "Header Management",
user: req.session.user || null,
data: data,
activeTab: activeTab,
menuData: menuData
});
} catch (error) {
console.error("Error loading header management:", error);
res.status(500).render("page/error", {
title: "Error",
message: "Failed to load header management page",
});
}
let flashMsg = 'Header updated successfully.';
if (menuUpdateCount > 0) {
flashMsg += ` Updated ${menuUpdateCount} menu items.`;
}
if (menuErrorCount > 0) {
req.flash('error_msg', `Updated ${menuUpdateCount} items, but failed to update ${menuErrorCount} items. Check logs.`);
} else {
req.flash('success_msg', flashMsg);
}
// Redirect back to the active tab
const activeTab = req.body.activeTab || 'topbar';
res.redirect(`/admin/header?activeTab=${activeTab}`);
} catch (error) {
console.error('Error updating header:', error);
req.flash('error_msg', 'Error updating header: ' + error.message);
res.redirect('/admin/header');
}
};
// Update menu structure (order and parent)
exports.updateMenu = async (req, res) => {
try {
const { updates } = req.body;
if (!updates || !Array.isArray(updates)) {
return res.status(400).json({
success: false,
error: 'Invalid updates data'
});
// Admin: Get all headers (API)
exports.getAll = async (req, res) => {
try {
const headers = await Header.find().sort({ order: 1 });
res.json({
success: true,
data: headers,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
const Menu = require('../models/menuHeader');
// Update each menu item
for (const update of updates) {
const { menuid, title, order, type, parent, fetch, isActive } = update;
console.log(menuid, title, order, type, parent, fetch, isActive);
const updateData = {
title: title,
order: order,
type: type,
parent: parent
};
// Add fetch field if provided (for level type menus)
if (fetch !== undefined) {
updateData.fetch = fetch;
}
// Add isActive field if provided
if (isActive !== undefined) {
updateData.isActive = isActive;
}
await Menu.findOneAndUpdate(
{ menuid: menuid },
updateData,
{ new: true }
);
// Admin: Get single header
exports.show = async (req, res) => {
try {
const header = await Header.findById(req.params.id);
if (!header) {
return res.status(404).json({
success: false,
message: "Header not found",
});
}
res.json({
success: true,
data: header,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
res.json({ success: true });
} catch (error) {
console.error('Error updating menu structure:', error);
req.flash('error_msg', 'Error updating menu structure: ' + error.message);
res.redirect('/admin/header');
}
};
};
// Admin: Create header
exports.store = async (req, res) => {
try {
const { top, offcanvas, menu, logo, ctaButton, status, order } = req.body;
const header = new Header({
top,
offcanvas,
menu,
logo,
ctaButton,
status: status || "active",
order: order || 1,
});
await header.save();
res.status(201).json({
success: true,
message: "Header created successfully",
data: header,
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message,
});
}
};
// Admin: Update header
exports.update = async (req, res) => {
try {
let { top, topbarJson, offcanvas, menu, logo, ctaButton, status, order } = req.body;
console.log("=== UPDATE REQUEST RECEIVED ===");
console.log("Raw body:", JSON.stringify(req.body, null, 2));
console.log("topbarJson type:", typeof topbarJson);
console.log("topbarJson value:", topbarJson);
// Nếu có topbarJson, parse nó
if (topbarJson && typeof topbarJson === "string") {
try {
const parsedData = JSON.parse(topbarJson);
console.log("✓ Parsed topbarJson successfully:", parsedData);
// Chuyển đổi từ topbarData sang top format
top = {
phone: parsedData.contactInfo?.phone || "",
email: parsedData.contactInfo?.email || "",
location: parsedData.contactInfo?.location || "",
socialLinks: parsedData.socialLinks || [],
};
console.log("✓ Converted to top object:", top);
} catch (e) {
console.error("✗ Error parsing topbarJson:", e.message);
return res.status(400).json({
success: false,
message: "Invalid JSON in topbarJson: " + e.message,
});
}
}
// Nếu không có id, tìm header đầu tiên hoặc tạo mới
let headerId = req.params.id;
if (!headerId) {
// Tìm header đầu tiên
let header = await Header.findOne().sort({ order: 1 });
if (!header) {
console.log("No existing header found, creating new one");
// Tạo header mới nếu chưa có
header = new Header({
top,
offcanvas,
menu,
logo: logo ? { light: logo } : {},
ctaButton,
status: status || "active",
order: order || 1,
});
await header.save();
console.log("✓ Header created:", header._id);
return res.json({
success: true,
message: "Header created successfully",
data: header,
});
}
headerId = header._id;
console.log("✓ Found existing header:", headerId);
}
// Chuẩn bị dữ liệu logo - merge với dữ liệu cũ
let logoData = {};
if (logo) {
// Nếu có logo mới, lấy dữ liệu cũ và update light
const existingHeader = await Header.findById(headerId);
logoData = {
light: logo,
dark: existingHeader?.logo?.dark || "",
alt: existingHeader?.logo?.alt || "",
};
}
const updateData = {
top,
offcanvas,
menu,
ctaButton,
status,
order,
};
if (logo) {
updateData.logo = logoData;
}
console.log("Preparing to update header with data:", JSON.stringify(updateData, null, 2));
const updatedHeader = await Header.findByIdAndUpdate(headerId, updateData, { new: true, runValidators: true });
if (!updatedHeader) {
console.error("✗ Header not found with ID:", headerId);
return res.status(404).json({
success: false,
message: "Header not found",
});
}
console.log("✓ Header updated successfully:", updatedHeader._id);
console.log("Updated header data:", JSON.stringify(updatedHeader, null, 2));
res.json({
success: true,
message: "Header updated successfully",
data: updatedHeader,
});
} catch (error) {
console.error("✗ Error updating header:", error);
res.status(400).json({
success: false,
message: error.message,
});
}
};
// Admin: Update status
exports.updateStatus = async (req, res) => {
try {
const { status } = req.body;
if (!["active", "inactive"].includes(status)) {
return res.status(400).json({
success: false,
message: "Invalid status",
});
}
const header = await Header.findByIdAndUpdate(req.params.id, { status }, { new: true });
if (!header) {
return res.status(404).json({
success: false,
message: "Header not found",
});
}
res.json({
success: true,
message: "Header status updated",
data: header,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Admin: Delete header
exports.destroy = async (req, res) => {
try {
const header = await Header.findByIdAndDelete(req.params.id);
if (!header) {
return res.status(404).json({
success: false,
message: "Header not found",
});
}
res.json({
success: true,
message: "Header deleted successfully",
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Public API: Get active header
exports.api = async (req, res) => {
try {
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
return res.status(404).json({
success: false,
message: "No active header found",
});
}
res.json({
success: true,
data: header,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Public API: Get menu tree structure
exports.getMenuTreeAPI = async (req, res) => {
try {
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header || !header.menu) {
return res.status(404).json({
success: false,
message: "No active menu found",
});
}
res.json({
success: true,
data: header.menu,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};

View File

@@ -0,0 +1,175 @@
const HeaderMenu = require("../models/HeaderMenu");
const slugify = require("slugify");
/**
* Helper: Build tree structure from flat array
*/
const buildMenuTree = (items, parentId = null, isPublic = false) => {
const branch = [];
const children = items.filter(item =>
String(item.parentId) === String(parentId) || (item.parentId === null && parentId === null)
);
for (const child of children) {
const item = child.toObject ? child.toObject() : { ...child };
// Clean data for public API if requested
let cleanItem = item;
if (isPublic) {
cleanItem = {
id: item._id,
title: item.title,
url: item.url,
type: item.type
};
}
const subChildren = buildMenuTree(items, item._id, isPublic);
cleanItem.children = subChildren.length > 0 ? subChildren : [];
branch.push(cleanItem);
}
return branch.sort((a, b) => a.order - b.order);
};
/**
* Helper: Recursive delete children
*/
const deleteRecursive = async (parentId) => {
const children = await HeaderMenu.find({ parentId });
for (const child of children) {
await deleteRecursive(child._id);
await HeaderMenu.findByIdAndDelete(child._id);
}
};
// 1. Render Menu Tab logic
exports.index = async (req, res) => {
try {
const items = await HeaderMenu.find().sort({ order: 1 });
const menuTree = buildMenuTree(items);
return { menuTree, flatItems: items };
} catch (error) {
console.error("Error fetching menu items:", error);
throw error;
}
};
// 2. Create Menu Item
exports.store = async (req, res) => {
try {
console.log('=== BACKEND: store hit ===');
console.log('Body:', req.body);
const { title, url, parentId, order, status, type } = req.body;
const slug = slugify(title, { lower: true, strict: true });
const newItem = new HeaderMenu({
title,
slug,
url,
parentId: parentId || null,
order: order || 0,
status: status || "active",
type: type || "internal"
});
const savedItem = await newItem.save();
console.log('=== MENU CREATED ===', savedItem);
req.flash("success_msg", "Menu item created successfully");
res.redirect("/admin/header?tab=menu");
} catch (error) {
console.error('=== CREATE MENU ERROR ===', error);
req.flash("error_msg", "Failed to create menu item: " + error.message);
res.redirect("/admin/header?tab=menu");
}
};
// 3. Update Menu Item
exports.update = async (req, res) => {
try {
const { id } = req.params;
console.log('=== BACKEND: update hit ===', { id });
console.log('Body:', req.body);
const { title, url, parentId, order, status, type } = req.body;
const updateData = {
url,
parentId: parentId || null,
order,
status,
type
};
if (title) {
updateData.title = title;
updateData.slug = slugify(title, { lower: true, strict: true });
}
const updated = await HeaderMenu.findByIdAndUpdate(id, updateData, { new: true });
if (!updated) {
console.log('=== UPDATE MENU NOT FOUND ===', id);
req.flash("error_msg", "Menu item not found");
} else {
console.log('=== MENU UPDATED ===', updated);
req.flash("success_msg", "Menu item updated successfully");
}
res.redirect("/admin/header?tab=menu");
} catch (error) {
console.error('=== UPDATE MENU ERROR ===', error);
req.flash("error_msg", "Update failed: " + error.message);
res.redirect("/admin/header?tab=menu");
}
};
// 4. Delete Menu Item (Cascade delete children)
exports.destroy = async (req, res) => {
try {
const { id } = req.body;
const menuId = id || req.params.id;
console.log('=== BACKEND: destroy hit ===', { menuId, body: req.body });
await deleteRecursive(menuId);
await HeaderMenu.findByIdAndDelete(menuId);
console.log('=== MENU DELETED ===', menuId);
req.flash("success_msg", "Menu item and its sub-menu deleted successfully");
res.redirect("/admin/header?tab=menu");
} catch (error) {
console.error('=== DELETE MENU ERROR ===', error);
req.flash("error_msg", "Delete failed: " + error.message);
res.redirect("/admin/header?tab=menu");
}
};
// 5. Reorder Menu
exports.reorder = async (req, res) => {
try {
const { items } = req.body; // Array of { id, order, parentId }
if (items && Array.isArray(items)) {
const bulkOps = items.map(item => ({
updateOne: {
filter: { _id: item.id },
update: { order: item.order, parentId: item.parentId || null }
}
}));
await HeaderMenu.bulkWrite(bulkOps);
return res.json({ success: true, message: "Reordered successfully" });
}
res.status(400).json({ success: false, message: "Invalid data" });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// Public API: Get active menu as clean tree
exports.api = async (req, res) => {
try {
const items = await HeaderMenu.find({ status: "active" }).sort({ order: 1 });
const tree = buildMenuTree(items, null, true);
res.json({ success: true, data: tree });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};

View File

@@ -0,0 +1,321 @@
const Header = require("../models/header");
// Get all social links
exports.index = async (req, res) => {
try {
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
return res.status(404).json({
success: false,
message: "No active header found",
});
}
res.json({
success: true,
data: header.top?.socialLinks || [],
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Get single social link by platform
exports.show = async (req, res) => {
try {
const { platform } = req.params;
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
return res.status(404).json({
success: false,
message: "No active header found",
});
}
const socialLink = header.top?.socialLinks?.find((link) => link.platform === platform);
if (!socialLink) {
return res.status(404).json({
success: false,
message: "Social link not found",
});
}
res.json({
success: true,
data: socialLink,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Create social link
exports.store = async (req, res) => {
try {
let { platform, url, icon } = req.body;
// Convert platform to lowercase
platform = platform.toLowerCase().trim();
url = url.trim();
icon = icon ? icon.trim() : null;
console.log("Creating social link:", { platform, url, icon });
// Validate required fields
if (!platform || !url) {
console.log("Validation failed: platform or url missing");
return res.status(400).json({
success: false,
message: "Platform and URL are required",
});
}
// Validate platform is in enum
const validPlatforms = ["linkedin", "twitter", "instagram", "youtube", "facebook"];
if (!validPlatforms.includes(platform)) {
console.log("Invalid platform:", platform);
return res.status(400).json({
success: false,
message: `Invalid platform. Must be one of: ${validPlatforms.join(", ")}`,
});
}
// Find header
let header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
console.log("No active header found");
return res.status(404).json({
success: false,
message: "No active header found",
});
}
console.log("Found header:", header._id);
// Check if platform already exists
const existingLink = header.top?.socialLinks?.find((link) => link.platform === platform);
if (existingLink) {
console.log("Platform already exists:", platform);
return res.status(400).json({
success: false,
message: `Social link for ${platform} already exists`,
});
}
// Add new social link
if (!header.top) {
header.top = {};
}
if (!header.top.socialLinks) {
header.top.socialLinks = [];
}
// Calculate next order number
const maxOrder =
header.top.socialLinks.length > 0 ? Math.max(...header.top.socialLinks.map((link) => link.order || 0)) : 0;
header.top.socialLinks.push({
platform,
url,
icon: icon || `fa-brands fa-${platform}`,
order: maxOrder + 1,
});
console.log("Saving header with new social link");
await header.save();
console.log("Social link created successfully");
res.status(201).json({
success: true,
message: "Social link created successfully",
data: header.top.socialLinks[header.top.socialLinks.length - 1],
});
} catch (error) {
console.error("Error creating social link:", error);
res.status(400).json({
success: false,
message: error.message,
});
}
};
// Update social link
exports.update = async (req, res) => {
try {
let { platform } = req.params;
let { url, icon } = req.body;
// Convert to lowercase
platform = platform.toLowerCase().trim();
url = url.trim();
icon = icon ? icon.trim() : null;
// Validate required fields
if (!url) {
return res.status(400).json({
success: false,
message: "URL is required",
});
}
// Find header
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
return res.status(404).json({
success: false,
message: "No active header found",
});
}
// Find and update social link
const socialLink = header.top?.socialLinks?.find((link) => link.platform === platform);
if (!socialLink) {
return res.status(404).json({
success: false,
message: "Social link not found",
});
}
socialLink.url = url;
if (icon) {
socialLink.icon = icon;
}
await header.save();
res.json({
success: true,
message: "Social link updated successfully",
data: socialLink,
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message,
});
}
};
// Delete social link
exports.destroy = async (req, res) => {
try {
let { platform } = req.params;
// Convert to lowercase
platform = platform.toLowerCase().trim();
console.log("Deleting social link:", platform);
// Find header
const header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
console.log("No active header found");
return res.status(404).json({
success: false,
message: "No active header found",
});
}
// Find and remove social link
const index = header.top?.socialLinks?.findIndex((link) => link.platform === platform);
if (index === -1 || index === undefined) {
console.log("Social link not found:", platform);
return res.status(404).json({
success: false,
message: "Social link not found",
});
}
const deletedLink = header.top.socialLinks.splice(index, 1);
console.log("Saving header after delete");
await header.save();
console.log("Social link deleted successfully");
res.json({
success: true,
message: "Social link deleted successfully",
data: deletedLink[0],
});
} catch (error) {
console.error("Error deleting social link:", error);
res.status(500).json({
success: false,
message: error.message,
});
}
};
// Bulk update social links (used for reordering and batch updates)
exports.reorder = async (req, res) => {
try {
const { socialLinks } = req.body;
if (!Array.isArray(socialLinks)) {
return res.status(400).json({
success: false,
message: "socialLinks must be an array",
});
}
// Find header
let header = await Header.findOne({ status: "active" }).sort({ order: 1 });
if (!header) {
return res.status(404).json({
success: false,
message: "No active header found",
});
}
// Validate all social links
for (const link of socialLinks) {
if (!link.platform || !link.url) {
return res.status(400).json({
success: false,
message: "Each social link must have platform and url",
});
}
}
// Update social links with order field
if (!header.top) {
header.top = {};
}
header.top.socialLinks = socialLinks.map((link, index) => ({
platform: link.platform,
url: link.url,
icon: link.icon || `fa-brands fa-${link.platform}`,
order: link.order || index + 1, // Use provided order or calculate from index
}));
await header.save();
res.json({
success: true,
message: "Social links updated successfully",
data: header.top.socialLinks,
});
} catch (error) {
res.status(400).json({
success: false,
message: error.message,
});
}
};