pricing tiers

This commit is contained in:
jackiettran
2025-11-06 15:54:27 -05:00
parent 9c258177ae
commit 3dca6c803a
14 changed files with 508 additions and 154 deletions

View File

@@ -18,6 +18,8 @@ interface ItemFormData {
inPlaceUseAvailable: boolean;
pricePerHour?: number | string;
pricePerDay?: number | string;
pricePerWeek?: number | string;
pricePerMonth?: number | string;
replacementCost: number | string;
address1: string;
address2: string;
@@ -28,7 +30,6 @@ interface ItemFormData {
latitude?: number;
longitude?: number;
rules?: string;
minimumRentalDays: number;
needsTraining: boolean;
generalAvailableAfter: string;
generalAvailableBefore: string;
@@ -53,12 +54,19 @@ const EditItem: React.FC = () => {
const [success, setSuccess] = useState(false);
const [imageFiles, setImageFiles] = useState<File[]>([]);
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 [selectedPricingUnit, setSelectedPricingUnit] = useState<string>("day");
const [showAdvancedPricing, setShowAdvancedPricing] = useState<boolean>(false);
const [enabledPricingTiers, setEnabledPricingTiers] = useState({
hour: false,
day: false,
week: false,
month: false,
});
// Reference to LocationForm geocoding function
const geocodeLocationRef = useRef<(() => Promise<boolean>) | null>(null);
const [formData, setFormData] = useState<ItemFormData>({
@@ -76,7 +84,6 @@ const EditItem: React.FC = () => {
zipCode: "",
country: "US",
rules: "",
minimumRentalDays: 1,
needsTraining: false,
generalAvailableAfter: "09:00",
generalAvailableBefore: "17:00",
@@ -119,13 +126,6 @@ const EditItem: React.FC = () => {
return;
}
// Set the price type based on available pricing
if (item.pricePerHour) {
setPriceType("hour");
} else if (item.pricePerDay) {
setPriceType("day");
}
// Convert item data to form data format
setFormData({
name: item.name,
@@ -134,6 +134,8 @@ const EditItem: React.FC = () => {
inPlaceUseAvailable: item.inPlaceUseAvailable || false,
pricePerHour: item.pricePerHour || "",
pricePerDay: item.pricePerDay || "",
pricePerWeek: item.pricePerWeek || "",
pricePerMonth: item.pricePerMonth || "",
replacementCost: item.replacementCost || "",
address1: item.address1 || "",
address2: item.address2 || "",
@@ -144,7 +146,6 @@ const EditItem: React.FC = () => {
latitude: item.latitude,
longitude: item.longitude,
rules: item.rules || "",
minimumRentalDays: item.minimumRentalDays,
needsTraining: item.needsTraining || false,
generalAvailableAfter: item.availableAfter || "09:00",
generalAvailableBefore: item.availableBefore || "17:00",
@@ -164,6 +165,40 @@ const EditItem: React.FC = () => {
if (item.images && item.images.length > 0) {
setImagePreviews(item.images);
}
// Determine which pricing unit to select based on existing data
// Priority: hour -> day -> week -> month (first one with a value)
if (item.pricePerHour) {
setSelectedPricingUnit("hour");
} else if (item.pricePerDay) {
setSelectedPricingUnit("day");
} else if (item.pricePerWeek) {
setSelectedPricingUnit("week");
} else if (item.pricePerMonth) {
setSelectedPricingUnit("month");
} else {
setSelectedPricingUnit("day"); // Default to day if no pricing is set
}
// Set enabled tiers based on which prices are populated
setEnabledPricingTiers({
hour: !!(item.pricePerHour && Number(item.pricePerHour) > 0),
day: !!(item.pricePerDay && Number(item.pricePerDay) > 0),
week: !!(item.pricePerWeek && Number(item.pricePerWeek) > 0),
month: !!(item.pricePerMonth && Number(item.pricePerMonth) > 0),
});
// Auto-expand advanced section if multiple pricing tiers are set
const pricingTiersSet = [
item.pricePerHour,
item.pricePerDay,
item.pricePerWeek,
item.pricePerMonth,
].filter((price) => price && Number(price) > 0).length;
if (pricingTiersSet > 1) {
setShowAdvancedPricing(true);
}
} catch (err: any) {
setError(err.response?.data?.message || "Failed to fetch item");
} finally {
@@ -240,6 +275,12 @@ const EditItem: React.FC = () => {
pricePerHour: formData.pricePerHour
? parseFloat(formData.pricePerHour.toString())
: undefined,
pricePerWeek: formData.pricePerWeek
? parseFloat(formData.pricePerWeek.toString())
: undefined,
pricePerMonth: formData.pricePerMonth
? parseFloat(formData.pricePerMonth.toString())
: undefined,
replacementCost: formData.replacementCost
? parseFloat(formData.replacementCost.toString())
: 0,
@@ -360,6 +401,21 @@ const EditItem: React.FC = () => {
}));
};
const handlePricingUnitChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedPricingUnit(e.target.value);
};
const handleToggleAdvancedPricing = () => {
setShowAdvancedPricing((prev) => !prev);
};
const handleTierToggle = (tier: string) => {
setEnabledPricingTiers((prev) => ({
...prev,
[tier]: !prev[tier as keyof typeof prev],
}));
};
if (loading) {
return (
<div className="container mt-5">
@@ -445,13 +501,18 @@ const EditItem: React.FC = () => {
/>
<PricingForm
priceType={priceType}
pricePerHour={formData.pricePerHour || ""}
pricePerDay={formData.pricePerDay || ""}
pricePerWeek={formData.pricePerWeek || ""}
pricePerMonth={formData.pricePerMonth || ""}
replacementCost={formData.replacementCost}
minimumRentalDays={formData.minimumRentalDays}
onPriceTypeChange={setPriceType}
selectedPricingUnit={selectedPricingUnit}
showAdvancedPricing={showAdvancedPricing}
enabledTiers={enabledPricingTiers}
onChange={handleChange}
onPricingUnitChange={handlePricingUnitChange}
onToggleAdvancedPricing={handleToggleAdvancedPricing}
onTierToggle={handleTierToggle}
/>
<div className="card mb-4">