chore: Cleanup booking widget with types and other minor issues
This commit is contained in:
@@ -97,7 +97,7 @@ export default function BookingWidgetClient({
|
||||
const defaultRoomsData: BookingWidgetSchema["rooms"] = params.rooms?.map(
|
||||
(room) => ({
|
||||
adults: room.adults,
|
||||
childrenInRoom: room.childrenInRoom ?? [],
|
||||
childrenInRoom: room.childrenInRoom,
|
||||
})
|
||||
) ?? [
|
||||
{
|
||||
@@ -149,21 +149,18 @@ export default function BookingWidgetClient({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const debouncedResizeHandler = debounce(function ([
|
||||
entry,
|
||||
]: ResizeObserverEntry[]) {
|
||||
if (entry.contentRect.width > 1366) {
|
||||
closeMobileSearch()
|
||||
}
|
||||
})
|
||||
const observer = new ResizeObserver(debouncedResizeHandler)
|
||||
const observer = new ResizeObserver(
|
||||
debounce(([entry]) => {
|
||||
if (entry.contentRect.width > 1366) {
|
||||
closeMobileSearch()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
observer.observe(document.body)
|
||||
|
||||
return () => {
|
||||
if (observer) {
|
||||
observer.unobserve(document.body)
|
||||
}
|
||||
observer.unobserve(document.body)
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -176,13 +173,15 @@ export default function BookingWidgetClient({
|
||||
? JSON.parse(sessionStorageSearchData)
|
||||
: undefined
|
||||
|
||||
initialSelectedLocation?.name &&
|
||||
if (initialSelectedLocation?.name) {
|
||||
methods.setValue("search", initialSelectedLocation.name)
|
||||
sessionStorageSearchData &&
|
||||
}
|
||||
if (sessionStorageSearchData) {
|
||||
methods.setValue(
|
||||
"location",
|
||||
encodeURIComponent(sessionStorageSearchData)
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [methods, selectedLocation])
|
||||
|
||||
|
||||
@@ -27,21 +27,23 @@ export default function MobileToggleButton({
|
||||
}: BookingWidgetToggleButtonProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const d = useWatch({ name: "date" })
|
||||
const location = useWatch({ name: "location" })
|
||||
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
||||
const date = useWatch<BookingWidgetSchema, "date">({ name: "date" })
|
||||
const location = useWatch<BookingWidgetSchema, "location">({
|
||||
name: "location",
|
||||
})
|
||||
const rooms = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })
|
||||
|
||||
const parsedLocation: Location | null =
|
||||
location && isValidJson(location)
|
||||
? JSON.parse(decodeURIComponent(location))
|
||||
: null
|
||||
|
||||
const selectedFromDate = dt(d.fromDate).locale(lang).format("D MMM")
|
||||
const selectedToDate = dt(d.toDate).locale(lang).format("D MMM")
|
||||
const selectedFromDate = dt(date.fromDate).locale(lang).format("D MMM")
|
||||
const selectedToDate = dt(date.toDate).locale(lang).format("D MMM")
|
||||
|
||||
const locationAndDateIsSet = parsedLocation && d
|
||||
const locationAndDateIsSet = parsedLocation && date
|
||||
|
||||
const totalNights = dt(d.toDate).diff(dt(d.fromDate), "days")
|
||||
const totalNights = dt(date.toDate).diff(dt(date.fromDate), "days")
|
||||
const totalRooms = rooms.length
|
||||
const totalAdults = rooms.reduce((acc, room) => {
|
||||
if (room.adults) {
|
||||
|
||||
@@ -27,15 +27,13 @@ import {
|
||||
type SetStorageData,
|
||||
} from "@/types/components/form/bookingwidget"
|
||||
import type { SearchHistoryItem, SearchProps } from "@/types/components/search"
|
||||
import type { Location, Locations } from "@/types/trpc/routers/hotel/locations"
|
||||
|
||||
const name = "search"
|
||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||
|
||||
export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
const { register, setValue, unregister, getValues } =
|
||||
useFormContext<BookingWidgetSchema>()
|
||||
const intl = useIntl()
|
||||
const value = useWatch({ name })
|
||||
const value = useWatch<BookingWidgetSchema, "search">({ name: "search" })
|
||||
const locationString = getValues("location")
|
||||
const location =
|
||||
locationString && isValidJson(decodeURIComponent(locationString))
|
||||
@@ -75,7 +73,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
evt: FormEvent<HTMLInputElement> | ChangeEvent<HTMLInputElement>
|
||||
) {
|
||||
const newValue = evt.currentTarget.value
|
||||
setValue(name, newValue)
|
||||
setValue("search", newValue)
|
||||
dispatchInputValue(value)
|
||||
}
|
||||
|
||||
@@ -97,7 +95,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
const stringified = JSON.stringify(selectedItem)
|
||||
setValue("location", encodeURIComponent(stringified))
|
||||
sessionStorage.setItem(sessionStorageKey, stringified)
|
||||
setValue(name, selectedItem.name)
|
||||
setValue("search", selectedItem.name)
|
||||
const newHistoryItem: SearchHistoryItem = {
|
||||
type: selectedItem.type,
|
||||
id: selectedItem.id,
|
||||
@@ -114,7 +112,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
const searchHistory = [newHistoryItem, ...oldHistoryItems]
|
||||
localStorage.setItem(localStorageKey, JSON.stringify(searchHistory))
|
||||
|
||||
const enhancedSearchHistory: Locations = [
|
||||
const enhancedSearchHistory: Location[] = [
|
||||
...getEnhancedSearchHistory([newHistoryItem], locations),
|
||||
...oldSearchHistoryWithoutTheNew,
|
||||
]
|
||||
@@ -213,7 +211,10 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
// Adding hidden input to define hotel or city based on destination selection for basic form submit.
|
||||
<input type="hidden" {...register(stayType)} />
|
||||
) : null}
|
||||
<label {...getLabelProps({ htmlFor: name })} className={styles.label}>
|
||||
<label
|
||||
{...getLabelProps({ htmlFor: "search" })}
|
||||
className={styles.label}
|
||||
>
|
||||
<Caption
|
||||
type="bold"
|
||||
color={isOpen ? "uiTextActive" : "red"}
|
||||
@@ -226,7 +227,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
<label className={styles.searchInput}>
|
||||
<Input
|
||||
{...getInputProps({
|
||||
id: name,
|
||||
id: "search",
|
||||
onFocus(evt) {
|
||||
handleOnFocus(evt)
|
||||
openMenu()
|
||||
@@ -234,7 +235,7 @@ export default function Search({ locations, handlePressEnter }: SearchProps) {
|
||||
placeholder: intl.formatMessage({
|
||||
id: "Hotels & Destinations",
|
||||
}),
|
||||
...register(name, {
|
||||
...register("search", {
|
||||
onChange: handleOnChange,
|
||||
}),
|
||||
onKeyDown: (e) => {
|
||||
@@ -285,8 +286,8 @@ export function SearchSkeleton() {
|
||||
*/
|
||||
function getEnhancedSearchHistory(
|
||||
searchHistory: SearchHistoryItem[],
|
||||
locations: Locations
|
||||
): Locations {
|
||||
locations: Location[]
|
||||
): Location[] {
|
||||
return searchHistory
|
||||
.map((historyItem) =>
|
||||
locations.find(
|
||||
@@ -294,5 +295,5 @@ function getEnhancedSearchHistory(
|
||||
location.type === historyItem.type && location.id === historyItem.id
|
||||
)
|
||||
)
|
||||
.filter((r) => !!r) as Locations
|
||||
.filter((r): r is Location => !!r)
|
||||
}
|
||||
|
||||
@@ -31,10 +31,8 @@ export default function FormContent({
|
||||
const {
|
||||
formState: { errors },
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
const bookingCodeError = errors["bookingCode"]?.value
|
||||
const selectedDate = useWatch({ name: "date" })
|
||||
|
||||
const roomsLabel = intl.formatMessage({ id: "Rooms & Guests" })
|
||||
const selectedDate = useWatch({ name: "date" })
|
||||
|
||||
const nights = dt(selectedDate.toDate).diff(dt(selectedDate.fromDate), "days")
|
||||
|
||||
@@ -63,7 +61,7 @@ export default function FormContent({
|
||||
<div className={styles.rooms}>
|
||||
<label>
|
||||
<Caption color="red" type="bold" asChild>
|
||||
<span>{roomsLabel}</span>
|
||||
<span>{intl.formatMessage({ id: "Rooms & Guests" })}</span>
|
||||
</Caption>
|
||||
</label>
|
||||
<GuestsRoomsPickerForm />
|
||||
@@ -86,7 +84,7 @@ export default function FormContent({
|
||||
<Voucher />
|
||||
</div>
|
||||
<div className={`${styles.buttonContainer} ${styles.hideOnTablet}`}>
|
||||
{bookingCodeError?.message?.indexOf("Multi-room") === 0 ? (
|
||||
{errors.bookingCode?.value?.message?.indexOf("Multi-room") === 0 ? (
|
||||
<RemoveExtraRooms
|
||||
size="medium"
|
||||
fullWidth
|
||||
|
||||
@@ -15,7 +15,7 @@ import { GuestsRoom } from "./GuestsRoom"
|
||||
import styles from "./guests-rooms-picker.module.css"
|
||||
|
||||
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||
import type { TGuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
import type { GuestsRoom as TGuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
|
||||
const MAX_ROOMS = 4
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ import ChildSelector from "../ChildSelector"
|
||||
import styles from "../guests-rooms-picker.module.css"
|
||||
|
||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||
import type { TGuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
import type { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
|
||||
export function GuestsRoom({
|
||||
room,
|
||||
index,
|
||||
onRemove,
|
||||
}: {
|
||||
room: TGuestsRoom
|
||||
room: GuestsRoom
|
||||
index: number
|
||||
onRemove: (index: number) => void
|
||||
}) {
|
||||
@@ -54,22 +54,18 @@ export function GuestsRoom({
|
||||
childrenInAdultsBed={childrenInAdultsBed}
|
||||
/>
|
||||
{index !== 0 && (
|
||||
<div className={styles.roomActions}>
|
||||
<Button
|
||||
intent="text"
|
||||
variant="icon"
|
||||
wrapping
|
||||
theme="secondaryLight"
|
||||
onPress={() => onRemove(index)}
|
||||
size="small"
|
||||
className={styles.roomActionsButton}
|
||||
>
|
||||
<MaterialIcon icon="delete" color="CurrentColor" />
|
||||
<span className={styles.roomActionsLabel}>
|
||||
{intl.formatMessage({ id: "Remove room" })}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
intent="text"
|
||||
variant="icon"
|
||||
wrapping
|
||||
theme="secondaryLight"
|
||||
onPress={() => onRemove(index)}
|
||||
size="small"
|
||||
className={styles.roomActionsButton}
|
||||
>
|
||||
<MaterialIcon icon="delete" color="CurrentColor" />
|
||||
{intl.formatMessage({ id: "Remove room" })}
|
||||
</Button>
|
||||
)}
|
||||
</section>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { useCallback, useEffect, useId, useState } from "react"
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Modal,
|
||||
Popover,
|
||||
} from "react-aria-components"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { useFormContext, useWatch } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
@@ -18,23 +18,23 @@ import PickerForm from "./Form"
|
||||
|
||||
import styles from "./guests-rooms-picker.module.css"
|
||||
|
||||
import type { TGuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||
import type { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
||||
|
||||
export default function GuestsRoomsPickerForm() {
|
||||
const { watch, trigger } = useFormContext()
|
||||
const rooms = watch("rooms") as TGuestsRoom[]
|
||||
const { trigger } = useFormContext<BookingWidgetSchema>()
|
||||
const rooms = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })
|
||||
|
||||
const popoverId = useId()
|
||||
const checkIsDesktop = useMediaQuery("(min-width: 1367px)")
|
||||
const [isDesktop, setIsDesktop] = useState(true)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [containerHeight, setContainerHeight] = useState(0)
|
||||
const childCount =
|
||||
rooms[0] && rooms[0].childrenInRoom ? rooms[0].childrenInRoom.length : 0 // ToDo Update for multiroom later
|
||||
const childCount = rooms[0]?.childrenInRoom.length ?? 0 // ToDo Update for multiroom later
|
||||
|
||||
const htmlElement =
|
||||
typeof window !== "undefined" ? document.querySelector("body") : null
|
||||
//isOpen is the 'old state', so isOpen === true means "The modal is open and WILL be closed".
|
||||
async function setOverflowClip(isOpen: boolean) {
|
||||
const htmlElement = document.body
|
||||
if (htmlElement) {
|
||||
if (isOpen) {
|
||||
htmlElement.style.overflow = "visible"
|
||||
@@ -57,34 +57,32 @@ export default function GuestsRoomsPickerForm() {
|
||||
}, [checkIsDesktop])
|
||||
|
||||
const updateHeight = useCallback(() => {
|
||||
if (typeof window !== undefined) {
|
||||
// Get available space for picker to show without going beyond screen
|
||||
let maxHeight =
|
||||
window.innerHeight -
|
||||
(document.querySelector("#booking-widget")?.getBoundingClientRect()
|
||||
.bottom ?? 0) -
|
||||
50
|
||||
const innerContainerHeight = document
|
||||
.querySelector(".guests_picker_popover")
|
||||
?.getBoundingClientRect().height
|
||||
if (
|
||||
maxHeight != containerHeight &&
|
||||
innerContainerHeight &&
|
||||
maxHeight <= innerContainerHeight
|
||||
) {
|
||||
setContainerHeight(maxHeight)
|
||||
} else if (
|
||||
containerHeight &&
|
||||
innerContainerHeight &&
|
||||
maxHeight > innerContainerHeight
|
||||
) {
|
||||
setContainerHeight(0)
|
||||
}
|
||||
// Get available space for picker to show without going beyond screen
|
||||
const bookingWidget = document.getElementById("booking-widget")
|
||||
let maxHeight =
|
||||
window.innerHeight -
|
||||
(bookingWidget?.getBoundingClientRect().bottom ?? 0) -
|
||||
50
|
||||
const innerContainerHeight = document
|
||||
.getElementsByClassName(popoverId)[0]
|
||||
?.getBoundingClientRect().height
|
||||
if (
|
||||
maxHeight != containerHeight &&
|
||||
innerContainerHeight &&
|
||||
maxHeight <= innerContainerHeight
|
||||
) {
|
||||
setContainerHeight(maxHeight)
|
||||
} else if (
|
||||
containerHeight &&
|
||||
innerContainerHeight &&
|
||||
maxHeight > innerContainerHeight
|
||||
) {
|
||||
setContainerHeight(0)
|
||||
}
|
||||
}, [containerHeight])
|
||||
}, [containerHeight, popoverId])
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== undefined && isDesktop && rooms.length > 0) {
|
||||
if (isDesktop && rooms.length > 0) {
|
||||
updateHeight()
|
||||
}
|
||||
}, [childCount, isDesktop, updateHeight, rooms])
|
||||
@@ -99,10 +97,10 @@ export default function GuestsRoomsPickerForm() {
|
||||
}}
|
||||
/>
|
||||
<Popover
|
||||
className="guests_picker_popover"
|
||||
className={popoverId}
|
||||
placement="bottom start"
|
||||
offset={36}
|
||||
style={containerHeight ? { overflow: "auto" } : {}}
|
||||
style={containerHeight ? { overflow: "auto" } : undefined}
|
||||
>
|
||||
<Dialog className={styles.pickerContainerDesktop}>
|
||||
{({ close }) => <PickerForm rooms={rooms} onClose={close} />}
|
||||
@@ -132,7 +130,7 @@ function Trigger({
|
||||
className,
|
||||
triggerFn,
|
||||
}: {
|
||||
rooms: TGuestsRoom[]
|
||||
rooms: GuestsRoom[]
|
||||
className: string
|
||||
triggerFn?: () => void
|
||||
}) {
|
||||
|
||||
@@ -5,7 +5,7 @@ export type ChildBed = {
|
||||
value: number
|
||||
}
|
||||
|
||||
export type TGuestsRoom = Required<Pick<Room, "adults" | "childrenInRoom">>
|
||||
export type GuestsRoom = Required<Pick<Room, "adults" | "childrenInRoom">>
|
||||
|
||||
export type GuestsRoomPickerProps = {
|
||||
index: number
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
bookingWidgetSchema,
|
||||
} from "@/components/Forms/BookingWidget/schema"
|
||||
import type { bookingWidgetVariants } from "@/components/Forms/BookingWidget/variants"
|
||||
import type { TGuestsRoom } from "./guestsRoomsPicker"
|
||||
import type { GuestsRoom } from "./guestsRoomsPicker"
|
||||
|
||||
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
||||
export type BookingCodeSchema = z.output<typeof bookingCodeSchema>
|
||||
@@ -17,7 +17,7 @@ export type BookingWidgetSearchData = {
|
||||
hotel?: string
|
||||
fromDate?: string
|
||||
toDate?: string
|
||||
rooms?: TGuestsRoom[]
|
||||
rooms?: GuestsRoom[]
|
||||
bookingCode?: string
|
||||
searchType?: "redemption"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user