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;