Merge pull request 'refactor(header): improve code formatting and add JSON response support' (#30) from refactor/huy-06022026-header-code-formatting into main

Reviewed-on: UKSOURCE/cms.hailearning.edu.vn#30
This commit is contained in:
2026-02-06 04:41:45 +00:00
4 changed files with 112 additions and 40 deletions

View File

@@ -6,13 +6,13 @@ const slugify = require("slugify");
*/
const buildMenuTree = (items, parentId = null, isPublic = false) => {
const branch = [];
const children = items.filter(item =>
String(item.parentId) === String(parentId) || (item.parentId === null && parentId === null)
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) {
@@ -20,7 +20,7 @@ const buildMenuTree = (items, parentId = null, isPublic = false) => {
id: item._id,
title: item.title,
url: item.url,
type: item.type
type: item.type,
};
}
@@ -57,8 +57,8 @@ exports.index = async (req, res) => {
// 2. Create Menu Item
exports.store = async (req, res) => {
try {
console.log('=== BACKEND: store hit ===');
console.log('Body:', req.body);
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 });
@@ -69,15 +69,27 @@ exports.store = async (req, res) => {
parentId: parentId || null,
order: order || 0,
status: status || "active",
type: type || "internal"
type: type || "internal",
});
const savedItem = await newItem.save();
console.log('=== MENU CREATED ===', savedItem);
console.log("=== MENU CREATED ===", savedItem);
// Return JSON for AJAX requests
if (req.xhr || req.headers.accept?.indexOf("json") > -1) {
return res.json({ success: true, message: "Menu item created successfully", data: savedItem });
}
req.flash("success_msg", "Menu item created successfully");
res.redirect("/admin/header?tab=menu");
} catch (error) {
console.error('=== CREATE MENU ERROR ===', error);
console.error("=== CREATE MENU ERROR ===", error);
// Return JSON for AJAX requests
if (req.xhr || req.headers.accept?.indexOf("json") > -1) {
return res.status(400).json({ success: false, message: error.message });
}
req.flash("error_msg", "Failed to create menu item: " + error.message);
res.redirect("/admin/header?tab=menu");
}
@@ -87,16 +99,16 @@ exports.store = async (req, res) => {
exports.update = async (req, res) => {
try {
const { id } = req.params;
console.log('=== BACKEND: update hit ===', { id });
console.log('Body:', req.body);
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
const updateData = {
url,
parentId: parentId || null,
order,
status,
type,
};
if (title) {
@@ -105,17 +117,35 @@ exports.update = async (req, res) => {
}
const updated = await HeaderMenu.findByIdAndUpdate(id, updateData, { new: true });
if (!updated) {
console.log('=== UPDATE MENU NOT FOUND ===', id);
console.log("=== UPDATE MENU NOT FOUND ===", id);
// Return JSON for AJAX requests
if (req.xhr || req.headers.accept?.indexOf("json") > -1) {
return res.status(404).json({ success: false, message: "Menu item not found" });
}
req.flash("error_msg", "Menu item not found");
} else {
console.log('=== MENU UPDATED ===', updated);
console.log("=== MENU UPDATED ===", updated);
// Return JSON for AJAX requests
if (req.xhr || req.headers.accept?.indexOf("json") > -1) {
return res.json({ success: true, message: "Menu item updated successfully", data: updated });
}
req.flash("success_msg", "Menu item updated successfully");
}
res.redirect("/admin/header?tab=menu");
} catch (error) {
console.error('=== UPDATE MENU ERROR ===', error);
console.error("=== UPDATE MENU ERROR ===", error);
// Return JSON for AJAX requests
if (req.xhr || req.headers.accept?.indexOf("json") > -1) {
return res.status(400).json({ success: false, message: error.message });
}
req.flash("error_msg", "Update failed: " + error.message);
res.redirect("/admin/header?tab=menu");
}
@@ -126,16 +156,16 @@ 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 });
console.log("=== BACKEND: destroy hit ===", { menuId, body: req.body });
await deleteRecursive(menuId);
await HeaderMenu.findByIdAndDelete(menuId);
console.log('=== MENU DELETED ===', 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);
console.error("=== DELETE MENU ERROR ===", error);
req.flash("error_msg", "Delete failed: " + error.message);
res.redirect("/admin/header?tab=menu");
}
@@ -145,18 +175,18 @@ exports.destroy = async (req, res) => {
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 => ({
const bulkOps = items.map((item) => ({
updateOne: {
filter: { _id: item.id },
update: { order: item.order, parentId: item.parentId || null }
}
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 });