forked from UKSOURCE/cms.hailearning.edu.vn
feat: Implement core admin panel functionalities including appointment, contact, and pricing management with associated models, controllers, views, and routes.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
const { addBaseUrlToImages } = require("../utils/imageHelper");
|
||||
const Contact = require("../models/contact");
|
||||
const ContactSubmission = require("../models/contactSubmission");
|
||||
|
||||
// Get contact data from MongoDB
|
||||
const getContactData = async () => {
|
||||
@@ -60,6 +61,7 @@ exports.index = async (req, res) => {
|
||||
zoom: 15,
|
||||
location: "",
|
||||
markerTitle: "",
|
||||
embedUrl: "",
|
||||
tileLayer: {
|
||||
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: "",
|
||||
@@ -70,16 +72,38 @@ exports.index = async (req, res) => {
|
||||
form: {
|
||||
sectionLabel: "",
|
||||
heading: "",
|
||||
description: "",
|
||||
fields: [],
|
||||
submitButton: { text: "Send Message" },
|
||||
submitButton: { text: "Send Message", icon: "fa-solid fa-arrow-right", buttonClass: "theme-btn style-2" },
|
||||
},
|
||||
};
|
||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||
|
||||
const { startDate, endDate } = req.query;
|
||||
const query = {};
|
||||
|
||||
if (startDate || endDate) {
|
||||
query.createdAt = {};
|
||||
if (startDate) {
|
||||
query.createdAt.$gte = new Date(startDate);
|
||||
}
|
||||
if (endDate) {
|
||||
// Set end date to end of day
|
||||
const end = new Date(endDate);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
query.createdAt.$lte = end;
|
||||
}
|
||||
}
|
||||
|
||||
const submissions = await ContactSubmission.find(query).sort({ createdAt: -1 }).limit(50);
|
||||
const frontendUrl = process.env.FRONTEND_URL;
|
||||
|
||||
res.render("admin/contact/index", {
|
||||
title: "Contact Management",
|
||||
layout: "layouts/main",
|
||||
data,
|
||||
submissions,
|
||||
startDate,
|
||||
endDate,
|
||||
frontendUrl,
|
||||
currentPath: req.path,
|
||||
user: req.session.user,
|
||||
@@ -140,6 +164,7 @@ exports.update = async (req, res) => {
|
||||
zoom: 15,
|
||||
location: "",
|
||||
markerTitle: "",
|
||||
embedUrl: "",
|
||||
tileLayer: {
|
||||
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: "",
|
||||
@@ -150,8 +175,9 @@ exports.update = async (req, res) => {
|
||||
form: formData || {
|
||||
sectionLabel: "",
|
||||
heading: "",
|
||||
description: "",
|
||||
fields: [],
|
||||
submitButton: { text: "Send Message" },
|
||||
submitButton: { text: "Send Message", icon: "fa-solid fa-arrow-right", buttonClass: "theme-btn style-2" },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -179,3 +205,141 @@ exports.update = async (req, res) => {
|
||||
res.redirect("/admin/contact");
|
||||
}
|
||||
};
|
||||
|
||||
// API để submit contact form (từ frontend)
|
||||
exports.submitForm = async (req, res) => {
|
||||
try {
|
||||
const { name, email, phone, address, date, message } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!name || !email) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: "Name and email are required",
|
||||
});
|
||||
}
|
||||
|
||||
// Create new submission
|
||||
const submission = new ContactSubmission({
|
||||
name: name.trim(),
|
||||
email: email.trim().toLowerCase(),
|
||||
phone: phone?.trim() || "",
|
||||
address: address?.trim() || "",
|
||||
date: date?.trim() || "",
|
||||
message: message?.trim() || "",
|
||||
ipAddress: req.ip || req.connection?.remoteAddress || "",
|
||||
userAgent: req.get("User-Agent") || "",
|
||||
});
|
||||
|
||||
await submission.save();
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: "Thank you for contacting us! We will get back to you soon.",
|
||||
data: {
|
||||
id: submission._id,
|
||||
name: submission.name,
|
||||
email: submission.email,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error submitting contact form:", err);
|
||||
|
||||
// Handle validation errors
|
||||
if (err.name === "ValidationError") {
|
||||
const errors = Object.values(err.errors).map((e) => e.message);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: errors.join(", "),
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "Error submitting form. Please try again later.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// API để lấy danh sách submissions (cho admin)
|
||||
exports.getSubmissions = async (req, res) => {
|
||||
try {
|
||||
const { status, page = 1, limit = 20 } = req.query;
|
||||
|
||||
const query = {};
|
||||
if (status && ["pending", "read", "replied", "archived"].includes(status)) {
|
||||
query.status = status;
|
||||
}
|
||||
|
||||
const skip = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
const [submissions, total] = await Promise.all([
|
||||
ContactSubmission.find(query)
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(parseInt(limit)),
|
||||
ContactSubmission.countDocuments(query),
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: submissions,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total,
|
||||
totalPages: Math.ceil(total / parseInt(limit)),
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error getting submissions:", err);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "Error loading submissions",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// API để cập nhật status của submission
|
||||
exports.updateSubmissionStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, notes } = req.body;
|
||||
|
||||
const validStatuses = ["pending", "read", "replied", "archived"];
|
||||
if (!validStatuses.includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: "Invalid status",
|
||||
});
|
||||
}
|
||||
|
||||
const updateData = { status };
|
||||
if (notes !== undefined) updateData.notes = notes;
|
||||
if (status === "replied") updateData.repliedAt = new Date();
|
||||
|
||||
const submission = await ContactSubmission.findByIdAndUpdate(
|
||||
id,
|
||||
updateData,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!submission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: "Submission not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: submission,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error updating submission:", err);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "Error updating submission",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user