fixing bugs with item notification radius
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { forumAPI } from "../services/api";
|
||||
import { forumAPI, addressAPI } from "../services/api";
|
||||
import TagInput from "../components/TagInput";
|
||||
import ForumImageUpload from "../components/ForumImageUpload";
|
||||
import { Address } from "../types";
|
||||
|
||||
const CreateForumPost: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [userAddresses, setUserAddresses] = useState<Address[]>([]);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
title: "",
|
||||
@@ -21,11 +23,26 @@ const CreateForumPost: React.FC = () => {
|
||||
| "general_discussion",
|
||||
tags: [] as string[],
|
||||
zipCode: user?.zipCode || "",
|
||||
latitude: undefined as number | undefined,
|
||||
longitude: undefined as number | undefined,
|
||||
});
|
||||
|
||||
const [imageFiles, setImageFiles] = useState<File[]>([]);
|
||||
const [imagePreviews, setImagePreviews] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserAddresses();
|
||||
}, []);
|
||||
|
||||
const fetchUserAddresses = async () => {
|
||||
try {
|
||||
const response = await addressAPI.getAddresses();
|
||||
setUserAddresses(response.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching addresses:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const categories = [
|
||||
{
|
||||
value: "item_request",
|
||||
@@ -55,7 +72,21 @@ const CreateForumPost: React.FC = () => {
|
||||
>
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
|
||||
// If category is being changed to item_request and user has addresses, autopopulate location data
|
||||
if (name === "category" && value === "item_request" && userAddresses.length > 0) {
|
||||
// Try to find primary address first, otherwise use first address
|
||||
const primaryAddress = userAddresses.find(addr => addr.isPrimary) || userAddresses[0];
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
zipCode: primaryAddress.zipCode,
|
||||
latitude: primaryAddress.latitude,
|
||||
longitude: primaryAddress.longitude
|
||||
}));
|
||||
} else {
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleTagsChange = (tags: string[]) => {
|
||||
@@ -134,6 +165,11 @@ const CreateForumPost: React.FC = () => {
|
||||
// Add location data for item requests
|
||||
if (formData.category === 'item_request' && formData.zipCode) {
|
||||
submitData.append('zipCode', formData.zipCode);
|
||||
// If we have coordinates from a saved address, send them to avoid re-geocoding
|
||||
if (formData.latitude !== undefined && formData.longitude !== undefined) {
|
||||
submitData.append('latitude', formData.latitude.toString());
|
||||
submitData.append('longitude', formData.longitude.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Add images
|
||||
|
||||
@@ -83,7 +83,8 @@ const CreateItem: React.FC = () => {
|
||||
const [selectedAddressId, setSelectedAddressId] = useState<string>("");
|
||||
const [addressesLoading, setAddressesLoading] = useState(true);
|
||||
const [selectedPricingUnit, setSelectedPricingUnit] = useState<string>("day");
|
||||
const [showAdvancedPricing, setShowAdvancedPricing] = useState<boolean>(false);
|
||||
const [showAdvancedPricing, setShowAdvancedPricing] =
|
||||
useState<boolean>(false);
|
||||
const [enabledPricingTiers, setEnabledPricingTiers] = useState({
|
||||
hour: false,
|
||||
day: false,
|
||||
@@ -92,7 +93,9 @@ const CreateItem: React.FC = () => {
|
||||
});
|
||||
|
||||
// Reference to LocationForm geocoding function
|
||||
const geocodeLocationRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
const geocodeLocationRef = useRef<
|
||||
(() => Promise<{ latitude: number; longitude: number } | null>) | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserAddresses();
|
||||
@@ -161,11 +164,15 @@ const CreateItem: React.FC = () => {
|
||||
setError("");
|
||||
|
||||
// Try to geocode the address before submitting
|
||||
let geocodedCoordinates = null;
|
||||
if (geocodeLocationRef.current) {
|
||||
try {
|
||||
await geocodeLocationRef.current();
|
||||
geocodedCoordinates = await geocodeLocationRef.current();
|
||||
} catch (error) {
|
||||
console.warn('Geocoding failed, creating item without coordinates:', error);
|
||||
console.warn(
|
||||
"Geocoding failed, creating item without coordinates:",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +195,9 @@ const CreateItem: React.FC = () => {
|
||||
|
||||
const response = await api.post("/items", {
|
||||
...formData,
|
||||
// Use geocoded coordinates if available, otherwise fall back to formData
|
||||
latitude: geocodedCoordinates?.latitude ?? formData.latitude,
|
||||
longitude: geocodedCoordinates?.longitude ?? formData.longitude,
|
||||
pricePerDay: formData.pricePerDay
|
||||
? parseFloat(formData.pricePerDay.toString())
|
||||
: undefined,
|
||||
@@ -221,8 +231,8 @@ const CreateItem: React.FC = () => {
|
||||
state: formData.state,
|
||||
zipCode: formData.zipCode,
|
||||
country: formData.country,
|
||||
latitude: formData.latitude,
|
||||
longitude: formData.longitude,
|
||||
latitude: geocodedCoordinates?.latitude ?? formData.latitude,
|
||||
longitude: geocodedCoordinates?.longitude ?? formData.longitude,
|
||||
isPrimary: true,
|
||||
});
|
||||
} catch (addressError) {
|
||||
|
||||
@@ -59,7 +59,8 @@ const EditItem: React.FC = () => {
|
||||
const [selectedAddressId, setSelectedAddressId] = useState<string>("");
|
||||
const [addressesLoading, setAddressesLoading] = useState(true);
|
||||
const [selectedPricingUnit, setSelectedPricingUnit] = useState<string>("day");
|
||||
const [showAdvancedPricing, setShowAdvancedPricing] = useState<boolean>(false);
|
||||
const [showAdvancedPricing, setShowAdvancedPricing] =
|
||||
useState<boolean>(false);
|
||||
const [enabledPricingTiers, setEnabledPricingTiers] = useState({
|
||||
hour: false,
|
||||
day: false,
|
||||
@@ -68,7 +69,9 @@ const EditItem: React.FC = () => {
|
||||
});
|
||||
|
||||
// Reference to LocationForm geocoding function
|
||||
const geocodeLocationRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
const geocodeLocationRef = useRef<
|
||||
(() => Promise<{ latitude: number; longitude: number } | null>) | null
|
||||
>(null);
|
||||
const [formData, setFormData] = useState<ItemFormData>({
|
||||
name: "",
|
||||
description: "",
|
||||
@@ -255,20 +258,29 @@ const EditItem: React.FC = () => {
|
||||
setError(null);
|
||||
|
||||
// Try to geocode the address before submitting
|
||||
let geocodedCoordinates = null;
|
||||
if (geocodeLocationRef.current) {
|
||||
try {
|
||||
await geocodeLocationRef.current();
|
||||
geocodedCoordinates = await geocodeLocationRef.current();
|
||||
} catch (error) {
|
||||
console.warn('Geocoding failed, updating item without coordinates:', error);
|
||||
console.warn(
|
||||
"Geocoding failed, updating item without coordinates:",
|
||||
error
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn("No geocoding function available");
|
||||
}
|
||||
|
||||
try {
|
||||
// Use existing image previews (which includes both old and new images)
|
||||
const imageUrls = imagePreviews;
|
||||
|
||||
await itemAPI.updateItem(id!, {
|
||||
const updatePayload = {
|
||||
...formData,
|
||||
// Use geocoded coordinates if available, otherwise fall back to formData
|
||||
latitude: geocodedCoordinates?.latitude ?? formData.latitude,
|
||||
longitude: geocodedCoordinates?.longitude ?? formData.longitude,
|
||||
pricePerDay: formData.pricePerDay
|
||||
? parseFloat(formData.pricePerDay.toString())
|
||||
: undefined,
|
||||
@@ -289,7 +301,9 @@ const EditItem: React.FC = () => {
|
||||
specifyTimesPerDay: formData.specifyTimesPerDay,
|
||||
weeklyTimes: formData.weeklyTimes,
|
||||
images: imageUrls,
|
||||
});
|
||||
};
|
||||
|
||||
await itemAPI.updateItem(id!, updatePayload);
|
||||
|
||||
// Check if user has other items - only save to user profile if no other items
|
||||
try {
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
geocodingService,
|
||||
AddressComponents,
|
||||
} from "../services/geocodingService";
|
||||
import AddressAutocomplete from "../components/AddressAutocomplete";
|
||||
import { PlaceDetails } from "../services/placesService";
|
||||
import { useAddressAutocomplete, usStates } from "../hooks/useAddressAutocomplete";
|
||||
|
||||
const Profile: React.FC = () => {
|
||||
const { user, updateUser, logout } = useAuth();
|
||||
@@ -398,6 +401,52 @@ const Profile: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveNotificationPreferences = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
|
||||
try {
|
||||
const response = await userAPI.updateProfile({
|
||||
itemRequestNotificationRadius: formData.itemRequestNotificationRadius,
|
||||
});
|
||||
setProfileData(response.data);
|
||||
updateUser(response.data);
|
||||
setSuccess("Notification preferences saved successfully");
|
||||
setTimeout(() => setSuccess(null), 3000);
|
||||
} catch (err: any) {
|
||||
console.error("Notification preferences update error:", err.response?.data);
|
||||
const errorMessage =
|
||||
err.response?.data?.error ||
|
||||
err.response?.data?.message ||
|
||||
"Failed to update notification preferences";
|
||||
setError(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNotificationRadiusChange = async (
|
||||
e: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
const { value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, itemRequestNotificationRadius: parseInt(value) }));
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await userAPI.updateProfile({
|
||||
itemRequestNotificationRadius: parseInt(value),
|
||||
});
|
||||
setProfileData(response.data);
|
||||
updateUser(response.data);
|
||||
} catch (err: any) {
|
||||
console.error("Notification preferences update error:", err.response?.data);
|
||||
const errorMessage =
|
||||
err.response?.data?.error ||
|
||||
err.response?.data?.message ||
|
||||
"Failed to update notification radius";
|
||||
setError(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
const formatAddressDisplay = (address: Address) => {
|
||||
return `${address.address1}, ${address.city}, ${address.state} ${address.zipCode}`;
|
||||
};
|
||||
@@ -455,7 +504,7 @@ const Profile: React.FC = () => {
|
||||
if (
|
||||
!geocodingService.isAddressComplete(addressData as AddressComponents)
|
||||
) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
setAddressGeocoding(true);
|
||||
@@ -469,6 +518,7 @@ const Profile: React.FC = () => {
|
||||
|
||||
if ("error" in result) {
|
||||
setAddressGeocodeError(result.details || result.error);
|
||||
return null;
|
||||
} else {
|
||||
setAddressGeocodeSuccess(true);
|
||||
setAddressFormData((prev) => ({
|
||||
@@ -478,9 +528,11 @@ const Profile: React.FC = () => {
|
||||
}));
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => setAddressGeocodeSuccess(false), 3000);
|
||||
return { latitude: result.latitude, longitude: result.longitude };
|
||||
}
|
||||
} catch (error) {
|
||||
setAddressGeocodeError("Failed to geocode address");
|
||||
return null;
|
||||
} finally {
|
||||
setAddressGeocoding(false);
|
||||
}
|
||||
@@ -488,12 +540,13 @@ const Profile: React.FC = () => {
|
||||
[]
|
||||
);
|
||||
|
||||
const handleSaveAddress = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const handleSaveAddress = async (e?: React.FormEvent | React.MouseEvent) => {
|
||||
e?.preventDefault();
|
||||
|
||||
// Try to geocode the address before saving
|
||||
let coordinates = null;
|
||||
try {
|
||||
await geocodeAddressForm(addressFormData);
|
||||
coordinates = await geocodeAddressForm(addressFormData);
|
||||
} catch (error) {
|
||||
// Geocoding failed, but we'll continue with saving
|
||||
console.warn(
|
||||
@@ -502,12 +555,21 @@ const Profile: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare the data to save, including coordinates if geocoding succeeded
|
||||
const dataToSave = {
|
||||
...addressFormData,
|
||||
...(coordinates && {
|
||||
latitude: coordinates.latitude,
|
||||
longitude: coordinates.longitude
|
||||
})
|
||||
};
|
||||
|
||||
try {
|
||||
if (editingAddressId) {
|
||||
// Update existing address
|
||||
const response = await addressAPI.updateAddress(
|
||||
editingAddressId,
|
||||
addressFormData
|
||||
dataToSave
|
||||
);
|
||||
setUserAddresses((prev) =>
|
||||
prev.map((addr) =>
|
||||
@@ -517,7 +579,7 @@ const Profile: React.FC = () => {
|
||||
} else {
|
||||
// Create new address
|
||||
const response = await addressAPI.createAddress({
|
||||
...addressFormData,
|
||||
...dataToSave,
|
||||
isPrimary: userAddresses.length === 0,
|
||||
});
|
||||
setUserAddresses((prev) => [...prev, response.data]);
|
||||
@@ -548,58 +610,21 @@ const Profile: React.FC = () => {
|
||||
setAddressGeocodeSuccess(false);
|
||||
};
|
||||
|
||||
const usStates = [
|
||||
"Alabama",
|
||||
"Alaska",
|
||||
"Arizona",
|
||||
"Arkansas",
|
||||
"California",
|
||||
"Colorado",
|
||||
"Connecticut",
|
||||
"Delaware",
|
||||
"Florida",
|
||||
"Georgia",
|
||||
"Hawaii",
|
||||
"Idaho",
|
||||
"Illinois",
|
||||
"Indiana",
|
||||
"Iowa",
|
||||
"Kansas",
|
||||
"Kentucky",
|
||||
"Louisiana",
|
||||
"Maine",
|
||||
"Maryland",
|
||||
"Massachusetts",
|
||||
"Michigan",
|
||||
"Minnesota",
|
||||
"Mississippi",
|
||||
"Missouri",
|
||||
"Montana",
|
||||
"Nebraska",
|
||||
"Nevada",
|
||||
"New Hampshire",
|
||||
"New Jersey",
|
||||
"New Mexico",
|
||||
"New York",
|
||||
"North Carolina",
|
||||
"North Dakota",
|
||||
"Ohio",
|
||||
"Oklahoma",
|
||||
"Oregon",
|
||||
"Pennsylvania",
|
||||
"Rhode Island",
|
||||
"South Carolina",
|
||||
"South Dakota",
|
||||
"Tennessee",
|
||||
"Texas",
|
||||
"Utah",
|
||||
"Vermont",
|
||||
"Virginia",
|
||||
"Washington",
|
||||
"West Virginia",
|
||||
"Wisconsin",
|
||||
"Wyoming",
|
||||
];
|
||||
// Use address autocomplete hook
|
||||
const { parsePlace } = useAddressAutocomplete();
|
||||
|
||||
// Handle place selection from autocomplete
|
||||
const handlePlaceSelect = useCallback((place: PlaceDetails) => {
|
||||
const parsedAddress = parsePlace(place);
|
||||
if (parsedAddress) {
|
||||
setAddressFormData((prev) => ({
|
||||
...prev,
|
||||
...parsedAddress,
|
||||
}));
|
||||
setAddressGeocodeSuccess(true);
|
||||
setTimeout(() => setAddressGeocodeSuccess(false), 3000);
|
||||
}
|
||||
}, [parsePlace]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -652,6 +677,15 @@ const Profile: React.FC = () => {
|
||||
<i className="bi bi-gear me-2"></i>
|
||||
Owner Settings
|
||||
</button>
|
||||
<button
|
||||
className={`list-group-item list-group-item-action ${
|
||||
activeSection === "notification-preferences" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveSection("notification-preferences")}
|
||||
>
|
||||
<i className="bi bi-bell me-2"></i>
|
||||
Notification Preferences
|
||||
</button>
|
||||
<button
|
||||
className={`list-group-item list-group-item-action ${
|
||||
activeSection === "rental-history" ? "active" : ""
|
||||
@@ -682,112 +716,59 @@ const Profile: React.FC = () => {
|
||||
{/* Profile Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="text-center">
|
||||
<div className="position-relative d-inline-block mb-3">
|
||||
{imagePreview ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
alt="Profile"
|
||||
className="rounded-circle"
|
||||
style={{
|
||||
width: "120px",
|
||||
height: "120px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="rounded-circle bg-secondary d-flex align-items-center justify-content-center"
|
||||
style={{ width: "120px", height: "120px" }}
|
||||
>
|
||||
<i
|
||||
className="bi bi-person-fill text-white"
|
||||
style={{ fontSize: "2.5rem" }}
|
||||
></i>
|
||||
</div>
|
||||
)}
|
||||
{editing && (
|
||||
<label
|
||||
htmlFor="profileImageOverview"
|
||||
className="position-absolute bottom-0 end-0 btn btn-sm btn-primary rounded-circle"
|
||||
style={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
padding: "0",
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-camera-fill"></i>
|
||||
<input
|
||||
type="file"
|
||||
id="profileImageOverview"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="d-none"
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{editing ? (
|
||||
<div>
|
||||
<div className="row justify-content-center mb-3">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="firstName"
|
||||
value={formData.firstName}
|
||||
onChange={handleChange}
|
||||
placeholder="First Name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={handleChange}
|
||||
placeholder="Last Name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex gap-2 justify-content-center">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Save Changes
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="position-relative d-inline-block mb-3">
|
||||
{imagePreview ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
alt="Profile"
|
||||
className="rounded-circle"
|
||||
style={{
|
||||
width: "120px",
|
||||
height: "120px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
<h5>
|
||||
{profileData?.firstName} {profileData?.lastName}
|
||||
</h5>
|
||||
<p className="text-muted">@{profileData?.username}</p>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary"
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
<i className="bi bi-pencil me-2"></i>
|
||||
Edit Profile
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="rounded-circle bg-secondary d-flex align-items-center justify-content-center"
|
||||
style={{ width: "120px", height: "120px" }}
|
||||
>
|
||||
<i
|
||||
className="bi bi-person-fill text-white"
|
||||
style={{ fontSize: "2.5rem" }}
|
||||
></i>
|
||||
</div>
|
||||
)}
|
||||
{editing && (
|
||||
<label
|
||||
htmlFor="profileImageOverview"
|
||||
className="position-absolute bottom-0 end-0 btn btn-sm btn-primary rounded-circle"
|
||||
style={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
padding: "0",
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-camera-fill"></i>
|
||||
<input
|
||||
type="file"
|
||||
id="profileImageOverview"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="d-none"
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<h5>
|
||||
{profileData?.firstName} {profileData?.lastName}
|
||||
</h5>
|
||||
<p className="text-muted">@{profileData?.username}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -807,6 +788,39 @@ const Profile: React.FC = () => {
|
||||
</div>
|
||||
{showPersonalInfo && (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="firstName" className="form-label">
|
||||
First Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="firstName"
|
||||
name="firstName"
|
||||
value={formData.firstName}
|
||||
onChange={handleChange}
|
||||
disabled={!editing}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="lastName" className="form-label">
|
||||
Last Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="lastName"
|
||||
name="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={handleChange}
|
||||
disabled={!editing}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="email" className="form-label">
|
||||
Email
|
||||
@@ -931,7 +945,7 @@ const Profile: React.FC = () => {
|
||||
|
||||
{/* Address Form */}
|
||||
{showAddressForm && (
|
||||
<form onSubmit={handleSaveAddress}>
|
||||
<div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<label
|
||||
@@ -940,15 +954,26 @@ const Profile: React.FC = () => {
|
||||
>
|
||||
Address Line 1 *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
<AddressAutocomplete
|
||||
id="addressFormAddress1"
|
||||
name="address1"
|
||||
value={addressFormData.address1}
|
||||
onChange={handleAddressFormChange}
|
||||
placeholder=""
|
||||
onChange={(value) => {
|
||||
const syntheticEvent = {
|
||||
target: {
|
||||
name: "address1",
|
||||
value,
|
||||
type: "text",
|
||||
},
|
||||
} as React.ChangeEvent<HTMLInputElement>;
|
||||
handleAddressFormChange(syntheticEvent);
|
||||
}}
|
||||
onPlaceSelect={handlePlaceSelect}
|
||||
placeholder="Start typing an address..."
|
||||
className="form-control"
|
||||
required
|
||||
countryRestriction="us"
|
||||
types={["address"]}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
@@ -1025,6 +1050,11 @@ const Profile: React.FC = () => {
|
||||
name="zipCode"
|
||||
value={addressFormData.zipCode}
|
||||
onChange={handleAddressFormChange}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleSaveAddress(e);
|
||||
}
|
||||
}}
|
||||
placeholder="12345"
|
||||
required
|
||||
/>
|
||||
@@ -1032,7 +1062,11 @@ const Profile: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="d-flex gap-2">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleSaveAddress}
|
||||
>
|
||||
{editingAddressId
|
||||
? "Update Address"
|
||||
: "Save Address"}
|
||||
@@ -1045,7 +1079,7 @@ const Profile: React.FC = () => {
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
@@ -1053,36 +1087,6 @@ const Profile: React.FC = () => {
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
{/* Notification Preferences Section */}
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Notification Preferences</label>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="itemRequestNotificationRadius" className="form-label">
|
||||
Item Requests Notification Radius
|
||||
</label>
|
||||
<select
|
||||
className="form-select"
|
||||
id="itemRequestNotificationRadius"
|
||||
name="itemRequestNotificationRadius"
|
||||
value={formData.itemRequestNotificationRadius}
|
||||
onChange={handleChange}
|
||||
disabled={!editing}
|
||||
>
|
||||
<option value="5">5 miles</option>
|
||||
<option value="10">10 miles</option>
|
||||
<option value="25">25 miles</option>
|
||||
<option value="50">50 miles</option>
|
||||
<option value="100">100 miles</option>
|
||||
</select>
|
||||
<div className="form-text">
|
||||
You'll receive notifications when someone posts an item request within this distance from your primary address
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
{editing ? (
|
||||
<div className="d-flex gap-2">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
@@ -1447,6 +1451,39 @@ const Profile: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Notification Preferences Section */}
|
||||
{activeSection === "notification-preferences" && (
|
||||
<div>
|
||||
<h4 className="mb-4">Notification Preferences</h4>
|
||||
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="itemRequestNotificationRadius" className="form-label">
|
||||
Item Requests Notification Radius
|
||||
</label>
|
||||
<select
|
||||
className="form-select"
|
||||
id="itemRequestNotificationRadius"
|
||||
name="itemRequestNotificationRadius"
|
||||
value={formData.itemRequestNotificationRadius}
|
||||
onChange={handleNotificationRadiusChange}
|
||||
>
|
||||
<option value="5">5 miles</option>
|
||||
<option value="10">10 miles</option>
|
||||
<option value="25">25 miles</option>
|
||||
<option value="50">50 miles</option>
|
||||
<option value="100">100 miles</option>
|
||||
</select>
|
||||
<div className="form-text">
|
||||
You'll receive notifications when someone posts an item request within this distance from your primary address
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user