feat: Implement core admin panel functionalities including appointment, contact, and pricing management with associated models, controllers, views, and routes.

This commit is contained in:
LNHA
2026-02-03 14:58:00 +07:00
parent d1b931d547
commit df8e1f9665
25 changed files with 4574 additions and 659 deletions

View File

@@ -13,7 +13,7 @@ const uploadController = {
// Lấy loại ảnh từ query params
const imageType = req.query.imageType || 'general';
// Tạo đường dẫn tương đối để lưu vào database
const relativePath = `/uploads/${imageType}/${req.file.filename}`;
const baseUrl = process.env.BACKEND_URL || `${req.protocol}://${req.get('host')}`;
@@ -41,7 +41,7 @@ const uploadController = {
if (camp) {
// Đồng bộ camps.image và camps.camp-detail.hero.bgImage - 2 trường này luôn giống nhau
camp.image = relativePath;
// Đảm bảo camp-detail.hero tồn tại và sync bgImage
if (!camp['camp-detail']) camp['camp-detail'] = {};
if (!camp['camp-detail'].hero) camp['camp-detail'].hero = {};
@@ -73,59 +73,59 @@ const uploadController = {
updateImagePath: async (req, res) => {
try {
const { jsonFile, jsonPath, newImagePath } = req.body;
if (!jsonFile || !jsonPath || !newImagePath) {
return res.status(400).json({
success: false,
message: 'Missing required information (jsonFile, jsonPath, newImagePath)'
return res.status(400).json({
success: false,
message: 'Missing required information (jsonFile, jsonPath, newImagePath)'
});
}
// Đọc file JSON
const jsonFilePath = path.join(__dirname, '../data', jsonFile);
const jsonData = jsonHelper.readJsonFile(jsonFilePath);
// Cập nhật đường dẫn ảnh theo jsonPath
// jsonPath có định dạng như "banner.image" hoặc "partners[0].logo"
const pathParts = jsonPath.split('.');
let current = jsonData;
// Duyệt qua các phần của path trừ phần cuối
for (let i = 0; i < pathParts.length - 1; i++) {
const part = pathParts[i];
// Kiểm tra nếu là mảng (ví dụ: partners[0])
if (part.includes('[') && part.includes(']')) {
const arrName = part.substring(0, part.indexOf('['));
const index = parseInt(part.substring(part.indexOf('[') + 1, part.indexOf(']')));
if (!current[arrName] || !Array.isArray(current[arrName])) {
return res.status(400).json({
success: false,
message: `Array ${arrName} not found in data`
return res.status(400).json({
success: false,
message: `Array ${arrName} not found in data`
});
}
current = current[arrName][index];
} else {
if (!current[part]) {
return res.status(400).json({
success: false,
message: `Property ${part} not found in data`
return res.status(400).json({
success: false,
message: `Property ${part} not found in data`
});
}
current = current[part];
}
}
// Cập nhật giá trị
const lastPart = pathParts[pathParts.length - 1];
current[lastPart] = newImagePath;
// Lưu lại file JSON
jsonHelper.writeJsonFile(jsonFilePath, jsonData);
return res.status(200).json({
success: true,
message: 'Image path updated successfully',
@@ -141,22 +141,22 @@ const uploadController = {
deleteImage: async (req, res) => {
try {
const { imagePath } = req.body;
if (!imagePath) {
return res.status(400).json({ success: false, message: 'Missing image path to delete' });
}
// Chuyển đổi đường dẫn tương đối thành đường dẫn tuyệt đối
const fullPath = path.join(__dirname, '../public', imagePath);
// Kiểm tra xem file có tồn tại không
if (!fs.existsSync(fullPath)) {
return res.status(404).json({ success: false, message: 'Image file not found' });
}
// Xóa file
fs.unlinkSync(fullPath);
return res.status(200).json({
success: true,
message: 'Image deleted successfully',