const express = require('express'); const { Op } = require('sequelize'); const { Rental, Item, User } = require('../models'); // Import from models/index.js to get models with associations const { authenticateToken } = require('../middleware/auth'); const router = express.Router(); // Helper function to check and update review visibility const checkAndUpdateReviewVisibility = async (rental) => { const now = new Date(); const tenMinutesInMs = 10 * 60 * 1000; // 10 minutes let needsUpdate = false; let updates = {}; // Check if both reviews are submitted if (rental.itemReviewSubmittedAt && rental.renterReviewSubmittedAt) { if (!rental.itemReviewVisible || !rental.renterReviewVisible) { updates.itemReviewVisible = true; updates.renterReviewVisible = true; needsUpdate = true; } } else { // Check item review visibility (10-minute rule) if (rental.itemReviewSubmittedAt && !rental.itemReviewVisible) { const timeSinceSubmission = now - new Date(rental.itemReviewSubmittedAt); if (timeSinceSubmission >= tenMinutesInMs) { updates.itemReviewVisible = true; needsUpdate = true; } } // Check renter review visibility (10-minute rule) if (rental.renterReviewSubmittedAt && !rental.renterReviewVisible) { const timeSinceSubmission = now - new Date(rental.renterReviewSubmittedAt); if (timeSinceSubmission >= tenMinutesInMs) { updates.renterReviewVisible = true; needsUpdate = true; } } } if (needsUpdate) { await rental.update(updates); } return rental; }; router.get('/my-rentals', authenticateToken, async (req, res) => { try { const rentals = await Rental.findAll({ where: { renterId: req.user.id }, // Remove explicit attributes to let Sequelize handle missing columns gracefully include: [ { model: Item, as: 'item' }, { model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] } ], order: [['createdAt', 'DESC']] }); console.log('My-rentals data:', rentals.length > 0 ? rentals[0].toJSON() : 'No rentals'); res.json(rentals); } catch (error) { console.error('Error in my-rentals route:', error); res.status(500).json({ error: error.message }); } }); router.get('/my-listings', authenticateToken, async (req, res) => { try { const rentals = await Rental.findAll({ where: { ownerId: req.user.id }, // Remove explicit attributes to let Sequelize handle missing columns gracefully include: [ { model: Item, as: 'item' }, { model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] } ], order: [['createdAt', 'DESC']] }); console.log('My-listings rentals:', rentals.length > 0 ? rentals[0].toJSON() : 'No rentals'); res.json(rentals); } catch (error) { console.error('Error in my-listings route:', error); res.status(500).json({ error: error.message }); } }); router.post('/', authenticateToken, async (req, res) => { try { const { itemId, startDate, endDate, startTime, endTime, deliveryMethod, deliveryAddress, notes } = req.body; const item = await Item.findByPk(itemId); if (!item) { return res.status(404).json({ error: 'Item not found' }); } if (!item.availability) { return res.status(400).json({ error: 'Item is not available' }); } const overlappingRental = await Rental.findOne({ where: { itemId, status: { [Op.in]: ['confirmed', 'active'] }, [Op.or]: [ { startDate: { [Op.between]: [startDate, endDate] } }, { endDate: { [Op.between]: [startDate, endDate] } } ] } }); if (overlappingRental) { return res.status(400).json({ error: 'Item is already booked for these dates' }); } const rentalDays = Math.ceil((new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24)); const totalAmount = rentalDays * (item.pricePerDay || 0); const rental = await Rental.create({ itemId, renterId: req.user.id, ownerId: item.ownerId, startDate, endDate, startTime, endTime, totalAmount, deliveryMethod, deliveryAddress, notes }); const rentalWithDetails = await Rental.findByPk(rental.id, { include: [ { model: Item, as: 'item' }, { model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] } ] }); res.status(201).json(rentalWithDetails); } catch (error) { res.status(500).json({ error: error.message }); } }); router.put('/:id/status', authenticateToken, async (req, res) => { try { const { status } = req.body; const rental = await Rental.findByPk(req.params.id); if (!rental) { return res.status(404).json({ error: 'Rental not found' }); } if (rental.ownerId !== req.user.id && rental.renterId !== req.user.id) { return res.status(403).json({ error: 'Unauthorized' }); } await rental.update({ status }); const updatedRental = await Rental.findByPk(rental.id, { include: [ { model: Item, as: 'item' }, { model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] } ] }); res.json(updatedRental); } catch (error) { res.status(500).json({ error: error.message }); } }); // Owner reviews renter router.post('/:id/review-renter', authenticateToken, async (req, res) => { try { const { rating, review, privateMessage } = req.body; const rental = await Rental.findByPk(req.params.id); if (!rental) { return res.status(404).json({ error: 'Rental not found' }); } if (rental.ownerId !== req.user.id) { return res.status(403).json({ error: 'Only owners can review renters' }); } if (rental.status !== 'completed') { return res.status(400).json({ error: 'Can only review completed rentals' }); } if (rental.renterReviewSubmittedAt) { return res.status(400).json({ error: 'Renter review already submitted' }); } // Submit the review and private message await rental.update({ renterRating: rating, renterReview: review, renterReviewSubmittedAt: new Date(), renterPrivateMessage: privateMessage }); // Check and update visibility const updatedRental = await checkAndUpdateReviewVisibility(rental); res.json({ success: true, reviewVisible: updatedRental.renterReviewVisible }); } catch (error) { res.status(500).json({ error: error.message }); } }); // Renter reviews item router.post('/:id/review-item', authenticateToken, async (req, res) => { try { const { rating, review, privateMessage } = req.body; const rental = await Rental.findByPk(req.params.id); if (!rental) { return res.status(404).json({ error: 'Rental not found' }); } if (rental.renterId !== req.user.id) { return res.status(403).json({ error: 'Only renters can review items' }); } if (rental.status !== 'completed') { return res.status(400).json({ error: 'Can only review completed rentals' }); } if (rental.itemReviewSubmittedAt) { return res.status(400).json({ error: 'Item review already submitted' }); } // Submit the review and private message await rental.update({ itemRating: rating, itemReview: review, itemReviewSubmittedAt: new Date(), itemPrivateMessage: privateMessage }); // Check and update visibility const updatedRental = await checkAndUpdateReviewVisibility(rental); res.json({ success: true, reviewVisible: updatedRental.itemReviewVisible }); } catch (error) { res.status(500).json({ error: error.message }); } }); // Mark rental as completed (owner only) router.post('/:id/mark-completed', authenticateToken, async (req, res) => { try { console.log('Mark completed endpoint hit for rental ID:', req.params.id); const rental = await Rental.findByPk(req.params.id); if (!rental) { return res.status(404).json({ error: 'Rental not found' }); } if (rental.ownerId !== req.user.id) { return res.status(403).json({ error: 'Only owners can mark rentals as completed' }); } if (!['active', 'confirmed'].includes(rental.status)) { return res.status(400).json({ error: 'Can only mark active or confirmed rentals as completed' }); } await rental.update({ status: 'completed' }); const updatedRental = await Rental.findByPk(rental.id, { include: [ { model: Item, as: 'item' }, { model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] } ] }); res.json(updatedRental); } catch (error) { res.status(500).json({ error: error.message }); } }); // Legacy review endpoint (for backward compatibility) router.post('/:id/review', authenticateToken, async (req, res) => { try { const { rating, review } = req.body; const rental = await Rental.findByPk(req.params.id); if (!rental) { return res.status(404).json({ error: 'Rental not found' }); } if (rental.renterId !== req.user.id) { return res.status(403).json({ error: 'Only renters can leave reviews' }); } if (rental.status !== 'completed') { return res.status(400).json({ error: 'Can only review completed rentals' }); } await rental.update({ rating, review }); res.json(rental); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;