Spaces:
Running
Running
File size: 3,883 Bytes
1e480b5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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 });
},
];
|