diff --git a/backend/routes/rentals.js b/backend/routes/rentals.js index b3886b0..7fed6aa 100644 --- a/backend/routes/rentals.js +++ b/backend/routes/rentals.js @@ -273,37 +273,20 @@ router.post("/", authenticateToken, requireVerifiedEmail, async (req, res) => { // Check for overlapping rentals using datetime ranges // Note: "active" rentals are stored as "confirmed" with startDateTime in the past + // Two ranges [A,B] and [C,D] overlap if and only if A < D AND C < B + // Here: existing rental [existingStart, existingEnd], new rental [rentalStartDateTime, rentalEndDateTime] + // Overlap: existingStart < rentalEndDateTime AND rentalStartDateTime < existingEnd const overlappingRental = await Rental.findOne({ where: { itemId, status: "confirmed", - [Op.or]: [ - { - [Op.and]: [ - { startDateTime: { [Op.not]: null } }, - { endDateTime: { [Op.not]: null } }, - { - [Op.or]: [ - { - startDateTime: { - [Op.between]: [rentalStartDateTime, rentalEndDateTime], - }, - }, - { - endDateTime: { - [Op.between]: [rentalStartDateTime, rentalEndDateTime], - }, - }, - { - [Op.and]: [ - { startDateTime: { [Op.lte]: rentalStartDateTime } }, - { endDateTime: { [Op.gte]: rentalEndDateTime } }, - ], - }, - ], - }, - ], - }, + startDateTime: { [Op.not]: null }, + endDateTime: { [Op.not]: null }, + [Op.and]: [ + // existingStart < newEnd (existing rental starts before new one ends) + { startDateTime: { [Op.lt]: rentalEndDateTime } }, + // existingEnd > newStart (existing rental ends after new one starts) + { endDateTime: { [Op.gt]: rentalStartDateTime } }, ], }, }); @@ -1087,6 +1070,27 @@ router.post("/cost-preview", authenticateToken, async (req, res) => { }); } + // Check for overlapping rentals (same logic as in POST /rentals) + // Two ranges overlap if: existingStart < newEnd AND existingEnd > newStart + const overlappingRental = await Rental.findOne({ + where: { + itemId, + status: "confirmed", + startDateTime: { [Op.not]: null }, + endDateTime: { [Op.not]: null }, + [Op.and]: [ + { startDateTime: { [Op.lt]: rentalEndDateTime } }, + { endDateTime: { [Op.gt]: rentalStartDateTime } }, + ], + }, + }); + + if (overlappingRental) { + return res + .status(400) + .json({ error: "Item is already booked for these dates" }); + } + // Calculate rental cost using duration calculator const totalAmount = RentalDurationCalculator.calculateRentalCost( rentalStartDateTime, diff --git a/frontend/src/pages/RentItem.tsx b/frontend/src/pages/RentItem.tsx index 14d50f1..49517f2 100644 --- a/frontend/src/pages/RentItem.tsx +++ b/frontend/src/pages/RentItem.tsx @@ -420,6 +420,20 @@ const RentItem: React.FC = () => { {dateValidationError} + ) : costError ? ( +
+ + {costError} +
+ +
+
) : !rentalDates.startDateTime || !rentalDates.endDateTime || !getRentalData() ? (