Updated search bar to remove location. Will get or ask for user's location. Removed Start Earning button. Works on desktop and mobile

This commit is contained in:
jackiettran
2025-12-23 18:09:12 -05:00
parent 07e5a2a320
commit 347f709f72
5 changed files with 531 additions and 214 deletions

View File

@@ -1,6 +1,6 @@
const express = require("express");
const { Op } = require("sequelize");
const { Item, User, Rental } = require("../models"); // Import from models/index.js to get models with associations
const { Op, Sequelize } = require("sequelize");
const { Item, User, Rental, sequelize } = require("../models"); // Import from models/index.js to get models with associations
const { authenticateToken, requireVerifiedEmail, requireAdmin, optionalAuth } = require("../middleware/auth");
const logger = require("../utils/logger");
const { validateS3Keys } = require("../utils/s3KeyValidator");
@@ -61,6 +61,9 @@ router.get("/", async (req, res, next) => {
city,
zipCode,
search,
lat,
lng,
radius = 25,
page = 1,
limit = 20,
} = req.query;
@@ -74,8 +77,50 @@ router.get("/", async (req, res, next) => {
if (minPrice) where.pricePerDay[Op.gte] = minPrice;
if (maxPrice) where.pricePerDay[Op.lte] = maxPrice;
}
if (city) where.city = { [Op.iLike]: `%${city}%` };
if (zipCode) where.zipCode = { [Op.iLike]: `%${zipCode}%` };
// Location filtering: Radius search OR city/ZIP fallback
if (lat && lng) {
// Parse and validate coordinates
const latNum = parseFloat(lat);
const lngNum = parseFloat(lng);
const radiusNum = parseFloat(radius);
if (!isNaN(latNum) && !isNaN(lngNum) && !isNaN(radiusNum)) {
// Bounding box pre-filter (fast, uses indexes)
// ~69 miles per degree latitude, longitude varies by latitude
const latDelta = radiusNum / 69;
const lngDelta = radiusNum / (69 * Math.cos(latNum * Math.PI / 180));
where.latitude = {
[Op.and]: [
{ [Op.gte]: latNum - latDelta },
{ [Op.lte]: latNum + latDelta },
{ [Op.ne]: null }
]
};
where.longitude = {
[Op.and]: [
{ [Op.gte]: lngNum - lngDelta },
{ [Op.lte]: lngNum + lngDelta },
{ [Op.ne]: null }
]
};
// Haversine formula for exact distance (applied after bounding box)
// 3959 = Earth's radius in miles
where[Op.and] = sequelize.literal(`
(3959 * acos(
cos(radians(${latNum})) * cos(radians("Item"."latitude")) *
cos(radians("Item"."longitude") - radians(${lngNum})) +
sin(radians(${latNum})) * sin(radians("Item"."latitude"))
)) <= ${radiusNum}
`);
}
} else {
// Fallback to city/ZIP string matching
if (city) where.city = { [Op.iLike]: `%${city}%` };
if (zipCode) where.zipCode = { [Op.iLike]: `%${zipCode}%` };
}
if (search) {
where[Op.or] = [
{ name: { [Op.iLike]: `%${search}%` } },
@@ -113,7 +158,7 @@ router.get("/", async (req, res, next) => {
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Items search completed", {
filters: { minPrice, maxPrice, city, zipCode, search },
filters: { minPrice, maxPrice, city, zipCode, search, lat, lng, radius },
resultsCount: count,
page: parseInt(page),
limit: parseInt(limit)