Files
2026-04-22 15:22:22 +07:00

375 lines
9.4 KiB
JavaScript

const Header = require("../models/header");
const HeaderMenu = require("../models/headerMenu");
const writeAuditLog = require("../audit/writeAuditLog");
const diffObject = require("../audit/diffObject");
const AUDIT_ACTIONS = require("../constants/auditAction");
/**
* 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
? {
logo: header.logo?.light || "",
signInButton: header.signInButton || {
label: "Sign In",
href: "/signin",
},
ctaButton: header.ctaButton || {
label: "Request Info",
href: "/request",
},
}
: {
logo: "",
signInButton: { label: "Sign In", href: "/signin" },
ctaButton: { label: "Request Info", href: "/request" },
};
const activeTab = req.query.tab || "logo";
// 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 { logo, signInButton, ctaButton, status, order } = req.body;
const 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();
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 {
const { logo, signInButton, ctaButton, status, order } = req.body;
console.log("=== UPDATE REQUEST RECEIVED ===");
console.log("Raw body:", JSON.stringify(req.body, null, 2));
// Upsert logic: find existing header or prepare to create new one
let header = await Header.findOne().sort({ order: 1 });
let headerId = header?._id;
// Capture BEFORE state for audit logging
const beforeData = header
? JSON.parse(JSON.stringify(header.toObject()))
: {};
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);
// 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,
});
}
// Prepare logo data - merge with existing data
let logoData = header.logo || {};
if (logo) {
logoData = {
light: logo,
dark: header.logo?.dark || "",
alt: header.logo?.alt || "",
};
}
// Prepare update data
const updateData = {
logo: logoData,
signInButton: signInButton || header.signInButton,
ctaButton: ctaButton || header.ctaButton,
};
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),
);
// Update existing header
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",
});
}
// Capture AFTER state for audit logging
const afterData = JSON.parse(JSON.stringify(updatedHeader.toObject()));
// Audit logging - Header Updated
const changes = diffObject(beforeData, afterData);
if (changes.length > 0) {
await writeAuditLog({
model: "Header",
documentId: updatedHeader._id,
action: AUDIT_ACTIONS.UPDATE_HEADER,
before: beforeData,
after: afterData,
changes,
req,
});
}
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 header (returns header regardless of status)
exports.api = async (req, res) => {
try {
const header = await Header.findOne().sort({
order: 1,
});
if (!header) {
return res.status(404).json({
success: false,
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: {
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({
success: false,
message: error.message,
});
}
};