fixed sticky bottom pricing card for mobile
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
|
||||
@@ -64,6 +64,7 @@ main {
|
||||
right: 0;
|
||||
background: white;
|
||||
padding: 12px 16px;
|
||||
padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
@@ -74,7 +75,7 @@ main {
|
||||
/* Make sticky card non-sticky on mobile */
|
||||
.sticky-pricing-card {
|
||||
position: static !important;
|
||||
margin-bottom: 80px;
|
||||
margin-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -525,10 +525,235 @@ const ItemDetail: React.FC = () => {
|
||||
<div>Replacement Cost: ${item.replacementCost}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Pricing Card - shown inline on mobile */}
|
||||
<div className="d-md-none mb-4">
|
||||
<div className="card sticky-pricing-card">
|
||||
<div className="card-body text-center">
|
||||
{(() => {
|
||||
const hasAnyPositivePrice =
|
||||
(item.pricePerHour !== undefined &&
|
||||
Number(item.pricePerHour) > 0) ||
|
||||
(item.pricePerDay !== undefined &&
|
||||
Number(item.pricePerDay) > 0) ||
|
||||
(item.pricePerWeek !== undefined &&
|
||||
Number(item.pricePerWeek) > 0) ||
|
||||
(item.pricePerMonth !== undefined &&
|
||||
Number(item.pricePerMonth) > 0);
|
||||
|
||||
const hasAnyZeroPrice =
|
||||
(item.pricePerHour !== undefined &&
|
||||
Number(item.pricePerHour) === 0) ||
|
||||
(item.pricePerDay !== undefined &&
|
||||
Number(item.pricePerDay) === 0) ||
|
||||
(item.pricePerWeek !== undefined &&
|
||||
Number(item.pricePerWeek) === 0) ||
|
||||
(item.pricePerMonth !== undefined &&
|
||||
Number(item.pricePerMonth) === 0);
|
||||
|
||||
if (!hasAnyPositivePrice && hasAnyZeroPrice) {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<h4>Free to Borrow</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{item.pricePerHour !== undefined &&
|
||||
Number(item.pricePerHour) > 0 && (
|
||||
<div className="mb-2">
|
||||
<h4>
|
||||
${Math.floor(Number(item.pricePerHour))}/Hour
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
{item.pricePerDay !== undefined &&
|
||||
Number(item.pricePerDay) > 0 && (
|
||||
<div className="mb-2">
|
||||
<h4>
|
||||
${Math.floor(Number(item.pricePerDay))}/Day
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
{item.pricePerWeek !== undefined &&
|
||||
Number(item.pricePerWeek) > 0 && (
|
||||
<div className="mb-2">
|
||||
<h4>
|
||||
${Math.floor(Number(item.pricePerWeek))}/Week
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
{item.pricePerMonth !== undefined &&
|
||||
Number(item.pricePerMonth) > 0 && (
|
||||
<div className="mb-2">
|
||||
<h4>
|
||||
${Math.floor(Number(item.pricePerMonth))}/Month
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Rental Period Selection - Only show for non-owners */}
|
||||
{!isOwner && item.isAvailable && !isAlreadyRenting && (
|
||||
<>
|
||||
<hr />
|
||||
<div className="text-start">
|
||||
<div className="mb-3">
|
||||
<label className="form-label fw-medium mb-2">Start</label>
|
||||
<div className="input-group input-group-lg">
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
value={rentalDates.startDate}
|
||||
onChange={(e) =>
|
||||
handleDateTimeChange(
|
||||
"startDate",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
min={new Date().toLocaleDateString()}
|
||||
style={{ flex: "1 1 50%" }}
|
||||
/>
|
||||
<select
|
||||
className="form-select time-select"
|
||||
value={rentalDates.startTime}
|
||||
onChange={(e) =>
|
||||
handleDateTimeChange(
|
||||
"startTime",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
style={{ flex: "1 1 50%" }}
|
||||
disabled={
|
||||
!!(
|
||||
rentalDates.startDate &&
|
||||
generateTimeOptions(
|
||||
item,
|
||||
rentalDates.startDate
|
||||
).every(
|
||||
(opt) => opt.label === "Not Available"
|
||||
)
|
||||
)
|
||||
}
|
||||
>
|
||||
{generateTimeOptions(
|
||||
item,
|
||||
rentalDates.startDate
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="form-label fw-medium mb-2">End</label>
|
||||
<div className="input-group input-group-lg">
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
value={rentalDates.endDate}
|
||||
onChange={(e) =>
|
||||
handleDateTimeChange("endDate", e.target.value)
|
||||
}
|
||||
min={
|
||||
rentalDates.startDate ||
|
||||
new Date().toLocaleDateString()
|
||||
}
|
||||
style={{ flex: "1 1 50%" }}
|
||||
/>
|
||||
<select
|
||||
className="form-select time-select"
|
||||
value={rentalDates.endTime}
|
||||
onChange={(e) =>
|
||||
handleDateTimeChange("endTime", e.target.value)
|
||||
}
|
||||
style={{ flex: "1 1 50%" }}
|
||||
disabled={
|
||||
!!(
|
||||
(rentalDates.endDate ||
|
||||
rentalDates.startDate) &&
|
||||
generateTimeOptions(
|
||||
item,
|
||||
rentalDates.endDate || rentalDates.startDate
|
||||
).every(
|
||||
(opt) => opt.label === "Not Available"
|
||||
)
|
||||
)
|
||||
}
|
||||
>
|
||||
{generateTimeOptions(
|
||||
item,
|
||||
rentalDates.endDate || rentalDates.startDate
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{rentalDates.startDate && rentalDates.endDate && (
|
||||
<div className="mb-3 p-2 bg-light rounded text-center">
|
||||
{costLoading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">
|
||||
Calculating...
|
||||
</span>
|
||||
</div>
|
||||
) : costError ? (
|
||||
<small className="text-danger">{costError}</small>
|
||||
) : totalCost > 0 ? (
|
||||
<strong>Total: ${totalCost}</strong>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
{!isOwner && item.isAvailable && !isAlreadyRenting && (
|
||||
<div className="d-grid">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={handleRent}
|
||||
disabled={
|
||||
!rentalDates.startDate || !rentalDates.endDate
|
||||
}
|
||||
>
|
||||
Rent Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!isOwner && isAlreadyRenting && (
|
||||
<div className="d-grid">
|
||||
<button
|
||||
className="btn btn-success"
|
||||
disabled
|
||||
style={{ opacity: 0.8 }}
|
||||
>
|
||||
✓ Renting
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Sticky Pricing Card */}
|
||||
<div className="col-md-4">
|
||||
{/* Right Side - Sticky Pricing Card (hidden on mobile, shown on desktop) */}
|
||||
<div className="col-md-4 d-none d-md-block">
|
||||
<div
|
||||
className="card sticky-pricing-card"
|
||||
id="pricing-card"
|
||||
|
||||
Reference in New Issue
Block a user