forked from UKSOURCE/cms.lams
375 lines
9.4 KiB
JavaScript
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,
|
|
});
|
|
}
|
|
};
|
|
|