feat(header-menu): add maintenance mode functionality and related UI elements

This commit is contained in:
Tống Thành Đạt
2026-04-08 20:57:28 +07:00
parent ffe2f12bb3
commit b6f1b92feb
6 changed files with 135 additions and 3 deletions

View File

@@ -1,6 +1,36 @@
const HeaderMenu = require("../models/headerMenu");
const slugify = require("slugify");
const parseBooleanFlag = (value) => {
if (typeof value === "boolean") {
return value;
}
if (typeof value === "string") {
const normalized = value.trim().toLowerCase();
return ["true", "1", "on", "yes"].includes(normalized);
}
return false;
};
const normalizeInternalUrl = (url = "") => {
if (typeof url !== "string") {
return null;
}
const trimmed = url.trim();
if (!trimmed || !trimmed.startsWith("/")) {
return null;
}
if (trimmed === "/") {
return "/";
}
return trimmed.replace(/\/+$/, "");
};
/**
* Helper: Build tree structure from flat array
*/
@@ -19,8 +49,10 @@ const buildMenuTree = (items, parentId = null, isPublic = false) => {
cleanItem = {
id: item._id,
title: item.title,
url: item.url,
url: item.is_maintainance ? "/maintenance" : item.url,
originalUrl: item.url,
type: item.type,
is_maintainance: Boolean(item.is_maintainance),
};
}
@@ -59,7 +91,7 @@ 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 { title, url, parentId, order, status, type, is_maintainance } = req.body;
const slug = slugify(title, { lower: true, strict: true });
const newItem = new HeaderMenu({
@@ -70,6 +102,7 @@ exports.store = async (req, res) => {
order: order || 0,
status: status || "active",
type: type || "internal",
is_maintainance: parseBooleanFlag(is_maintainance),
});
const savedItem = await newItem.save();
@@ -101,7 +134,7 @@ exports.update = async (req, res) => {
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 { title, url, parentId, order, status, type, is_maintainance } = req.body;
const updateData = {
url,
@@ -109,6 +142,7 @@ exports.update = async (req, res) => {
order,
status,
type,
is_maintainance: parseBooleanFlag(is_maintainance),
};
if (title) {
@@ -203,3 +237,33 @@ exports.api = async (req, res) => {
res.status(500).json({ success: false, message: error.message });
}
};
exports.maintenanceStatus = async (req, res) => {
try {
const items = await HeaderMenu.find({
status: "active",
is_maintainance: true,
})
.select("title url slug")
.sort({ order: 1 })
.lean();
const urls = [...new Set(items.map((item) => normalizeInternalUrl(item.url)).filter(Boolean))];
res.json({
success: true,
data: {
enabled: items.length > 0,
urls,
items: items.map((item) => ({
id: String(item._id),
title: item.title,
slug: item.slug,
url: item.url,
})),
},
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};