forked from UKSOURCE/cms.hailearning.edu.vn
403 lines
11 KiB
JavaScript
403 lines
11 KiB
JavaScript
const { getServiceData } = require("../services/service.service");
|
|
const Service = require("../models/service");
|
|
const syncServiceMenu = require("../services/syncServiceMenu");
|
|
const { addBaseUrlToImages, getFullImageUrl } = require("../utils/imageHelper");
|
|
const writeAuditLog = require("../audit/writeAuditLog");
|
|
const diffObject = require("../audit/diffObject");
|
|
const AUDIT_ACTIONS = require("../constants/auditAction");
|
|
|
|
const slugify = require("slugify");
|
|
|
|
// Admin page - Service list
|
|
exports.index = async (req, res) => {
|
|
try {
|
|
const data = await getServiceData();
|
|
console.log(data.services.items.image);
|
|
res.render("admin/service/index", {
|
|
title: "Service Management",
|
|
data,
|
|
layout: "layouts/main",
|
|
getFullImageUrl, // Truyền helper function vào view
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", "Error loading service data");
|
|
res.redirect("/admin/dashboard");
|
|
}
|
|
};
|
|
|
|
// Admin page - Service edit
|
|
exports.edit = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
const data = await getServiceData();
|
|
|
|
const service = data.services?.items?.find((item) => item.slug === slug);
|
|
if (!service) {
|
|
req.flash("error_msg", "Service not found");
|
|
return res.redirect("/admin/service");
|
|
}
|
|
|
|
res.render("admin/service/edit", {
|
|
title: `Edit Service - ${service.name}`,
|
|
service,
|
|
layout: "layouts/main",
|
|
getFullImageUrl,
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", "Error loading service for editing");
|
|
res.redirect("/admin/service");
|
|
}
|
|
};
|
|
|
|
// Update single service
|
|
exports.updateService = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
const currentData = await getServiceData();
|
|
|
|
const serviceIndex = currentData.services?.items?.findIndex(
|
|
(item) => item.slug === slug,
|
|
);
|
|
if (serviceIndex === -1) {
|
|
req.flash("error_msg", "Service not found");
|
|
return res.redirect("/admin/service");
|
|
}
|
|
|
|
const oldItem = JSON.parse(
|
|
JSON.stringify(currentData.services.items[serviceIndex]),
|
|
);
|
|
|
|
// Update service data
|
|
const updatedData = { ...currentData.toObject?.() };
|
|
updatedData.services.items[serviceIndex] = {
|
|
...updatedData.services.items[serviceIndex],
|
|
name: req.body.name,
|
|
slug: req.body.slug,
|
|
description: req.body.description,
|
|
image: req.body.image,
|
|
layout: req.body.layout,
|
|
};
|
|
|
|
if (currentData._id) {
|
|
await Service.findByIdAndUpdate(currentData._id, updatedData);
|
|
} else {
|
|
await Service.create(updatedData);
|
|
}
|
|
const newItem = updatedData.services.items[serviceIndex];
|
|
|
|
const changes = diffObject(oldItem, newItem);
|
|
console.log("USER:", req.session?.user || req.user || "No user found");
|
|
|
|
await writeAuditLog({
|
|
model: "Service",
|
|
documentId: currentData._id,
|
|
action: AUDIT_ACTIONS.UPDATE_SERVICE,
|
|
before: oldItem,
|
|
after: newItem,
|
|
changes,
|
|
req,
|
|
});
|
|
// Sync header menu children to reflect updated service name/slug
|
|
await syncServiceMenu(updatedData.services?.items || []);
|
|
req.flash("success_msg", "Service updated successfully");
|
|
res.redirect("/admin/service");
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", err.message);
|
|
res.redirect("/admin/service");
|
|
}
|
|
};
|
|
|
|
// Admin page - Service details
|
|
exports.details = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
const data = await getServiceData();
|
|
|
|
const service = data.services?.items?.find((item) => item.slug === slug);
|
|
if (!service) {
|
|
req.flash("error_msg", "Service not found");
|
|
return res.redirect("/admin/service");
|
|
}
|
|
|
|
res.render("admin/service/details", {
|
|
title: `Service Details - ${service.name}`,
|
|
service,
|
|
layout: "layouts/main",
|
|
getFullImageUrl, // Truyền helper function vào view
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", "Error loading service details");
|
|
res.redirect("/admin/service");
|
|
}
|
|
};
|
|
|
|
// Update service list
|
|
exports.update = async (req, res) => {
|
|
try {
|
|
const currentData = await getServiceData();
|
|
const sections = [
|
|
"pageTitle",
|
|
"services",
|
|
"destinations",
|
|
"visas",
|
|
"reviews",
|
|
];
|
|
|
|
let updatedData = { ...currentData.toObject?.() };
|
|
let hasChanges = false;
|
|
|
|
sections.forEach((section) => {
|
|
if (!req.body[section]) return;
|
|
|
|
const newData = JSON.parse(req.body[section]);
|
|
if (JSON.stringify(newData) !== JSON.stringify(currentData[section])) {
|
|
updatedData[section] = newData;
|
|
hasChanges = true;
|
|
}
|
|
});
|
|
|
|
if (!hasChanges) {
|
|
req.flash("info_msg", "No changes were made");
|
|
return res.redirect("/admin/service");
|
|
}
|
|
|
|
if (currentData._id) {
|
|
await Service.findByIdAndUpdate(currentData._id, updatedData);
|
|
} else {
|
|
await Service.create(updatedData);
|
|
}
|
|
|
|
// Sync header menu children to reflect current service list
|
|
await syncServiceMenu(updatedData.services?.items || []);
|
|
|
|
req.flash("success_msg", "Service updated successfully");
|
|
res.redirect("/admin/service");
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", err.message);
|
|
res.redirect("/admin/service");
|
|
}
|
|
};
|
|
|
|
// Update service details
|
|
exports.updateDetails = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
const currentData = await getServiceData();
|
|
|
|
const serviceIndex = currentData.services?.items?.findIndex(
|
|
(item) => item.slug === slug,
|
|
);
|
|
if (serviceIndex === -1) {
|
|
req.flash("error_msg", "Service not found");
|
|
return res.redirect("/admin/service");
|
|
}
|
|
const beforeDetails = JSON.parse(
|
|
JSON.stringify(currentData.services.items[serviceIndex].details || {}),
|
|
);
|
|
// Parse features and FAQ from JSON strings
|
|
const features = req.body.features ? JSON.parse(req.body.features) : [];
|
|
const faq = req.body.faq ? JSON.parse(req.body.faq) : [];
|
|
|
|
// Update service details
|
|
const updatedData = { ...currentData.toObject?.() };
|
|
const updatedDetails = {
|
|
title: req.body.title,
|
|
description: req.body.description,
|
|
mainImage: req.body.mainImage,
|
|
overviewTitle: req.body.overviewTitle,
|
|
overviewDescription: req.body.overviewDescription,
|
|
additionalDescription: req.body.additionalDescription,
|
|
keyFeaturesTitle: req.body.keyFeaturesTitle,
|
|
keyFeaturesImage: req.body.keyFeaturesImage,
|
|
features,
|
|
faqTitle: req.body.faqTitle,
|
|
faqImage: req.body.faqImage,
|
|
faq,
|
|
};
|
|
|
|
updatedData.services.items[serviceIndex].details = updatedDetails;
|
|
if (currentData._id) {
|
|
await Service.findByIdAndUpdate(currentData._id, updatedData);
|
|
} else {
|
|
await Service.create(updatedData);
|
|
}
|
|
const changes = diffObject(beforeDetails, updatedDetails);
|
|
if (changes.length > 0) {
|
|
await writeAuditLog({
|
|
model: "Service",
|
|
documentId: currentData._id,
|
|
action: AUDIT_ACTIONS.UPDATE_SERVICE_DETAILS,
|
|
before: beforeDetails,
|
|
after: updatedDetails,
|
|
changes,
|
|
req,
|
|
});
|
|
}
|
|
|
|
req.flash("success_msg", "Service details updated successfully");
|
|
res.redirect(`/admin/service/${slug}/details`);
|
|
} catch (err) {
|
|
console.error(err);
|
|
req.flash("error_msg", err.message);
|
|
res.redirect("/admin/service");
|
|
}
|
|
};
|
|
|
|
// API endpoint
|
|
exports.api = async (req, res) => {
|
|
try {
|
|
const serviceData = await getServiceData();
|
|
const baseUrl =
|
|
process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`;
|
|
|
|
const processedData = addBaseUrlToImages(serviceData, baseUrl);
|
|
res.json(processedData);
|
|
} catch (err) {
|
|
res.status(500).json({ error: "Error loading service data" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get service details by slug - API endpoint
|
|
*/
|
|
exports.getServiceBySlug = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
|
|
const serviceDoc = await Service.findOne().lean();
|
|
|
|
if (!serviceDoc) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: "Service data not found",
|
|
});
|
|
}
|
|
|
|
// Find service by slug
|
|
const service = serviceDoc.services?.items?.find(
|
|
(item) => item.slug === slug,
|
|
);
|
|
|
|
if (!service) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: `Service with slug '${slug}' not found`,
|
|
});
|
|
}
|
|
|
|
const baseUrl =
|
|
process.env.BACKEND_URL ?? `${req.protocol}://${req.get("host")}`;
|
|
|
|
// Return service details in the expected format
|
|
const responseData = {
|
|
pageTitle: serviceDoc.pageTitle,
|
|
breadcrumb: {
|
|
...serviceDoc.breadcrumb,
|
|
title: "Service Details",
|
|
items: [
|
|
{ label: "Home", href: "/" },
|
|
{ label: "Services", href: "/services" },
|
|
{ label: service.name, href: `/services/${slug}` },
|
|
],
|
|
},
|
|
serviceDetails: {
|
|
content: service.details,
|
|
keyFeatures: {
|
|
title: service.details.keyFeaturesTitle || "Key Features",
|
|
sideImage: service.details.keyFeaturesImage || "img/default.jpg",
|
|
items: service.details.features || [],
|
|
},
|
|
faq: {
|
|
title: service.details.faqTitle || "Frequently Asked Questions",
|
|
sideImage: service.details.faqImage || "img/default.jpg",
|
|
items: service.details.faq || [],
|
|
},
|
|
},
|
|
};
|
|
|
|
const processedData = addBaseUrlToImages(responseData, baseUrl);
|
|
res.json(processedData);
|
|
} catch (error) {
|
|
console.error("Error fetching service by slug:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Internal server error",
|
|
error: error.message,
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate slug from text - API endpoint
|
|
*/
|
|
exports.generateSlug = async (req, res) => {
|
|
try {
|
|
const { text } = req.body;
|
|
|
|
if (!text || typeof text !== "string") {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: "Text is required",
|
|
});
|
|
}
|
|
|
|
// Generate slug using slugify library with Vietnamese support
|
|
const slug = slugify(text, {
|
|
lower: true,
|
|
strict: true,
|
|
locale: "vi",
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
slug: slug,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error generating slug:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Internal server error",
|
|
error: error.message,
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get all service slugs - API endpoint
|
|
*/
|
|
exports.getServiceSlugs = async (req, res) => {
|
|
try {
|
|
const serviceDoc = await Service.findOne().lean();
|
|
|
|
if (!serviceDoc?.services?.items) {
|
|
return res.json({
|
|
success: true,
|
|
slugs: [],
|
|
});
|
|
}
|
|
|
|
const slugs = serviceDoc.services.items.map((item) => ({
|
|
slug: item.slug,
|
|
name: item.name,
|
|
id: item.id,
|
|
}));
|
|
|
|
res.json({
|
|
success: true,
|
|
slugs,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching service slugs:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Internal server error",
|
|
error: error.message,
|
|
});
|
|
}
|
|
};
|