const Header = require("../models/header"); const HeaderMenu = require("../models/HeaderMenu"); /** * 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) ); 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 branch.sort((a, b) => a.order - b.order); }; // Admin: Render header management page exports.index = async (req, res) => { try { const header = await Header.findOne().sort({ order: 1 }); // 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: "", }; 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) }; 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", }); } }; // 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, }); } }; // 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, }); } }; // 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, }); } };