feat: add hours, minutes, and price per hour fields to AdhocBookingTab; update booking price calculation

This commit is contained in:
2026-09-30 11:41:53 +05:30
parent 23ae7b39d0
commit 0ba1e6a9e6
2 changed files with 88 additions and 52 deletions
+62 -28
View File
@@ -39,7 +39,9 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
const [facilityId, setFacilityId] = React.useState(adhocFacilities[0]?.facilityId ?? "") const [facilityId, setFacilityId] = React.useState(adhocFacilities[0]?.facilityId ?? "")
const [startTime, setStartTime] = React.useState(() => toLocalInputValue(new Date())) const [startTime, setStartTime] = React.useState(() => toLocalInputValue(new Date()))
const [duration, setDuration] = React.useState("60") const [hours, setHours] = React.useState("1")
const [minutes, setMinutes] = React.useState("0")
const [pricePerHour, setPricePerHour] = React.useState(() => String(adhocFacilities[0]?.pricePerHour ?? 0))
const [partySize, setPartySize] = React.useState("") const [partySize, setPartySize] = React.useState("")
const [guestName, setGuestName] = React.useState("") const [guestName, setGuestName] = React.useState("")
const [guestPhone, setGuestPhone] = React.useState("") const [guestPhone, setGuestPhone] = React.useState("")
@@ -49,13 +51,19 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
const { busy: checking, run: runCheck } = useMutation() const { busy: checking, run: runCheck } = useMutation()
const { busy: booking, run: runBook } = useMutation() const { busy: booking, run: runBook } = useMutation()
const selectedFacility = adhocFacilities.find((f) => f.facilityId === facilityId) const durationMinutes = (Number(hours) || 0) * 60 + (Number(minutes) || 0)
const durationMinutes = Number(duration) || 0 const bookingPrice = ((Number(pricePerHour) || 0) * durationMinutes) / 60
const estimatedPrice = selectedFacility ? (selectedFacility.pricePerHour * durationMinutes) / 60 : 0
const facilityOptions = adhocFacilities.map((f) => ({ label: f.name, value: f.facilityId })) const facilityOptions = adhocFacilities.map((f) => ({ label: f.name, value: f.facilityId }))
const canCheck = !!facilityId && !!startTime && durationMinutes >= 1 && !checking const canCheck = !!facilityId && !!startTime && durationMinutes >= 1 && !checking
const canSubmit = canCheck && !!guestName.trim() && !!guestPhone.trim() && !booking const canSubmit = canCheck && Number(pricePerHour) >= 0 && !!guestName.trim() && !!guestPhone.trim() && !booking
function selectFacility(id: string) {
setFacilityId(id)
setAvailability(null)
const facility = adhocFacilities.find((f) => f.facilityId === id)
setPricePerHour(String(facility?.pricePerHour ?? 0))
}
async function handleCheckAvailability() { async function handleCheckAvailability() {
if (!canCheck) return if (!canCheck) return
@@ -87,6 +95,7 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
guestName: guestName.trim(), guestName: guestName.trim(),
guestPhone: guestPhone.trim(), guestPhone: guestPhone.trim(),
partySize: partySize ? Number(partySize) : undefined, partySize: partySize ? Number(partySize) : undefined,
pricePerHour: Number(pricePerHour),
}) })
) )
if (bookingResult.shareUrl) { if (bookingResult.shareUrl) {
@@ -125,14 +134,7 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
<Label> <Label>
Facility <span className="text-destructive">*</span> Facility <span className="text-destructive">*</span>
</Label> </Label>
<Select <Select items={facilityOptions} value={facilityId} onValueChange={(v) => selectFacility(v ?? "")}>
items={facilityOptions}
value={facilityId}
onValueChange={(v) => {
setFacilityId(v ?? "")
setAvailability(null)
}}
>
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue placeholder="Select facility" /> <SelectValue placeholder="Select facility" />
</SelectTrigger> </SelectTrigger>
@@ -146,7 +148,6 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
</Select> </Select>
</div> </div>
<div className="grid grid-cols-2 gap-3">
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<Label htmlFor="adhoc-start"> <Label htmlFor="adhoc-start">
Start <span className="text-destructive">*</span> Start <span className="text-destructive">*</span>
@@ -162,25 +163,65 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
required required
/> />
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<Label htmlFor="adhoc-duration"> <Label>
Duration (minutes) <span className="text-destructive">*</span> Duration <span className="text-destructive">*</span>
</Label> </Label>
<div className="grid grid-cols-2 gap-3">
<div className="flex items-center gap-2">
<Input <Input
id="adhoc-duration"
type="number" type="number"
min={1} min={0}
value={duration} aria-label="Hours"
value={hours}
onChange={(e) => { onChange={(e) => {
setDuration(e.target.value) setHours(e.target.value)
setAvailability(null) setAvailability(null)
}} }}
required
/> />
<span className="text-sm text-muted-foreground">hours</span>
</div>
<div className="flex items-center gap-2">
<Input
type="number"
min={0}
max={59}
aria-label="Minutes"
value={minutes}
onChange={(e) => {
setMinutes(e.target.value)
setAvailability(null)
}}
/>
<span className="text-sm text-muted-foreground">minutes</span>
</div>
</div> </div>
</div> </div>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="flex flex-col gap-1.5">
<Label htmlFor="adhoc-price-per-hour">
Price / Hour (Rs.) <span className="text-destructive">*</span>
</Label>
<Input
id="adhoc-price-per-hour"
type="number"
min={0}
step="0.01"
value={pricePerHour}
onChange={(e) => setPricePerHour(e.target.value)}
required
/>
</div>
<div className="flex flex-col gap-1.5">
<Label>Price for Booking</Label>
<div className="flex h-9 items-center rounded-md border bg-muted/40 px-3 text-sm font-medium">
Rs. {bookingPrice.toLocaleString(undefined, { maximumFractionDigits: 2 })}
</div>
</div>
</div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<Label htmlFor="adhoc-party-size">Party Size</Label> <Label htmlFor="adhoc-party-size">Party Size</Label>
<Input <Input
@@ -192,13 +233,6 @@ function AdhocBookingTab({ facilities }: AdhocBookingTabProps) {
onChange={(e) => setPartySize(e.target.value)} onChange={(e) => setPartySize(e.target.value)}
/> />
</div> </div>
<div className="flex flex-col gap-1.5">
<Label>Estimated Price</Label>
<div className="flex h-9 items-center rounded-md border bg-muted/40 px-3 text-sm font-medium">
Rs. {estimatedPrice.toLocaleString(undefined, { maximumFractionDigits: 2 })}
</div>
</div>
</div>
<div className="flex items-end gap-3"> <div className="flex items-end gap-3">
<Button type="button" variant="outline" onClick={handleCheckAvailability} disabled={!canCheck}> <Button type="button" variant="outline" onClick={handleCheckAvailability} disabled={!canCheck}>
+2
View File
@@ -67,6 +67,8 @@ export interface CreateGuestAdhocBookingInput {
guestName: string guestName: string
guestPhone: string guestPhone: string
partySize?: number partySize?: number
/** Overrides the facility's own price per hour for this booking's charge only. */
pricePerHour?: number
} }
export interface ReassignBookingInput { export interface ReassignBookingInput {