Google maps integration
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import api, { addressAPI, userAPI, itemAPI } from "../services/api";
|
||||
@@ -83,6 +83,9 @@ const CreateItem: React.FC = () => {
|
||||
const [userAddresses, setUserAddresses] = useState<Address[]>([]);
|
||||
const [selectedAddressId, setSelectedAddressId] = useState<string>("");
|
||||
const [addressesLoading, setAddressesLoading] = useState(true);
|
||||
|
||||
// Reference to LocationForm geocoding function
|
||||
const geocodeLocationRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserAddresses();
|
||||
@@ -150,6 +153,15 @@ const CreateItem: React.FC = () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
// Try to geocode the address before submitting
|
||||
if (geocodeLocationRef.current) {
|
||||
try {
|
||||
await geocodeLocationRef.current();
|
||||
} catch (error) {
|
||||
console.warn('Geocoding failed, creating item without coordinates:', error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// For now, we'll store image URLs as base64 strings
|
||||
// In production, you'd upload to a service like S3
|
||||
@@ -253,6 +265,14 @@ const CreateItem: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCoordinatesChange = (latitude: number, longitude: number) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
latitude,
|
||||
longitude,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleAddressSelect = (addressId: string) => {
|
||||
if (addressId === "new") {
|
||||
// Clear form for new address entry
|
||||
@@ -379,6 +399,10 @@ const CreateItem: React.FC = () => {
|
||||
onChange={handleChange}
|
||||
onAddressSelect={handleAddressSelect}
|
||||
formatAddressDisplay={formatAddressDisplay}
|
||||
onCoordinatesChange={handleCoordinatesChange}
|
||||
onGeocodeRef={(geocodeFunction) => {
|
||||
geocodeLocationRef.current = geocodeFunction;
|
||||
}}
|
||||
/>
|
||||
|
||||
<DeliveryOptions
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { itemRequestAPI } from "../services/api";
|
||||
import AddressAutocomplete from "../components/AddressAutocomplete";
|
||||
|
||||
const CreateItemRequest: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -42,18 +41,6 @@ const CreateItemRequest: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddressChange = (value: string, lat?: number, lon?: number) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
address1: value,
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
city: prev.city,
|
||||
state: prev.state,
|
||||
zipCode: prev.zipCode,
|
||||
country: prev.country,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -186,10 +173,14 @@ const CreateItemRequest: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Address</label>
|
||||
<AddressAutocomplete
|
||||
<label htmlFor="address1" className="form-label">Address</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="address1"
|
||||
name="address1"
|
||||
value={formData.address1}
|
||||
onChange={handleAddressChange}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter your address or area"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Item, Rental, Address } from "../types";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
@@ -58,6 +58,9 @@ const EditItem: React.FC = () => {
|
||||
const [userAddresses, setUserAddresses] = useState<Address[]>([]);
|
||||
const [selectedAddressId, setSelectedAddressId] = useState<string>("");
|
||||
const [addressesLoading, setAddressesLoading] = useState(true);
|
||||
|
||||
// Reference to LocationForm geocoding function
|
||||
const geocodeLocationRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
const [formData, setFormData] = useState<ItemFormData>({
|
||||
name: "",
|
||||
description: "",
|
||||
@@ -204,10 +207,27 @@ const EditItem: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCoordinatesChange = (latitude: number, longitude: number) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
latitude,
|
||||
longitude,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
|
||||
// Try to geocode the address before submitting
|
||||
if (geocodeLocationRef.current) {
|
||||
try {
|
||||
await geocodeLocationRef.current();
|
||||
} catch (error) {
|
||||
console.warn('Geocoding failed, updating item without coordinates:', error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Use existing image previews (which includes both old and new images)
|
||||
const imageUrls = imagePreviews;
|
||||
@@ -412,6 +432,10 @@ const EditItem: React.FC = () => {
|
||||
onChange={handleChange}
|
||||
onAddressSelect={handleAddressSelect}
|
||||
formatAddressDisplay={formatAddressDisplay}
|
||||
onCoordinatesChange={handleCoordinatesChange}
|
||||
onGeocodeRef={(geocodeFunction) => {
|
||||
geocodeLocationRef.current = geocodeFunction;
|
||||
}}
|
||||
/>
|
||||
|
||||
<DeliveryOptions
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Item, Rental } from "../types";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { itemAPI, rentalAPI } from "../services/api";
|
||||
import LocationMap from "../components/LocationMap";
|
||||
import GoogleMapWithRadius from "../components/GoogleMapWithRadius";
|
||||
import ItemReviews from "../components/ItemReviews";
|
||||
|
||||
const ItemDetail: React.FC = () => {
|
||||
@@ -357,11 +357,9 @@ const ItemDetail: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Map */}
|
||||
<LocationMap
|
||||
<GoogleMapWithRadius
|
||||
latitude={item.latitude}
|
||||
longitude={item.longitude}
|
||||
location={item.location}
|
||||
itemName={item.name}
|
||||
/>
|
||||
|
||||
<ItemReviews itemId={item.id} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { userAPI, itemAPI, rentalAPI, addressAPI } from "../services/api";
|
||||
import { User, Item, Rental, Address } from "../types";
|
||||
@@ -6,8 +6,11 @@ import { getImageUrl } from "../utils/imageUrl";
|
||||
import AvailabilitySettings from "../components/AvailabilitySettings";
|
||||
import ReviewItemModal from "../components/ReviewModal";
|
||||
import ReviewRenterModal from "../components/ReviewRenterModal";
|
||||
import StarRating from "../components/StarRating";
|
||||
import ReviewDetailsModal from "../components/ReviewDetailsModal";
|
||||
import {
|
||||
geocodingService,
|
||||
AddressComponents,
|
||||
} from "../services/geocodingService";
|
||||
|
||||
const Profile: React.FC = () => {
|
||||
const { user, updateUser, logout } = useAuth();
|
||||
@@ -62,7 +65,14 @@ const Profile: React.FC = () => {
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "US",
|
||||
latitude: undefined as number | undefined,
|
||||
longitude: undefined as number | undefined,
|
||||
});
|
||||
const [addressGeocoding, setAddressGeocoding] = useState(false);
|
||||
const [addressGeocodeError, setAddressGeocodeError] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [addressGeocodeSuccess, setAddressGeocodeSuccess] = useState(false);
|
||||
|
||||
// Rental history state
|
||||
const [pastRenterRentals, setPastRenterRentals] = useState<Rental[]>([]);
|
||||
@@ -404,6 +414,8 @@ const Profile: React.FC = () => {
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "US",
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
});
|
||||
setEditingAddressId(null);
|
||||
setShowAddressForm(true);
|
||||
@@ -417,6 +429,8 @@ const Profile: React.FC = () => {
|
||||
state: address.state,
|
||||
zipCode: address.zipCode,
|
||||
country: address.country,
|
||||
latitude: address.latitude,
|
||||
longitude: address.longitude,
|
||||
});
|
||||
setEditingAddressId(address.id);
|
||||
setShowAddressForm(true);
|
||||
@@ -429,8 +443,59 @@ const Profile: React.FC = () => {
|
||||
setAddressFormData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
// Geocoding function for address form
|
||||
const geocodeAddressForm = useCallback(
|
||||
async (addressData: typeof addressFormData) => {
|
||||
if (
|
||||
!geocodingService.isAddressComplete(addressData as AddressComponents)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAddressGeocoding(true);
|
||||
setAddressGeocodeError(null);
|
||||
setAddressGeocodeSuccess(false);
|
||||
|
||||
try {
|
||||
const result = await geocodingService.geocodeAddress(
|
||||
addressData as AddressComponents
|
||||
);
|
||||
|
||||
if ("error" in result) {
|
||||
setAddressGeocodeError(result.details || result.error);
|
||||
} else {
|
||||
setAddressGeocodeSuccess(true);
|
||||
setAddressFormData((prev) => ({
|
||||
...prev,
|
||||
latitude: result.latitude,
|
||||
longitude: result.longitude,
|
||||
}));
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => setAddressGeocodeSuccess(false), 3000);
|
||||
}
|
||||
} catch (error) {
|
||||
setAddressGeocodeError("Failed to geocode address");
|
||||
} finally {
|
||||
setAddressGeocoding(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSaveAddress = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Try to geocode the address before saving
|
||||
try {
|
||||
await geocodeAddressForm(addressFormData);
|
||||
} catch (error) {
|
||||
// Geocoding failed, but we'll continue with saving
|
||||
console.warn(
|
||||
"Geocoding failed, saving address without coordinates:",
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (editingAddressId) {
|
||||
// Update existing address
|
||||
@@ -469,7 +534,12 @@ const Profile: React.FC = () => {
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "US",
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
});
|
||||
setAddressGeocoding(false);
|
||||
setAddressGeocodeError(null);
|
||||
setAddressGeocodeSuccess(false);
|
||||
};
|
||||
|
||||
const usStates = [
|
||||
|
||||
@@ -129,7 +129,7 @@ const PublicProfile: React.FC = () => {
|
||||
)}
|
||||
<div className="card-body">
|
||||
<h6 className="card-title">{item.name}</h6>
|
||||
<p className="card-text text-muted small">{item.location}</p>
|
||||
<p className="card-text text-muted small">{item.city && item.state ? `${item.city}, ${item.state}` : ''}</p>
|
||||
<div>
|
||||
{item.pricePerDay && (
|
||||
<span className="badge bg-primary">${item.pricePerDay}/day</span>
|
||||
|
||||
@@ -280,7 +280,7 @@ const RentItem: React.FC = () => {
|
||||
<p className="text-muted small">
|
||||
{item.city && item.state
|
||||
? `${item.city}, ${item.state}`
|
||||
: item.location}
|
||||
: ''}
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
Reference in New Issue
Block a user