feat:Add blog management page and enhance admin layout

This commit is contained in:
Wini_Fy
2026-02-03 17:04:28 +07:00
parent bb539ef213
commit 538317eade
14 changed files with 3096 additions and 277 deletions

View File

@@ -1,4 +1,5 @@
const BlogCategory = require('../models/blogCategory');
const slugify = require('slugify');
// -------------------- Admin Controllers --------------------
@@ -49,12 +50,11 @@ exports.store = async (req, res) => {
} = req.body;
// Generate slug
const slug = name
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim('-');
const slug = slugify(name, {
lower: true,
strict: true,
locale: 'vi'
});
// Check if slug exists
const existingCategory = await BlogCategory.findOne({ slug });
@@ -130,12 +130,11 @@ exports.update = async (req, res) => {
category.isActive = isActive === 'on';
// Generate new slug if name changed
const newSlug = name
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim('-');
const newSlug = slugify(name, {
lower: true,
strict: true,
locale: 'vi'
});
if (newSlug !== category.slug) {
const existingCategory = await BlogCategory.findOne({
@@ -166,6 +165,14 @@ exports.destroy = async (req, res) => {
const category = await BlogCategory.findById(req.params.id);
if (!category) {
// Check if it's an AJAX request
const isAjax = req.xhr || req.headers['accept']?.includes('application/json') || req.headers['content-type']?.includes('application/json');
if (isAjax) {
return res.status(404).json({
success: false,
message: 'Category not found'
});
}
req.flash('error_msg', 'Category not found');
return res.redirect('/admin/blog/categories');
}
@@ -175,16 +182,44 @@ exports.destroy = async (req, res) => {
const postCount = await Blog.countDocuments({ category: { $in: [category.name] } });
if (postCount > 0) {
// Check if it's an AJAX request
const isAjax = req.xhr || req.headers['accept']?.includes('application/json') || req.headers['content-type']?.includes('application/json');
if (isAjax) {
return res.status(400).json({
success: false,
message: 'Cannot delete category that has blog posts'
});
}
req.flash('error_msg', 'Cannot delete category that has blog posts');
return res.redirect('/admin/blog/categories');
}
await BlogCategory.findByIdAndDelete(req.params.id);
// Check if it's an AJAX request
const isAjax = req.xhr || req.headers['accept']?.includes('application/json') || req.headers['content-type']?.includes('application/json');
if (isAjax) {
return res.json({
success: true,
message: 'Category deleted successfully'
});
}
req.flash('success_msg', 'Category deleted successfully');
res.redirect('/admin/blog/categories');
} catch (err) {
console.error('Category delete error:', err);
// Check if it's an AJAX request
const isAjax = req.xhr || req.headers['accept']?.includes('application/json') || req.headers['content-type']?.includes('application/json');
if (isAjax) {
return res.status(500).json({
success: false,
message: 'Error deleting category',
error: err.message || 'Error deleting category'
});
}
req.flash('error_msg', 'Error deleting category');
res.redirect('/admin/blog/categories');
}
@@ -202,10 +237,18 @@ exports.api = async (req, res) => {
await category.updatePostCount();
}
res.json(categories);
res.json({
success: true,
message: 'Categories fetched successfully',
data: categories
});
} catch (err) {
console.error('Categories API error:', err);
res.status(500).json({ error: 'Error loading categories' });
res.status(500).json({
success: false,
message: 'Error loading categories',
error: err.message || 'Error loading categories'
});
}
};
@@ -218,13 +261,81 @@ exports.apiShow = async (req, res) => {
}).lean();
if (!category) {
return res.status(404).json({ error: 'Category not found' });
return res.status(404).json({
success: false,
message: 'Category not found'
});
}
res.json(category);
res.json({
success: true,
message: 'Category fetched successfully',
data: category
});
} catch (err) {
console.error('Category show API error:', err);
res.status(500).json({ error: 'Error loading category' });
res.status(500).json({
success: false,
message: 'Error loading category',
error: err.message || 'Error loading category'
});
}
};
// Quick create category (for inline creation in blog form)
exports.quickCreate = async (req, res) => {
try {
const { name, description } = req.body;
if (!name || !name.trim()) {
return res.status(400).json({
success: false,
message: 'Category name is required'
});
}
const categoryName = name.trim();
// Generate slug
const slug = slugify(categoryName, {
lower: true,
strict: true,
locale: 'vi'
});
// Check if category already exists
let category = await BlogCategory.findOne({ slug });
if (category) {
return res.json({
success: true,
message: 'Category already exists',
data: category.toObject()
});
}
// Create new category
category = new BlogCategory({
name: categoryName,
slug,
description: description || '',
isActive: true
});
await category.save();
res.json({
success: true,
message: 'Category created successfully',
data: category.toObject()
});
} catch (err) {
console.error('Quick create category error:', err);
res.status(500).json({
success: false,
message: 'Error creating category',
error: err.message || 'Error creating category'
});
}
};