const Category = require('../models/Category'); const MenuItem = require('../models/MenuItem'); const { uploadMenuImage } = require('../config/cloudinary'); // GET /api/menu/all — returns categories + their items (used for offline cache seeding) exports.getAll = async (req, res) => { const [categories, items] = await Promise.all([ Category.find({ hidden: false }).sort({ order: 1, name: 1 }), MenuItem.find().populate('category', 'name order').sort({ name: 1 }), ]); res.json({ categories, items }); }; // ── Categories ────────────────────────────────────────────── exports.getCategories = async (req, res) => { const cats = await Category.find().sort({ order: 1, name: 1 }); res.json(cats); }; exports.createCategory = async (req, res) => { const { name, order, hidden } = req.body; const cat = await Category.create({ name, order: order ?? 0, hidden: hidden ?? false }); res.status(201).json(cat); }; exports.updateCategory = async (req, res) => { const cat = await Category.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if (!cat) return res.status(404).json({ message: 'Category not found' }); res.json(cat); }; exports.deleteCategory = async (req, res) => { const count = await MenuItem.countDocuments({ category: req.params.id }); if (count > 0) return res.status(400).json({ message: `Cannot delete: ${count} menu item(s) use this category` }); await Category.findByIdAndDelete(req.params.id); res.json({ message: 'Category deleted' }); }; // ── Menu Items ─────────────────────────────────────────────── exports.createItem = async (req, res) => { const { name, category, variants, available } = req.body; const parsedVariants = typeof variants === 'string' ? JSON.parse(variants) : variants; const image = req.file ? req.file.path : null; // Cloudinary URL const item = await MenuItem.create({ name, category, variants: parsedVariants, available: available ?? true, image }); await item.populate('category', 'name order'); res.status(201).json(item); }; exports.updateItem = async (req, res) => { const { variants, ...rest } = req.body; const update = { ...rest }; if (variants) update.variants = typeof variants === 'string' ? JSON.parse(variants) : variants; if (req.file) update.image = req.file.path; const item = await MenuItem.findByIdAndUpdate(req.params.id, update, { new: true, runValidators: true }).populate('category', 'name order'); if (!item) return res.status(404).json({ message: 'Item not found' }); res.json(item); }; exports.deleteItem = async (req, res) => { const item = await MenuItem.findByIdAndDelete(req.params.id); if (!item) return res.status(404).json({ message: 'Item not found' }); res.json({ message: 'Item deleted' }); }; exports.toggleAvailability = async (req, res) => { const { available, variantId } = req.body; const item = await MenuItem.findById(req.params.id); if (!item) return res.status(404).json({ message: 'Item not found' }); if (variantId) { // Toggle a specific variant const variant = item.variants.id(variantId); if (!variant) return res.status(404).json({ message: 'Variant not found' }); variant.available = available ?? !variant.available; } else { // Toggle the whole item item.available = available ?? !item.available; } await item.save(); res.json(item); }; // POST /api/menu/upload-image — standalone image upload (returns URL for use in create/update) exports.uploadImage = [ uploadMenuImage.single('image'), (req, res) => { if (!req.file) return res.status(400).json({ message: 'No image uploaded' }); res.json({ url: req.file.path }); }, ];