getting to payment screen. Bug fixes and formatting changes for item detail

This commit is contained in:
jackiettran
2025-08-21 16:44:05 -04:00
parent b624841350
commit 022c0b9c06
10 changed files with 859 additions and 815 deletions

View File

@@ -1,127 +1,123 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const { DataTypes } = require("sequelize");
const sequelize = require("../config/database");
const Item = sequelize.define('Item', {
const Item = sequelize.define("Item", {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: false
allowNull: false,
},
pickUpAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
defaultValue: false,
},
localDeliveryAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
defaultValue: false,
},
localDeliveryRadius: {
type: DataTypes.INTEGER,
validate: {
min: 1,
max: 100
}
max: 100,
},
},
shippingAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
defaultValue: false,
},
inPlaceUseAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
defaultValue: false,
},
pricePerHour: {
type: DataTypes.DECIMAL(10, 2)
type: DataTypes.DECIMAL(10, 2),
},
pricePerDay: {
type: DataTypes.DECIMAL(10, 2)
type: DataTypes.DECIMAL(10, 2),
},
replacementCost: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false
},
location: {
type: DataTypes.STRING,
allowNull: false
allowNull: false,
},
address1: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
address2: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
city: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
state: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
zipCode: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
country: {
type: DataTypes.STRING
type: DataTypes.STRING,
},
latitude: {
type: DataTypes.DECIMAL(10, 8)
type: DataTypes.DECIMAL(10, 8),
},
longitude: {
type: DataTypes.DECIMAL(11, 8)
type: DataTypes.DECIMAL(11, 8),
},
images: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: []
defaultValue: [],
},
availability: {
type: DataTypes.BOOLEAN,
defaultValue: true
defaultValue: true,
},
specifications: {
type: DataTypes.JSONB,
defaultValue: {}
defaultValue: {},
},
rules: {
type: DataTypes.TEXT
type: DataTypes.TEXT,
},
minimumRentalDays: {
type: DataTypes.INTEGER,
defaultValue: 1
defaultValue: 1,
},
maximumRentalDays: {
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
},
needsTraining: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
defaultValue: false,
},
unavailablePeriods: {
type: DataTypes.JSONB,
defaultValue: []
defaultValue: [],
},
availableAfter: {
type: DataTypes.STRING,
defaultValue: '09:00'
defaultValue: "09:00",
},
availableBefore: {
type: DataTypes.STRING,
defaultValue: '17:00'
defaultValue: "17:00",
},
specifyTimesPerDay: {
type: DataTypes.BOOLEAN,
defaultValue: false
defaultValue: false,
},
weeklyTimes: {
type: DataTypes.JSONB,
@@ -132,17 +128,17 @@ const Item = sequelize.define('Item', {
wednesday: { availableAfter: "09:00", availableBefore: "17:00" },
thursday: { availableAfter: "09:00", availableBefore: "17:00" },
friday: { availableAfter: "09:00", availableBefore: "17:00" },
saturday: { availableAfter: "09:00", availableBefore: "17:00" }
}
saturday: { availableAfter: "09:00", availableBefore: "17:00" },
},
},
ownerId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
}
model: "Users",
key: "id",
},
},
});
module.exports = Item;
module.exports = Item;

View File

@@ -9,7 +9,6 @@ router.get("/", async (req, res) => {
const {
minPrice,
maxPrice,
location,
city,
zipCode,
search,
@@ -24,7 +23,6 @@ router.get("/", async (req, res) => {
if (minPrice) where.pricePerDay[Op.gte] = minPrice;
if (maxPrice) where.pricePerDay[Op.lte] = maxPrice;
}
if (location) where.location = { [Op.iLike]: `%${location}%` };
if (city) where.city = { [Op.iLike]: `%${city}%` };
if (zipCode) where.zipCode = { [Op.iLike]: `%${zipCode}%` };
if (search) {
@@ -83,6 +81,42 @@ router.get("/recommendations", authenticateToken, async (req, res) => {
}
});
// Public endpoint to get reviews for a specific item (must come before /:id route)
router.get('/:id/reviews', async (req, res) => {
try {
const { Rental, User } = require('../models');
const reviews = await Rental.findAll({
where: {
itemId: req.params.id,
status: 'completed',
rating: { [Op.not]: null },
review: { [Op.not]: null }
},
include: [
{
model: User,
as: 'renter',
attributes: ['id', 'firstName', 'lastName']
}
],
order: [['createdAt', 'DESC']]
});
const averageRating = reviews.length > 0
? reviews.reduce((sum, review) => sum + review.rating, 0) / reviews.length
: 0;
res.json({
reviews,
averageRating,
totalReviews: reviews.length
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.get("/:id", async (req, res) => {
try {
const item = await Item.findByPk(req.params.id, {