Files
Challenge_15_Magazijn_App_M…/routes/reservations.js

219 lines
8.0 KiB
JavaScript

const express = require('express');
const { auth, adminOnly } = require('../middleware/auth');
const Reservation = require('../models/Reservation');
const Item = require('../models/Item');
const router = express.Router();
// Get all reservations (admin only)
router.get('/', auth, adminOnly, async (req, res) => {
try {
const reservations = await Reservation.find({ status: { $ne: 'ARCHIVED' } })
.populate('userId', 'username')
.populate('itemId')
.exec();
if (!reservations) {
return res.json([]); // Return empty array if no reservations
}
const formattedReservations = reservations
.filter(reservation => reservation.userId && reservation.itemId) // Filter out any invalid references
.map(reservation => ({
_id: reservation._id,
studentName: reservation.userId.username,
itemName: reservation.itemId.name,
location: reservation.itemId.location,
status: reservation.status,
quantity: reservation.quantity || 1,
reservedDate: reservation.reservedDate
}));
res.json(formattedReservations);
} catch (error) {
console.error('Error fetching reservations:', error);
res.status(500).json({
message: 'Failed to load reservations',
error: error.message
});
}
});
// Get user's reservations
router.get('/my', auth, async (req, res) => {
try {
const reservations = await Reservation.find({
userId: req.user._id,
status: { $ne: 'ARCHIVED' }
}).populate('itemId');
// Filter out reservations with deleted items and format the response
const formattedReservations = reservations
.filter(reservation => reservation.itemId) // Only include reservations with valid items
.map(reservation => ({
_id: reservation._id,
itemName: reservation.itemId.name,
location: reservation.itemId.location,
status: reservation.status,
quantity: reservation.quantity || 1,
reservedDate: reservation.reservedDate
}));
res.json(formattedReservations);
} catch (error) {
console.error('Error fetching user reservations:', error);
res.status(500).json({
message: 'Failed to load reservations',
error: error.message
});
}
});
// Create reservation
router.post('/', auth, async (req, res) => {
try {
const item = await Item.findById(req.body.itemId);
if (!item) {
return res.status(404).json({ message: 'Item not found' });
}
const requestedQuantity = parseInt(req.body.quantity) || 1;
if (item.quantity < item.reserved + requestedQuantity) {
return res.status(400).json({ message: 'Requested quantity is not available' });
}
const reservation = new Reservation({
itemId: req.body.itemId,
userId: req.user._id,
quantity: requestedQuantity
});
await reservation.save();
item.reserved += requestedQuantity;
await item.save();
res.status(201).json(reservation);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// Update reservation status (admin can update any, students can only request returns)
router.patch('/:id', auth, async (req, res) => {
try {
const reservation = await Reservation.findById(req.params.id).populate('userId');
if (!reservation) {
return res.status(404).json({ message: 'Reservation not found' });
}
// Check authorization
const isAdmin = req.user.role === 'admin';
const isOwner = reservation.userId._id.toString() === req.user._id.toString();
const isRequestingReturn = req.body.status === 'RETURN_PENDING';
// Students can only request returns on their own approved reservations
if (!isAdmin) {
if (!isOwner) {
return res.status(403).json({
message: 'Not authorized. Students can only request return of their own items.'
});
}
if (!isRequestingReturn) {
return res.status(403).json({
message: 'Students can only request returns, not change other statuses.'
});
}
if (reservation.status !== 'APPROVED') {
return res.status(400).json({
message: 'Can only request return for approved items'
});
}
}
const item = await Item.findById(reservation.itemId);
if (!item) {
return res.status(404).json({ message: 'Item not found' });
}
const oldStatus = reservation.status;
const newStatus = req.body.status;
reservation.status = newStatus;
// Include quantity in the update if provided
if (req.body.quantity !== undefined) {
reservation.quantity = req.body.quantity;
}
await reservation.save();
// Update item reserved count based on status change
if (oldStatus === 'PENDING' && newStatus === 'REJECTED') {
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
await item.save();
} else if (oldStatus === 'RETURN_PENDING' && newStatus === 'RETURNED') {
// Admin approved the return request
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
await item.save();
} else if (oldStatus === 'APPROVED' && newStatus === 'RETURNED') {
// Admin directly marked approved item as returned
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
await item.save();
}
res.json(reservation);
} catch (error) {
console.error('Error updating reservation:', error);
res.status(400).json({ message: error.message });
}
});
// Delete reservation (admin can delete any, students can only delete their own)
router.delete('/:id', auth, async (req, res) => {
try {
const reservation = await Reservation.findById(req.params.id).populate('userId');
if (!reservation) {
return res.status(404).json({ message: 'Reservation not found' });
}
// Check if user is authorized to delete this reservation
if (req.user.role !== 'admin' && reservation.userId._id.toString() !== req.user._id.toString()) {
return res.status(403).json({ message: 'Not authorized to delete this reservation' });
}
// Update item's reserved count
const item = await Item.findById(reservation.itemId);
if (item) {
item.reserved = Math.max(0, item.reserved - 1); // Ensure it doesn't go below 0
await item.save();
}
await Reservation.findByIdAndDelete(req.params.id);
res.json({ message: 'Reservation deleted successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Archive reservation (admin only)
router.patch('/:id/archive', auth, adminOnly, async (req, res) => {
try {
const reservation = await Reservation.findById(req.params.id);
if (!reservation) {
return res.status(404).json({ message: 'Reservation not found' });
}
// Only allow archiving of returned reservations
if (reservation.status !== 'RETURNED') {
return res.status(400).json({ message: 'Can only archive returned reservations' });
}
reservation.status = 'ARCHIVED';
await reservation.save();
res.json({ message: 'Reservation archived successfully' });
} catch (error) {
console.error('Error archiving reservation:', error);
res.status(500).json({ message: error.message });
}
});
module.exports = router;