made components that create and edit item can share, started item detail changes, listings provide more views
This commit is contained in:
@@ -1,36 +1,47 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Item, Rental } from "../types";
|
||||
import { Item, Rental, Address } from "../types";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { itemAPI, rentalAPI } from "../services/api";
|
||||
import AvailabilityCalendar from "../components/AvailabilityCalendar";
|
||||
import AddressAutocomplete from "../components/AddressAutocomplete";
|
||||
import { itemAPI, rentalAPI, addressAPI, userAPI } from "../services/api";
|
||||
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";
|
||||
|
||||
interface ItemFormData {
|
||||
name: string;
|
||||
description: string;
|
||||
pickUpAvailable: boolean;
|
||||
localDeliveryAvailable: boolean;
|
||||
localDeliveryRadius?: number;
|
||||
shippingAvailable: boolean;
|
||||
inPlaceUseAvailable: boolean;
|
||||
pricePerHour?: number | string;
|
||||
pricePerDay?: number | string;
|
||||
replacementCost: number | string;
|
||||
location: string;
|
||||
address1: string;
|
||||
address2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
zipCode: string;
|
||||
country: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
rules?: string;
|
||||
minimumRentalDays: number;
|
||||
needsTraining: boolean;
|
||||
availability: boolean;
|
||||
unavailablePeriods?: Array<{
|
||||
id: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}>;
|
||||
generalAvailableAfter: string;
|
||||
generalAvailableBefore: string;
|
||||
specifyTimesPerDay: boolean;
|
||||
weeklyTimes: {
|
||||
sunday: { availableAfter: string; availableBefore: string };
|
||||
monday: { availableAfter: string; availableBefore: string };
|
||||
tuesday: { availableAfter: string; availableBefore: string };
|
||||
wednesday: { availableAfter: string; availableBefore: string };
|
||||
thursday: { availableAfter: string; availableBefore: string };
|
||||
friday: { availableAfter: string; availableBefore: string };
|
||||
saturday: { availableAfter: string; availableBefore: string };
|
||||
};
|
||||
}
|
||||
|
||||
const EditItem: React.FC = () => {
|
||||
@@ -44,29 +55,57 @@ const EditItem: React.FC = () => {
|
||||
const [imagePreviews, setImagePreviews] = useState<string[]>([]);
|
||||
const [priceType, setPriceType] = useState<"hour" | "day">("day");
|
||||
const [acceptedRentals, setAcceptedRentals] = useState<Rental[]>([]);
|
||||
const [userAddresses, setUserAddresses] = useState<Address[]>([]);
|
||||
const [selectedAddressId, setSelectedAddressId] = useState<string>("");
|
||||
const [addressesLoading, setAddressesLoading] = useState(true);
|
||||
const [formData, setFormData] = useState<ItemFormData>({
|
||||
name: "",
|
||||
description: "",
|
||||
pickUpAvailable: false,
|
||||
localDeliveryAvailable: false,
|
||||
shippingAvailable: false,
|
||||
inPlaceUseAvailable: false,
|
||||
pricePerHour: undefined,
|
||||
pricePerDay: undefined,
|
||||
replacementCost: 0,
|
||||
location: "",
|
||||
pricePerHour: "",
|
||||
pricePerDay: "",
|
||||
replacementCost: "",
|
||||
address1: "",
|
||||
address2: "",
|
||||
city: "",
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "US",
|
||||
rules: "",
|
||||
minimumRentalDays: 1,
|
||||
needsTraining: false,
|
||||
availability: true,
|
||||
unavailablePeriods: [],
|
||||
generalAvailableAfter: "09:00",
|
||||
generalAvailableBefore: "17:00",
|
||||
specifyTimesPerDay: false,
|
||||
weeklyTimes: {
|
||||
sunday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
monday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
tuesday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
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" },
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchItem();
|
||||
fetchAcceptedRentals();
|
||||
fetchUserAddresses();
|
||||
}, [id]);
|
||||
|
||||
const fetchUserAddresses = async () => {
|
||||
try {
|
||||
const response = await addressAPI.getAddresses();
|
||||
setUserAddresses(response.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching addresses:", error);
|
||||
} finally {
|
||||
setAddressesLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchItem = async () => {
|
||||
try {
|
||||
const response = await itemAPI.getItem(id!);
|
||||
@@ -89,21 +128,33 @@ const EditItem: React.FC = () => {
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
pickUpAvailable: item.pickUpAvailable || false,
|
||||
localDeliveryAvailable: item.localDeliveryAvailable || false,
|
||||
localDeliveryRadius: item.localDeliveryRadius || 25,
|
||||
shippingAvailable: item.shippingAvailable || false,
|
||||
inPlaceUseAvailable: item.inPlaceUseAvailable || false,
|
||||
pricePerHour: item.pricePerHour,
|
||||
pricePerDay: item.pricePerDay,
|
||||
replacementCost: item.replacementCost,
|
||||
location: item.location,
|
||||
pricePerHour: item.pricePerHour || "",
|
||||
pricePerDay: item.pricePerDay || "",
|
||||
replacementCost: item.replacementCost || "",
|
||||
address1: item.address1 || "",
|
||||
address2: item.address2 || "",
|
||||
city: item.city || "",
|
||||
state: item.state || "",
|
||||
zipCode: item.zipCode || "",
|
||||
country: item.country || "US",
|
||||
latitude: item.latitude,
|
||||
longitude: item.longitude,
|
||||
rules: item.rules || "",
|
||||
minimumRentalDays: item.minimumRentalDays,
|
||||
needsTraining: item.needsTraining || false,
|
||||
availability: item.availability,
|
||||
unavailablePeriods: item.unavailablePeriods || [],
|
||||
generalAvailableAfter: item.availableAfter || "09:00",
|
||||
generalAvailableBefore: item.availableBefore || "17:00",
|
||||
specifyTimesPerDay: item.specifyTimesPerDay || false,
|
||||
weeklyTimes: item.weeklyTimes || {
|
||||
sunday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
monday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
tuesday: { availableAfter: "09:00", availableBefore: "17:00" },
|
||||
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" },
|
||||
},
|
||||
});
|
||||
|
||||
// Set existing images as previews
|
||||
@@ -163,12 +214,43 @@ const EditItem: React.FC = () => {
|
||||
|
||||
await itemAPI.updateItem(id!, {
|
||||
...formData,
|
||||
pricePerDay: formData.pricePerDay ? parseFloat(formData.pricePerDay.toString()) : undefined,
|
||||
pricePerHour: formData.pricePerHour ? parseFloat(formData.pricePerHour.toString()) : undefined,
|
||||
replacementCost: formData.replacementCost ? parseFloat(formData.replacementCost.toString()) : 0,
|
||||
pricePerDay: formData.pricePerDay
|
||||
? parseFloat(formData.pricePerDay.toString())
|
||||
: undefined,
|
||||
pricePerHour: formData.pricePerHour
|
||||
? parseFloat(formData.pricePerHour.toString())
|
||||
: undefined,
|
||||
replacementCost: formData.replacementCost
|
||||
? parseFloat(formData.replacementCost.toString())
|
||||
: 0,
|
||||
availableAfter: formData.generalAvailableAfter,
|
||||
availableBefore: formData.generalAvailableBefore,
|
||||
specifyTimesPerDay: formData.specifyTimesPerDay,
|
||||
weeklyTimes: formData.weeklyTimes,
|
||||
images: imageUrls,
|
||||
});
|
||||
|
||||
// Check if user has other items - only save to user profile if no other items
|
||||
try {
|
||||
const userItemsResponse = await itemAPI.getItems({ owner: user?.id });
|
||||
const userItems = userItemsResponse.data.items || [];
|
||||
const hasOtherItems =
|
||||
userItems.filter((item: Item) => item.id !== id).length > 0;
|
||||
|
||||
if (!hasOtherItems) {
|
||||
// Save to user profile - this is still their primary/only item
|
||||
await userAPI.updateAvailability({
|
||||
generalAvailableAfter: formData.generalAvailableAfter,
|
||||
generalAvailableBefore: formData.generalAvailableBefore,
|
||||
specifyTimesPerDay: formData.specifyTimesPerDay,
|
||||
weeklyTimes: formData.weeklyTimes,
|
||||
});
|
||||
}
|
||||
} catch (availabilityError) {
|
||||
console.error("Failed to save availability:", availabilityError);
|
||||
// Don't fail item update if availability save fails
|
||||
}
|
||||
|
||||
setSuccess(true);
|
||||
setTimeout(() => {
|
||||
navigate(`/items/${id}`);
|
||||
@@ -204,6 +286,60 @@ const EditItem: React.FC = () => {
|
||||
setImagePreviews((prev) => prev.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const handleAddressSelect = (addressId: string) => {
|
||||
if (addressId === "new") {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
address1: "",
|
||||
address2: "",
|
||||
city: "",
|
||||
state: "",
|
||||
zipCode: "",
|
||||
country: "US",
|
||||
}));
|
||||
setSelectedAddressId("");
|
||||
} else {
|
||||
const selectedAddress = userAddresses.find(
|
||||
(addr) => addr.id === addressId
|
||||
);
|
||||
if (selectedAddress) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
address1: selectedAddress.address1,
|
||||
address2: selectedAddress.address2 || "",
|
||||
city: selectedAddress.city,
|
||||
state: selectedAddress.state,
|
||||
zipCode: selectedAddress.zipCode,
|
||||
country: selectedAddress.country,
|
||||
latitude: selectedAddress.latitude,
|
||||
longitude: selectedAddress.longitude,
|
||||
}));
|
||||
setSelectedAddressId(addressId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const formatAddressDisplay = (address: Address) => {
|
||||
return `${address.address1}, ${address.city}, ${address.state} ${address.zipCode}`;
|
||||
};
|
||||
|
||||
const handleWeeklyTimeChange = (
|
||||
day: string,
|
||||
field: "availableAfter" | "availableBefore",
|
||||
value: string
|
||||
) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
weeklyTimes: {
|
||||
...prev.weeklyTimes,
|
||||
[day]: {
|
||||
...prev.weeklyTimes[day as keyof typeof prev.weeklyTimes],
|
||||
[field]: value,
|
||||
},
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
@@ -245,386 +381,79 @@ const EditItem: React.FC = () => {
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* Images Card */}
|
||||
<ImageUpload
|
||||
imageFiles={imageFiles}
|
||||
imagePreviews={imagePreviews}
|
||||
onImageChange={handleImageChange}
|
||||
onRemoveImage={removeImage}
|
||||
error={error || ""}
|
||||
/>
|
||||
|
||||
<ItemInformation
|
||||
name={formData.name}
|
||||
description={formData.description}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<LocationForm
|
||||
data={{
|
||||
address1: formData.address1,
|
||||
address2: formData.address2,
|
||||
city: formData.city,
|
||||
state: formData.state,
|
||||
zipCode: formData.zipCode,
|
||||
country: formData.country,
|
||||
latitude: formData.latitude,
|
||||
longitude: formData.longitude,
|
||||
}}
|
||||
userAddresses={userAddresses}
|
||||
selectedAddressId={selectedAddressId}
|
||||
addressesLoading={addressesLoading}
|
||||
onChange={handleChange}
|
||||
onAddressSelect={handleAddressSelect}
|
||||
formatAddressDisplay={formatAddressDisplay}
|
||||
/>
|
||||
|
||||
<DeliveryOptions
|
||||
pickUpAvailable={formData.pickUpAvailable}
|
||||
inPlaceUseAvailable={formData.inPlaceUseAvailable}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<PricingForm
|
||||
priceType={priceType}
|
||||
pricePerHour={formData.pricePerHour || ""}
|
||||
pricePerDay={formData.pricePerDay || ""}
|
||||
replacementCost={formData.replacementCost}
|
||||
minimumRentalDays={formData.minimumRentalDays}
|
||||
onPriceTypeChange={setPriceType}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Images (Max 5)</label>
|
||||
<input
|
||||
type="file"
|
||||
className="form-control"
|
||||
onChange={handleImageChange}
|
||||
accept="image/*"
|
||||
multiple
|
||||
disabled={imagePreviews.length >= 5}
|
||||
/>
|
||||
<div className="form-text">
|
||||
Have pictures of everything that's included
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{imagePreviews.length > 0 && (
|
||||
<div className="row mt-3">
|
||||
{imagePreviews.map((preview, index) => (
|
||||
<div key={index} className="col-6 col-md-4 col-lg-3 mb-3">
|
||||
<div className="position-relative">
|
||||
<img
|
||||
src={preview}
|
||||
alt={`Preview ${index + 1}`}
|
||||
className="img-fluid rounded"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "150px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-danger position-absolute top-0 end-0 m-1"
|
||||
onClick={() => removeImage(index)}
|
||||
>
|
||||
<i className="bi bi-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Basic Information Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="name" className="form-label">
|
||||
Item Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="description" className="form-label">
|
||||
Description *
|
||||
</label>
|
||||
<textarea
|
||||
className="form-control"
|
||||
id="description"
|
||||
name="description"
|
||||
rows={4}
|
||||
value={formData.description}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Location Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="location" className="form-label">
|
||||
Address *
|
||||
</label>
|
||||
<AddressAutocomplete
|
||||
id="location"
|
||||
name="location"
|
||||
value={formData.location}
|
||||
onChange={(value, lat, lon) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
location: value,
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
}));
|
||||
}}
|
||||
placeholder="Enter address"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Delivery & Availability Options Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<label className="form-label">
|
||||
How will renters receive this item?
|
||||
</label>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="pickUpAvailable"
|
||||
name="pickUpAvailable"
|
||||
checked={formData.pickUpAvailable}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="pickUpAvailable">
|
||||
Pick-Up/Drop-off
|
||||
<div className="small text-muted">
|
||||
You and the renter coordinate pick-up and drop-off
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="localDeliveryAvailable"
|
||||
name="localDeliveryAvailable"
|
||||
checked={formData.localDeliveryAvailable}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label d-flex align-items-center"
|
||||
htmlFor="localDeliveryAvailable"
|
||||
>
|
||||
<div>
|
||||
Local Delivery
|
||||
{formData.localDeliveryAvailable && (
|
||||
<span className="ms-2">
|
||||
(Delivery Radius:
|
||||
<input
|
||||
type="number"
|
||||
className="form-control form-control-sm d-inline-block mx-1"
|
||||
id="localDeliveryRadius"
|
||||
name="localDeliveryRadius"
|
||||
value={formData.localDeliveryRadius || ""}
|
||||
onChange={handleChange}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
placeholder="25"
|
||||
min="1"
|
||||
max="100"
|
||||
style={{ width: "60px" }}
|
||||
/>
|
||||
miles)
|
||||
</span>
|
||||
)}
|
||||
<div className="small text-muted">
|
||||
You deliver and then pick-up the item when the rental
|
||||
period ends
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="shippingAvailable"
|
||||
name="shippingAvailable"
|
||||
checked={formData.shippingAvailable}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label"
|
||||
htmlFor="shippingAvailable"
|
||||
>
|
||||
Shipping
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="inPlaceUseAvailable"
|
||||
name="inPlaceUseAvailable"
|
||||
checked={formData.inPlaceUseAvailable}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label"
|
||||
htmlFor="inPlaceUseAvailable"
|
||||
>
|
||||
In-Place Use
|
||||
<div className="small text-muted">
|
||||
They use at your location
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<div className="mb-3">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<label className="col-form-label">Price per</label>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<select
|
||||
className="form-select"
|
||||
value={priceType}
|
||||
onChange={(e) =>
|
||||
setPriceType(e.target.value as "hour" | "day")
|
||||
}
|
||||
>
|
||||
<option value="hour">Hour</option>
|
||||
<option value="day">Day</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="input-group">
|
||||
<span className="input-group-text">$</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
id={
|
||||
priceType === "hour"
|
||||
? "pricePerHour"
|
||||
: "pricePerDay"
|
||||
}
|
||||
name={
|
||||
priceType === "hour"
|
||||
? "pricePerHour"
|
||||
: "pricePerDay"
|
||||
}
|
||||
value={
|
||||
priceType === "hour"
|
||||
? formData.pricePerHour || ""
|
||||
: formData.pricePerDay || ""
|
||||
}
|
||||
onChange={handleChange}
|
||||
step="0.01"
|
||||
min="0"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="minimumRentalDays" className="form-label">
|
||||
Minimum Rental {priceType === "hour" ? "Hours" : "Days"}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
id="minimumRentalDays"
|
||||
name="minimumRentalDays"
|
||||
value={formData.minimumRentalDays}
|
||||
onChange={handleChange}
|
||||
min="1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="replacementCost" className="form-label mb-0">
|
||||
Replacement Cost *
|
||||
</label>
|
||||
<div className="form-text mb-2">
|
||||
The cost to replace the item if lost
|
||||
</div>
|
||||
<div className="input-group">
|
||||
<span className="input-group-text">$</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
id="replacementCost"
|
||||
name="replacementCost"
|
||||
value={formData.replacementCost}
|
||||
onChange={handleChange}
|
||||
step="0.01"
|
||||
min="0"
|
||||
placeholder="0"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Availability Schedule Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<p className="text-muted">
|
||||
Select dates when the item is NOT available for rent. Dates
|
||||
with accepted rentals are shown in purple.
|
||||
</p>
|
||||
<AvailabilityCalendar
|
||||
unavailablePeriods={[
|
||||
...(formData.unavailablePeriods || []),
|
||||
...acceptedRentals.map((rental) => ({
|
||||
id: `rental-${rental.id}`,
|
||||
startDate: new Date(rental.startDate),
|
||||
endDate: new Date(rental.endDate),
|
||||
isAcceptedRental: true,
|
||||
})),
|
||||
]}
|
||||
onPeriodsChange={(periods) => {
|
||||
// Filter out accepted rental periods when saving
|
||||
const userPeriods = periods.filter(
|
||||
(p) => !p.isAcceptedRental
|
||||
);
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
unavailablePeriods: userPeriods,
|
||||
}));
|
||||
<AvailabilitySettings
|
||||
data={{
|
||||
generalAvailableAfter: formData.generalAvailableAfter,
|
||||
generalAvailableBefore: formData.generalAvailableBefore,
|
||||
specifyTimesPerDay: formData.specifyTimesPerDay,
|
||||
weeklyTimes: formData.weeklyTimes,
|
||||
}}
|
||||
mode="owner"
|
||||
/>
|
||||
|
||||
<div className="mt-3 form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="availability"
|
||||
name="availability"
|
||||
checked={formData.availability}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="availability">
|
||||
Available for rent
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rules & Guidelines Card */}
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<div className="form-check mb-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="needsTraining"
|
||||
name="needsTraining"
|
||||
checked={formData.needsTraining}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="needsTraining">
|
||||
Requires in-person training before rental
|
||||
</label>
|
||||
</div>
|
||||
<label htmlFor="rules" className="form-label">
|
||||
Additional Rules
|
||||
</label>
|
||||
<textarea
|
||||
className="form-control"
|
||||
id="rules"
|
||||
name="rules"
|
||||
rows={3}
|
||||
value={formData.rules || ""}
|
||||
onChange={handleChange}
|
||||
placeholder="Any specific rules for renting this item"
|
||||
onChange={(field, value) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
}}
|
||||
onWeeklyTimeChange={handleWeeklyTimeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-grid gap-2">
|
||||
<RulesForm
|
||||
needsTraining={formData.needsTraining}
|
||||
rules={formData.rules || ""}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<div className="d-grid gap-2 mb-5">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary"
|
||||
|
||||
Reference in New Issue
Block a user