diff --git a/backend/utils/rentalDurationCalculator.js b/backend/utils/rentalDurationCalculator.js index 5136a0e..67113bf 100644 --- a/backend/utils/rentalDurationCalculator.js +++ b/backend/utils/rentalDurationCalculator.js @@ -33,8 +33,8 @@ class RentalDurationCalculator { // Calculate base amount based on duration (tiered pricing) let totalAmount; - if (item.pricePerHour && diffHours <= 24) { - // Use hourly rate for rentals <= 24 hours + if (item.pricePerHour && diffHours < 24) { + // Use hourly rate for rentals under 24 hours totalAmount = diffHours * Number(item.pricePerHour); } else if (diffDays <= 7 && item.pricePerDay) { // Use daily rate for rentals <= 7 days diff --git a/frontend/src/App.css b/frontend/src/App.css index 0703e76..b6c94d7 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -54,4 +54,61 @@ main { .navbar .container-fluid { justify-content: space-between; } +} + +/* Mobile Sticky Bottom Bar for Item Detail */ +.mobile-sticky-bottom-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: white; + padding: 12px 16px; + box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); + z-index: 1000; + border-top: 1px solid #e0e0e0; +} + +/* Mobile-specific styles */ +@media (max-width: 767.98px) { + /* Make sticky card non-sticky on mobile */ + .sticky-pricing-card { + position: static !important; + margin-bottom: 80px; + } +} + +/* Pricing card input sizing - applies to all screen sizes */ +.sticky-pricing-card .form-label { + font-size: 1rem; +} + +.sticky-pricing-card .input-group-lg .form-control, +.sticky-pricing-card .input-group-lg .form-select { + font-size: 1rem; + padding: 12px 16px; + min-height: 48px; +} + +/* Style the date input specifically */ +.sticky-pricing-card input[type="date"] { + font-size: 1rem; +} + +/* Make the calendar icon larger on webkit browsers */ +.sticky-pricing-card input[type="date"]::-webkit-calendar-picker-indicator { + width: 20px; + height: 20px; + cursor: pointer; +} + +/* Time select dropdown */ +.sticky-pricing-card .time-select { + font-size: 1rem; +} + +/* Style options in the time dropdown */ +.sticky-pricing-card .time-select option { + font-size: 1rem; + padding: 8px; } \ No newline at end of file diff --git a/frontend/src/components/DeliveryOptions.tsx b/frontend/src/components/DeliveryOptions.tsx deleted file mode 100644 index 4eb877c..0000000 --- a/frontend/src/components/DeliveryOptions.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; - -interface DeliveryOptionsProps { - pickUpAvailable: boolean; - inPlaceUseAvailable: boolean; - onChange: (e: React.ChangeEvent) => void; -} - -const DeliveryOptions: React.FC = ({ - pickUpAvailable, - inPlaceUseAvailable, - onChange -}) => { - return ( -
-
- -
- - -
-
- - -
-
-
- ); -}; - -export default DeliveryOptions; \ No newline at end of file diff --git a/frontend/src/components/LocationPromptModal.tsx b/frontend/src/components/LocationPromptModal.tsx index 142d2bd..8820d33 100644 --- a/frontend/src/components/LocationPromptModal.tsx +++ b/frontend/src/components/LocationPromptModal.tsx @@ -1,11 +1,10 @@ import React, { useState } from "react"; +import { mapsAPI } from "../services/api"; interface LocationPromptModalProps { show: boolean; onClose: () => void; - onLocationSelect: ( - location: { lat: number; lng: number } | { city?: string; zipCode?: string } - ) => void; + onLocationSelect: (location: { lat: number; lng: number }) => void; } const LocationPromptModal: React.FC = ({ @@ -53,15 +52,31 @@ const LocationPromptModal: React.FC = ({ } }; - const handleManualSubmit = () => { + const handleManualSubmit = async () => { const trimmed = manualLocation.trim(); if (!trimmed) return; - // Check if it looks like a ZIP code - if (/^\d{5}(-\d{4})?$/.test(trimmed)) { - onLocationSelect({ zipCode: trimmed }); - } else { - onLocationSelect({ city: trimmed }); + setLoading(true); + setError(null); + + try { + // Geocode the input (works for both ZIP codes and city names) + const response = await mapsAPI.geocode({ + address: trimmed, + componentRestrictions: { country: "US" }, + }); + + const { latitude, longitude } = response.data; + + if (latitude && longitude) { + onLocationSelect({ lat: latitude, lng: longitude }); + } else { + setError("Could not find that location. Please try a different city or ZIP code."); + } + } catch (err: any) { + setError("Could not find that location. Please try a different city or ZIP code."); + } finally { + setLoading(false); } }; diff --git a/frontend/src/index.css b/frontend/src/index.css index ec2585e..b6bd06d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,3 +1,7 @@ +html { + font-size: 20px; +} + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 6bdaad0..9142240 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -48,7 +48,6 @@ export const mockRental = { totalAmount: 25, status: 'pending' as const, paymentStatus: 'pending' as const, - deliveryMethod: 'pickup' as const, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; diff --git a/frontend/src/pages/CreateItem.tsx b/frontend/src/pages/CreateItem.tsx index 402ca36..5971285 100644 --- a/frontend/src/pages/CreateItem.tsx +++ b/frontend/src/pages/CreateItem.tsx @@ -7,7 +7,6 @@ import AvailabilitySettings from "../components/AvailabilitySettings"; import ImageUpload from "../components/ImageUpload"; import ItemInformation from "../components/ItemInformation"; import LocationForm from "../components/LocationForm"; -import DeliveryOptions from "../components/DeliveryOptions"; import PricingForm from "../components/PricingForm"; import RulesForm from "../components/RulesForm"; import VerificationCodeModal from "../components/VerificationCodeModal"; @@ -17,8 +16,6 @@ import { IMAGE_LIMITS } from "../config/imageLimits"; interface ItemFormData { name: string; description: string; - pickUpAvailable: boolean; - inPlaceUseAvailable: boolean; pricePerHour?: number | string; pricePerDay?: number | string; pricePerWeek?: number | string; @@ -57,8 +54,6 @@ const CreateItem: React.FC = () => { const [formData, setFormData] = useState({ name: "", description: "", - pickUpAvailable: false, - inPlaceUseAvailable: false, pricePerDay: "", replacementCost: "", address1: "", @@ -539,12 +534,6 @@ const CreateItem: React.FC = () => { }} /> - - {/* Availability Card */}
diff --git a/frontend/src/pages/EditItem.tsx b/frontend/src/pages/EditItem.tsx index 94d039a..ee896c5 100644 --- a/frontend/src/pages/EditItem.tsx +++ b/frontend/src/pages/EditItem.tsx @@ -8,7 +8,6 @@ import AvailabilitySettings from "../components/AvailabilitySettings"; import ImageUpload from "../components/ImageUpload"; import ItemInformation from "../components/ItemInformation"; import LocationForm from "../components/LocationForm"; -import DeliveryOptions from "../components/DeliveryOptions"; import PricingForm from "../components/PricingForm"; import RulesForm from "../components/RulesForm"; import { IMAGE_LIMITS } from "../config/imageLimits"; @@ -16,8 +15,6 @@ import { IMAGE_LIMITS } from "../config/imageLimits"; interface ItemFormData { name: string; description: string; - pickUpAvailable: boolean; - inPlaceUseAvailable: boolean; pricePerHour?: number | string; pricePerDay?: number | string; pricePerWeek?: number | string; @@ -78,8 +75,6 @@ const EditItem: React.FC = () => { const [formData, setFormData] = useState({ name: "", description: "", - pickUpAvailable: false, - inPlaceUseAvailable: false, pricePerHour: "", pricePerDay: "", replacementCost: "", @@ -135,8 +130,6 @@ const EditItem: React.FC = () => { setFormData({ name: item.name, description: item.description, - pickUpAvailable: item.pickUpAvailable || false, - inPlaceUseAvailable: item.inPlaceUseAvailable || false, pricePerHour: item.pricePerHour || "", pricePerDay: item.pricePerDay || "", pricePerWeek: item.pricePerWeek || "", @@ -578,12 +571,6 @@ const EditItem: React.FC = () => { }} /> - - {
{ <>
-
- -
+
+ +
{ style={{ flex: "1 1 50%" }} /> { style={{ flex: "1 1 50%" }} />