You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

478 lines
15 KiB

const { Menu, MenuCategory } = require('../models/associations');
const { Op } = require('sequelize');
const menuController = {
// Get all menus with pagination and filters
getAllMenus: async (req, res) => {
try {
const {
page = 1,
limit = 10,
categorie_id,
disponible,
prix_min,
prix_max,
search,
sort_by = 'nom',
sort_order = 'ASC'
} = req.query;
const offset = (parseInt(page) - 1) * parseInt(limit);
const whereClause = {};
// Apply filters
if (categorie_id) {
whereClause.categorie_id = categorie_id;
}
if (disponible !== undefined) {
whereClause.disponible = disponible === 'true';
}
if (prix_min || prix_max) {
whereClause.prix = {};
if (prix_min) whereClause.prix[Op.gte] = parseFloat(prix_min);
if (prix_max) whereClause.prix[Op.lte] = parseFloat(prix_max);
}
if (search) {
whereClause[Op.or] = [
{ nom: { [Op.like]: `%${search}%` } },
{ commentaire: { [Op.like]: `%${search}%` } },
{ ingredients: { [Op.like]: `%${search}%` } }
];
}
const { count, rows } = await Menu.findAndCountAll({
where: whereClause,
include: [{
model: MenuCategory,
as: 'category',
attributes: ['id', 'nom', 'description']
}],
limit: parseInt(limit),
offset: offset,
order: [[sort_by, sort_order.toUpperCase()]],
distinct: true
});
const totalPages = Math.ceil(count / parseInt(limit));
res.json({
success: true,
data: {
menus: rows,
pagination: {
currentPage: parseInt(page),
totalPages,
totalItems: count,
itemsPerPage: parseInt(limit)
}
}
});
} catch (error) {
console.error('Error fetching menus:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la récupération des menus',
error: error.message
});
}
},
// Get menu by ID
getMenuById: async (req, res) => {
try {
const { id } = req.params;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
message: 'ID de menu invalide'
});
}
const menu = await Menu.findByPk(id, {
include: [{
model: MenuCategory,
as: 'category',
attributes: ['id', 'nom', 'description']
}]
});
if (!menu) {
return res.status(404).json({
success: false,
message: 'Menu non trouvé'
});
}
res.json({
success: true,
data: menu
});
} catch (error) {
console.error('Error fetching menu:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la récupération du menu',
error: error.message
});
}
},
// Get menus by category
getMenusByCategory: async (req, res) => {
try {
const { categoryId } = req.params;
const { disponible_only = 'false' } = req.query;
const whereClause = { categorie_id: categoryId };
if (disponible_only === 'true') {
whereClause.disponible = true;
}
const menus = await Menu.findAll({
where: whereClause,
include: [{
model: MenuCategory,
as: 'category',
attributes: ['id', 'nom', 'description']
}],
order: [['nom', 'ASC']]
});
res.json({
success: true,
data: menus,
count: menus.length
});
} catch (error) {
console.error('Error fetching menus by category:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la récupération des menus par catégorie',
error: error.message
});
}
},
// Create new menu
createMenu: async (req, res) => {
try {
const {
nom,
commentaire,
prix,
categorie_id,
disponible = true,
image_url,
ingredients,
allergenes,
calories,
temps_preparation
} = req.body;
// Validate required fields
if (!nom || !prix || !categorie_id) {
return res.status(400).json({
success: false,
message: 'Les champs nom, prix et categorie_id sont requis'
});
}
// Check if category exists
const category = await MenuCategory.findByPk(categorie_id);
if (!category) {
return res.status(400).json({
success: false,
message: 'Catégorie non trouvée'
});
}
const newMenu = await Menu.create({
nom,
commentaire,
prix,
categorie_id,
disponible,
image_url,
ingredients,
allergenes,
calories,
temps_preparation
});
// Fetch the created menu with category info
const menuWithCategory = await Menu.findByPk(newMenu.id, {
include: [{
model: MenuCategory,
as: 'category',
attributes: ['id', 'nom', 'description']
}]
});
res.status(201).json({
success: true,
message: 'Menu créé avec succès',
data: menuWithCategory
});
} catch (error) {
console.error('Error creating menu:', error);
if (error.name === 'SequelizeValidationError') {
return res.status(400).json({
success: false,
message: 'Erreur de validation',
errors: error.errors.map(err => ({
field: err.path,
message: err.message
}))
});
}
res.status(500).json({
success: false,
message: 'Erreur lors de la création du menu',
error: error.message
});
}
},
// Update menu
updateMenu: async (req, res) => {
try {
const { id } = req.params;
const {
nom,
commentaire,
prix,
categorie_id,
disponible,
image_url,
ingredients,
allergenes,
calories,
temps_preparation
} = req.body;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
message: 'ID de menu invalide'
});
}
const menu = await Menu.findByPk(id);
if (!menu) {
return res.status(404).json({
success: false,
message: 'Menu non trouvé'
});
}
// Check if new category exists
if (categorie_id && categorie_id !== menu.categorie_id) {
const category = await MenuCategory.findByPk(categorie_id);
if (!category) {
return res.status(400).json({
success: false,
message: 'Catégorie non trouvée'
});
}
}
await menu.update({
nom: nom !== undefined ? nom : menu.nom,
commentaire: commentaire !== undefined ? commentaire : menu.commentaire,
prix: prix !== undefined ? prix : menu.prix,
categorie_id: categorie_id !== undefined ? categorie_id : menu.categorie_id,
disponible: disponible !== undefined ? disponible : menu.disponible,
image_url: image_url !== undefined ? image_url : menu.image_url,
ingredients: ingredients !== undefined ? ingredients : menu.ingredients,
allergenes: allergenes !== undefined ? allergenes : menu.allergenes,
calories: calories !== undefined ? calories : menu.calories,
temps_preparation: temps_preparation !== undefined ? temps_preparation : menu.temps_preparation
});
// Fetch updated menu with category
const updatedMenu = await Menu.findByPk(id, {
include: [{
model: MenuCategory,
as: 'category',
attributes: ['id', 'nom', 'description']
}]
});
res.json({
success: true,
message: 'Menu mis à jour avec succès',
data: updatedMenu
});
} catch (error) {
console.error('Error updating menu:', error);
if (error.name === 'SequelizeValidationError') {
return res.status(400).json({
success: false,
message: 'Erreur de validation',
errors: error.errors.map(err => ({
field: err.path,
message: err.message
}))
});
}
res.status(500).json({
success: false,
message: 'Erreur lors de la mise à jour du menu',
error: error.message
});
}
},
// Update menu availability
updateMenuAvailability: async (req, res) => {
try {
const { id } = req.params;
const { disponible } = req.body;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
message: 'ID de menu invalide'
});
}
if (typeof disponible !== 'boolean') {
return res.status(400).json({
success: false,
message: 'Le champ disponible doit être un booléen'
});
}
const menu = await Menu.findByPk(id);
if (!menu) {
return res.status(404).json({
success: false,
message: 'Menu non trouvé'
});
}
await menu.update({ disponible });
res.json({
success: true,
message: `Menu ${disponible ? 'activé' : 'désactivé'} avec succès`,
data: { id: menu.id, disponible }
});
} catch (error) {
console.error('Error updating menu availability:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la mise à jour de la disponibilité',
error: error.message
});
}
},
// Delete menu
deleteMenu: async (req, res) => {
try {
const { id } = req.params;
if (!id || isNaN(id)) {
return res.status(400).json({
success: false,
message: 'ID de menu invalide'
});
}
const menu = await Menu.findByPk(id);
if (!menu) {
return res.status(404).json({
success: false,
message: 'Menu non trouvé'
});
}
await menu.destroy();
res.json({
success: true,
message: 'Menu supprimé avec succès'
});
} catch (error) {
console.error('Error deleting menu:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la suppression du menu',
error: error.message
});
}
},
// Get menu statistics
getMenuStats: async (req, res) => {
try {
const totalMenus = await Menu.count();
const availableMenus = await Menu.count({ where: { disponible: true } });
const unavailableMenus = await Menu.count({ where: { disponible: false } });
const menusByCategory = await Menu.findAll({
attributes: [
'categorie_id',
[Menu.sequelize.fn('COUNT', Menu.sequelize.col('id')), 'count']
],
include: [{
model: MenuCategory,
as: 'category',
attributes: ['nom']
}],
group: ['categorie_id', 'category.id'],
raw: false
});
const priceStats = await Menu.findOne({
attributes: [
[Menu.sequelize.fn('MIN', Menu.sequelize.col('prix')), 'min_prix'],
[Menu.sequelize.fn('MAX', Menu.sequelize.col('prix')), 'max_prix'],
[Menu.sequelize.fn('AVG', Menu.sequelize.col('prix')), 'avg_prix']
],
raw: true
});
res.json({
success: true,
data: {
total: totalMenus,
disponible: availableMenus,
non_disponible: unavailableMenus,
by_category: menusByCategory,
price_stats: {
min_price: parseFloat(priceStats.min_prix) || 0,
max_price: parseFloat(priceStats.max_prix) || 0,
avg_price: parseFloat(priceStats.avg_prix) || 0
}
}
});
} catch (error) {
console.error('Error fetching menu statistics:', error);
res.status(500).json({
success: false,
message: 'Erreur lors de la récupération des statistiques',
error: error.message
});
}
}
};
module.exports = menuController;