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
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;
|
|
|