forked from UKSOURCE/cms.lams
Adding backend of Student Support, Contact, Request
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
const { addBaseUrlToImages } = require("../utils/imageHelper");
|
||||
const { normalizeIconClass } = require("../utils/iconOptions");
|
||||
const StudentSupport = require("../models/studentSupport");
|
||||
const writeAuditLog = require("../audit/writeAuditLog");
|
||||
const diffObject = require("../audit/diffObject");
|
||||
const AUDIT_ACTIONS = require("../constants/auditAction");
|
||||
|
||||
const SLUG = "student-support";
|
||||
|
||||
function emptyPageShape() {
|
||||
return {
|
||||
metadata: { title: "", description: "" },
|
||||
hero: {
|
||||
badge: "",
|
||||
title: "",
|
||||
description: "",
|
||||
primaryButton: { label: "", href: "" },
|
||||
secondaryButton: { label: "", href: "" },
|
||||
image: "",
|
||||
imageAlt: "",
|
||||
},
|
||||
directory: { title: "", subtitle: "", services: [] },
|
||||
contactSection: {
|
||||
heading: "",
|
||||
description: "",
|
||||
channels: [],
|
||||
form: {
|
||||
heading: "",
|
||||
fields: {
|
||||
firstName: { label: "", placeholder: "" },
|
||||
lastName: { label: "", placeholder: "" },
|
||||
studentId: { label: "", placeholder: "" },
|
||||
department: { label: "" },
|
||||
message: { label: "", placeholder: "" },
|
||||
},
|
||||
departments: [],
|
||||
submitLabel: "",
|
||||
successMessage: "",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function parseJsonField(raw) {
|
||||
if (raw == null || raw === "") return null;
|
||||
if (typeof raw === "object") return raw;
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Chuẩn hoá mọi field icon theo whitelist (dropdown admin). */
|
||||
function sanitizeIcons(payload) {
|
||||
if (!payload) return payload;
|
||||
if (payload.hero) {
|
||||
/* hero không có icon list */
|
||||
}
|
||||
if (payload.directory?.services?.length) {
|
||||
payload.directory.services = payload.directory.services.map((s) => ({
|
||||
...s,
|
||||
icon: normalizeIconClass(s.icon),
|
||||
hoursIcon: normalizeIconClass(s.hoursIcon),
|
||||
}));
|
||||
}
|
||||
if (payload.contactSection?.channels?.length) {
|
||||
payload.contactSection.channels = payload.contactSection.channels.map((c) => ({
|
||||
...c,
|
||||
icon: normalizeIconClass(c.icon),
|
||||
}));
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function getDocument() {
|
||||
return StudentSupport.findOne({ slug: SLUG }).lean();
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/student-support — public payload giống file JSON frontend (không kèm _id/slug).
|
||||
*/
|
||||
exports.api = async (req, res) => {
|
||||
try {
|
||||
const doc = await getDocument();
|
||||
if (!doc) {
|
||||
return res.status(404).json({ error: "Student Support content not found" });
|
||||
}
|
||||
const baseUrl =
|
||||
process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`;
|
||||
const body = {
|
||||
metadata: doc.metadata || {},
|
||||
hero: doc.hero || {},
|
||||
directory: doc.directory || { services: [] },
|
||||
contactSection: doc.contactSection || {},
|
||||
};
|
||||
res.json(addBaseUrlToImages(body, baseUrl));
|
||||
} catch (err) {
|
||||
console.error("studentSupport.api:", err);
|
||||
res.status(500).json({ error: "Error loading Student Support data" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.index = async (req, res) => {
|
||||
try {
|
||||
const doc = await getDocument();
|
||||
const data = doc
|
||||
? {
|
||||
metadata: doc.metadata || {},
|
||||
hero: doc.hero || {},
|
||||
directory: doc.directory || { services: [] },
|
||||
contactSection: doc.contactSection || {},
|
||||
}
|
||||
: emptyPageShape();
|
||||
|
||||
const { ICON_OPTIONS } = require("../utils/iconOptions");
|
||||
const frontendUrl = process.env.FRONTEND_URL || "";
|
||||
|
||||
res.render("admin/student-support/index", {
|
||||
title: "Student Support",
|
||||
layout: "layouts/main",
|
||||
data,
|
||||
iconOptions: ICON_OPTIONS,
|
||||
frontendUrl,
|
||||
currentPath: req.path,
|
||||
user: req.session.user,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("studentSupport.index:", error);
|
||||
req.flash("error_msg", "Could not load Student Support page");
|
||||
res.redirect("/admin/dashboard");
|
||||
}
|
||||
};
|
||||
|
||||
exports.update = async (req, res) => {
|
||||
try {
|
||||
const metadata = parseJsonField(req.body.metadata);
|
||||
const hero = parseJsonField(req.body.hero);
|
||||
const directory = parseJsonField(req.body.directory);
|
||||
const contactSection = parseJsonField(req.body.contactSection);
|
||||
|
||||
if (!metadata || !hero || !directory || !contactSection) {
|
||||
req.flash("error_msg", "Invalid form payload. Please try again.");
|
||||
return res.redirect("/admin/student-support");
|
||||
}
|
||||
|
||||
const payload = sanitizeIcons({
|
||||
metadata,
|
||||
hero,
|
||||
directory,
|
||||
contactSection,
|
||||
});
|
||||
|
||||
let doc = await StudentSupport.findOne({ slug: SLUG });
|
||||
const beforeData = doc ? JSON.parse(JSON.stringify(doc.toObject())) : {};
|
||||
|
||||
if (!doc) {
|
||||
doc = new StudentSupport({ slug: SLUG, ...payload });
|
||||
} else {
|
||||
doc.set(payload);
|
||||
}
|
||||
|
||||
await doc.save();
|
||||
const afterData = JSON.parse(JSON.stringify(doc.toObject()));
|
||||
|
||||
const changes = diffObject(beforeData, afterData);
|
||||
if (changes.length > 0) {
|
||||
await writeAuditLog({
|
||||
model: "StudentSupport",
|
||||
documentId: doc._id,
|
||||
action: AUDIT_ACTIONS.UPDATE_STUDENT_SUPPORT,
|
||||
before: beforeData,
|
||||
after: afterData,
|
||||
changes,
|
||||
req,
|
||||
});
|
||||
}
|
||||
|
||||
req.flash("success_msg", "Student Support updated successfully");
|
||||
res.redirect("/admin/student-support");
|
||||
} catch (err) {
|
||||
console.error("studentSupport.update:", err);
|
||||
req.flash("error_msg", err.message || "Error updating Student Support");
|
||||
res.redirect("/admin/student-support");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user