forked from UKSOURCE/cms.lams
header
This commit is contained in:
+88
-151
@@ -33,29 +33,23 @@ exports.index = async (req, res) => {
|
||||
// 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 || "",
|
||||
signInButton: header.signInButton || {
|
||||
label: "Sign In",
|
||||
href: "/signin",
|
||||
},
|
||||
ctaButton: header.ctaButton || {
|
||||
label: "Request Info",
|
||||
href: "/request",
|
||||
},
|
||||
}
|
||||
: {
|
||||
topbar: {
|
||||
contactInfo: {
|
||||
phone: "",
|
||||
email: "",
|
||||
location: "",
|
||||
},
|
||||
socialLinks: [],
|
||||
},
|
||||
logo: "",
|
||||
signInButton: { label: "Sign In", href: "/signin" },
|
||||
ctaButton: { label: "Request Info", href: "/request" },
|
||||
};
|
||||
|
||||
const activeTab = req.query.tab || "topbar";
|
||||
const activeTab = req.query.tab || "logo";
|
||||
|
||||
// Always fetch menu items to ensure they are available even if the user
|
||||
// switches tabs client-side
|
||||
@@ -123,14 +117,12 @@ exports.show = async (req, res) => {
|
||||
// Admin: Create header
|
||||
exports.store = async (req, res) => {
|
||||
try {
|
||||
const { top, offcanvas, menu, logo, ctaButton, status, order } = req.body;
|
||||
const { logo, signInButton, ctaButton, status, order } = req.body;
|
||||
|
||||
const header = new Header({
|
||||
top,
|
||||
offcanvas,
|
||||
menu,
|
||||
logo,
|
||||
ctaButton,
|
||||
logo: logo ? { light: logo } : {},
|
||||
signInButton: signInButton || { label: "Sign In", href: "/signin" },
|
||||
ctaButton: ctaButton || {},
|
||||
status: status || "active",
|
||||
order: order || 1,
|
||||
});
|
||||
@@ -152,129 +144,81 @@ exports.store = async (req, res) => {
|
||||
// Admin: Update header
|
||||
exports.update = async (req, res) => {
|
||||
try {
|
||||
let { top, topbarJson, offcanvas, menu, logo, ctaButton, status, order } =
|
||||
req.body;
|
||||
const { logo, signInButton, 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 || [],
|
||||
};
|
||||
// Upsert logic: find existing header or prepare to create new one
|
||||
let header = await Header.findOne().sort({ order: 1 });
|
||||
let headerId = header?._id;
|
||||
|
||||
if (logo) {
|
||||
updateData.logo = logoData;
|
||||
}
|
||||
// Capture BEFORE state for audit logging
|
||||
const beforeData = header
|
||||
? JSON.parse(JSON.stringify(header.toObject()))
|
||||
: {};
|
||||
|
||||
console.log(
|
||||
"Preparing to update header with data:",
|
||||
JSON.stringify(updateData, null, 2),
|
||||
);
|
||||
if (!header) {
|
||||
console.log("No existing header found, creating new one");
|
||||
// Create new header document
|
||||
header = new Header({
|
||||
logo: logo ? { light: logo } : {},
|
||||
signInButton: signInButton || { label: "Sign In", href: "/signin" },
|
||||
ctaButton: ctaButton || {},
|
||||
status: status || "active",
|
||||
order: order || 1,
|
||||
});
|
||||
await header.save();
|
||||
console.log("✓ Header created:", header._id);
|
||||
|
||||
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",
|
||||
});
|
||||
}
|
||||
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,
|
||||
// Audit log for creation
|
||||
const afterData = JSON.parse(JSON.stringify(header.toObject()));
|
||||
const changes = diffObject(beforeData, afterData);
|
||||
if (changes.length > 0) {
|
||||
await writeAuditLog({
|
||||
model: "Header",
|
||||
documentId: header._id,
|
||||
action: AUDIT_ACTIONS.UPDATE_HEADER,
|
||||
before: beforeData,
|
||||
after: afterData,
|
||||
changes,
|
||||
req,
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "Header created successfully",
|
||||
data: header,
|
||||
});
|
||||
}
|
||||
|
||||
// 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 = {};
|
||||
// Prepare logo data - merge with existing data
|
||||
let logoData = header.logo || {};
|
||||
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 || "",
|
||||
dark: header.logo?.dark || "",
|
||||
alt: header.logo?.alt || "",
|
||||
};
|
||||
}
|
||||
|
||||
// Prepare update data
|
||||
const updateData = {
|
||||
top,
|
||||
offcanvas,
|
||||
menu,
|
||||
ctaButton,
|
||||
status,
|
||||
order,
|
||||
logo: logoData,
|
||||
signInButton: signInButton || header.signInButton,
|
||||
ctaButton: ctaButton || header.ctaButton,
|
||||
};
|
||||
|
||||
if (logo) {
|
||||
updateData.logo = logoData;
|
||||
}
|
||||
if (status !== undefined) updateData.status = status;
|
||||
if (order !== undefined) updateData.order = order;
|
||||
|
||||
console.log(
|
||||
"Preparing to update header with data:",
|
||||
JSON.stringify(updateData, null, 2),
|
||||
);
|
||||
|
||||
// ✅ Capture BEFORE state
|
||||
const beforeHeader = await Header.findById(headerId);
|
||||
const beforeData = beforeHeader
|
||||
? JSON.parse(JSON.stringify(beforeHeader.toObject()))
|
||||
: {};
|
||||
|
||||
// Update existing header
|
||||
const updatedHeader = await Header.findByIdAndUpdate(headerId, updateData, {
|
||||
new: true,
|
||||
runValidators: true,
|
||||
@@ -288,10 +232,10 @@ exports.update = async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ Capture AFTER state
|
||||
// Capture AFTER state for audit logging
|
||||
const afterData = JSON.parse(JSON.stringify(updatedHeader.toObject()));
|
||||
|
||||
// ✅ AUDIT LOGGING - Header Updated
|
||||
// Audit logging - Header Updated
|
||||
const changes = diffObject(beforeData, afterData);
|
||||
if (changes.length > 0) {
|
||||
await writeAuditLog({
|
||||
@@ -384,23 +328,41 @@ exports.destroy = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Public API: Get active header
|
||||
// Public API: Get header (returns header regardless of status)
|
||||
exports.api = async (req, res) => {
|
||||
try {
|
||||
const header = await Header.findOne({ status: "active" }).sort({
|
||||
const header = await Header.findOne().sort({
|
||||
order: 1,
|
||||
});
|
||||
|
||||
if (!header) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "No active header found",
|
||||
message: "No header found",
|
||||
});
|
||||
}
|
||||
|
||||
const baseUrl = (process.env.BACKEND_URL || `${req.protocol}://${req.get('host')}`).replace(/\/$/, '');
|
||||
const rawLogoPath = header.logo?.light || '';
|
||||
const logoImage = rawLogoPath.startsWith('http') ? rawLogoPath : `${baseUrl}${rawLogoPath}`;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: header,
|
||||
data: {
|
||||
logo: {
|
||||
image: logoImage,
|
||||
href: "/",
|
||||
},
|
||||
signInButton: {
|
||||
label: header.signInButton?.label || "Sign In",
|
||||
href: header.signInButton?.href || "/signin",
|
||||
},
|
||||
ctaButton: {
|
||||
label: header.ctaButton?.label || "Request Info",
|
||||
href: header.ctaButton?.href || "/request",
|
||||
},
|
||||
status: header.status || "active",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
@@ -410,28 +372,3 @@ exports.api = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ const buildMenuTree = (items, parentId = null, isPublic = false) => {
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
type: item.type,
|
||||
status: item.status || 'active',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ exports.reorder = async (req, res) => {
|
||||
// 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 items = await HeaderMenu.find().sort({ order: 1 });
|
||||
const tree = buildMenuTree(items, null, true);
|
||||
res.json({ success: true, data: tree });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user