Files
web/packages/booking-flow/lib/components/BookingWidget/BookingWidgetForm/index.tsx
2026-01-28 12:02:42 +00:00

138 lines
3.8 KiB
TypeScript

"use client"
import { usePathname, useRouter } from "next/navigation"
import { useTransition } from "react"
import { Form as FormRAC } from "react-aria-components"
import { useFormContext } from "react-hook-form"
import {
selectHotel,
selectHotelMap,
selectRate,
} from "@scandic-hotels/common/constants/routes/hotelReservation"
import { trackBookingSearchClick } from "@scandic-hotels/tracking/booking"
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { setBookingWidgetState } from "../../../hooks/useBookingWidgetState"
import useLang from "../../../hooks/useLang"
import {
BookingCodeFilterEnum,
useBookingCodeFilterStore,
} from "../../../stores/bookingCode-filter"
import { serializeBookingSearchParams } from "../../../utils/url"
import FormContent, { BookingWidgetFormContentSkeleton } from "./FormContent"
import { bookingWidgetVariants } from "./variants"
import styles from "./form.module.css"
import type { BookingWidgetType } from ".."
import type { BookingWidgetSchema } from "../Client"
const formId = "booking-widget"
type BookingWidgetFormProps = {
type?: BookingWidgetType
isFloating: boolean
onClose: () => void
}
export default function Form({
type,
isFloating,
onClose,
}: BookingWidgetFormProps) {
const router = useRouter()
const pathname = usePathname()
const lang = useLang()
const [isPending, startTransition] = useTransition()
const setBookingCodeFilter = useBookingCodeFilterStore(
(state) => state.setFilter
)
const classNames = bookingWidgetVariants({
type,
})
const { handleSubmit, setValue, reset } =
useFormContext<BookingWidgetSchema>()
function onSubmit(data: BookingWidgetSchema) {
trackBookingSearchClick(data.search, data.hotel ? "hotel" : "destination")
const isMapView = pathname.endsWith("/map")
const bookingFlowPage = data.hotel
? selectRate(lang)
: isMapView
? selectHotelMap(lang)
: selectHotel(lang)
const bookingWidgetParams = serializeBookingSearchParams({
rooms: data.rooms,
...data.date,
...(data.city ? { city: data.city } : {}),
...(data.hotel ? { hotel: data.hotel } : {}),
...(data.bookingCode?.value
? { bookingCode: data.bookingCode.value }
: {}),
// Followed current url structure to keep searchtype=redemption param incase of reward night
...(data.redemption ? { searchType: SEARCH_TYPE_REDEMPTION } : {}),
})
setBookingWidgetState({
fromDate: data.date.fromDate,
toDate: data.date.toDate,
rooms: data.rooms,
})
onClose()
startTransition(() => {
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
})
if (data.bookingCode?.value) {
// Reset the booking code filter if changed by user to "All rates"
setBookingCodeFilter(BookingCodeFilterEnum.Discounted)
if (data.bookingCode.remember) {
localStorage.setItem("bookingCode", JSON.stringify(data.bookingCode))
}
} else {
setValue("bookingCode.remember", false, {
shouldDirty: true,
})
localStorage.removeItem("bookingCode")
}
reset(data)
}
return (
<section className={classNames}>
<FormRAC
onSubmit={handleSubmit(onSubmit)}
className={styles.form}
id={formId}
>
<FormContent
formId={formId}
onSubmit={handleSubmit(onSubmit)}
isSearching={isPending}
isFloating={isFloating}
/>
</FormRAC>
</section>
)
}
export function BookingWidgetFormSkeleton({
type,
}: {
type: BookingWidgetType
}) {
const classNames = bookingWidgetVariants({
type,
})
return (
<section className={classNames}>
<form className={styles.form}>
<BookingWidgetFormContentSkeleton />
</form>
</section>
)
}