face / src /controllers /menuController.js
aliroohan179's picture
feat: implement core backend POS API with authentication, role-based access control, menu management, and order processing logic.
1e480b5
Raw History Blame Contribute Delete
3.88 kB
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 });
},
];