forked from UKSOURCE/cms.hailearning.edu.vn
add api headermenu and crud management
This commit is contained in:
175
controllers/headerMenuController.js
Normal file
175
controllers/headerMenuController.js
Normal 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 (called from Header index or directly if route allows)
|
||||
exports.renderMenuTab = 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.createMenu = async (req, res) => {
|
||||
try {
|
||||
console.log('=== BACKEND: createMenu 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.updateMenu = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
console.log('=== BACKEND: updateMenu 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.deleteMenu = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.body;
|
||||
const menuId = id || req.params.id;
|
||||
console.log('=== BACKEND: deleteMenu 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.reorderMenu = 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 });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user