Merged in feat/BOOK-377-keep-booking-widget-state (pull request #2894)
feat(BOOK-377): keep dates and rooms & guest in session storage * feat(BOOK-377): keep dates and rooms & guest in session storage * feat(BOOK-377): extract to hook and reuse Approved-by: Erik Tiekstra
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import { useBookingWidgetState } from "@scandic-hotels/booking-flow/hooks/useBookingWidgetState"
|
||||
import { serializeBookingSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { selectRateWithParams } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||
|
||||
import useLang from "@/hooks/useLang"
|
||||
@@ -15,9 +16,15 @@ export default function PricesAndAvailabilityButton({
|
||||
label,
|
||||
}: PricesAndAvailabilityProps) {
|
||||
const lang = useLang()
|
||||
const fromdate = dt().format("YYYY-MM-DD")
|
||||
const todate = dt().add(1, "day").format("YYYY-MM-DD")
|
||||
const selectRateURL = selectRateWithParams(lang, hotelId, fromdate, todate)
|
||||
const { fromDate, toDate, rooms } = useBookingWidgetState()
|
||||
|
||||
const params = serializeBookingSearchParams({
|
||||
hotelId,
|
||||
fromDate,
|
||||
toDate,
|
||||
rooms,
|
||||
})
|
||||
const selectRateURL = selectRateWithParams(lang, params)
|
||||
|
||||
const { name, roomTypes } = room
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
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,
|
||||
@@ -68,7 +69,11 @@ export default function Form({ type, onClose }: BookingWidgetFormProps) {
|
||||
// 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()}`)
|
||||
|
||||
@@ -15,6 +15,7 @@ import { trpc } from "@scandic-hotels/trpc/client"
|
||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||
|
||||
import { useBookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfigContext"
|
||||
import { useBookingWidgetState } from "../../hooks/useBookingWidgetState"
|
||||
import useLang from "../../hooks/useLang"
|
||||
import {
|
||||
type bookingCodeSchema,
|
||||
@@ -54,6 +55,7 @@ export default function BookingWidgetClient({
|
||||
null
|
||||
)
|
||||
const { bookingCodeEnabled } = useBookingFlowConfig()
|
||||
const storedBookingWidgetState = useBookingWidgetState()
|
||||
|
||||
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
|
||||
|
||||
@@ -207,6 +209,24 @@ export default function BookingWidgetClient({
|
||||
}
|
||||
}, [methods, selectedBookingCode])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!data.fromDate &&
|
||||
!data.toDate &&
|
||||
!data.rooms &&
|
||||
storedBookingWidgetState
|
||||
) {
|
||||
methods.reset({
|
||||
...methods.getValues(),
|
||||
date: {
|
||||
fromDate: storedBookingWidgetState.fromDate,
|
||||
toDate: storedBookingWidgetState.toDate,
|
||||
},
|
||||
rooms: storedBookingWidgetState.rooms,
|
||||
})
|
||||
}
|
||||
}, [data, methods, storedBookingWidgetState])
|
||||
|
||||
if (shouldShowSkeleton) {
|
||||
return <BookingWidgetSkeleton type={type} />
|
||||
}
|
||||
|
||||
92
packages/booking-flow/lib/hooks/useBookingWidgetState.ts
Normal file
92
packages/booking-flow/lib/hooks/useBookingWidgetState.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { z } from "zod"
|
||||
|
||||
import { BOOKING_WIDGET_STATE } from "@scandic-hotels/common/constants/bookingWidget"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
|
||||
import { adultsSchema, childAgeSchema, childBedSchema } from "../utils/url"
|
||||
|
||||
const DEFAULT_ROOMS = [{ adults: 1, childrenInRoom: [] }]
|
||||
|
||||
const stateSchema = z.object({
|
||||
fromDate: z.string(),
|
||||
toDate: z.string(),
|
||||
rooms: z.array(
|
||||
z.object({
|
||||
adults: adultsSchema,
|
||||
childrenInRoom: z
|
||||
.array(
|
||||
z.object({
|
||||
bed: childBedSchema,
|
||||
age: childAgeSchema,
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
type BookingWidgetState = z.infer<typeof stateSchema>
|
||||
|
||||
export function useBookingWidgetState() {
|
||||
const [bookingState, setBookingState] = useState<BookingWidgetState>({
|
||||
fromDate: dt().format("YYYY-MM-DD"),
|
||||
toDate: dt().add(1, "day").format("YYYY-MM-DD"),
|
||||
rooms: DEFAULT_ROOMS,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const now = dt()
|
||||
const stored = getBookingWidgetState()
|
||||
if (!stored) {
|
||||
return
|
||||
}
|
||||
const storedFrom = dt(stored.fromDate)
|
||||
const storedTo = dt(stored.toDate)
|
||||
|
||||
const isDateParamValid =
|
||||
storedFrom.isValid() &&
|
||||
storedTo.isValid() &&
|
||||
storedFrom.isSameOrAfter(now, "day") &&
|
||||
storedTo.isAfter(storedFrom)
|
||||
|
||||
if (isDateParamValid && stored.rooms?.length) {
|
||||
setBookingState({
|
||||
fromDate: storedFrom.format("YYYY-MM-DD"),
|
||||
toDate: storedTo.format("YYYY-MM-DD"),
|
||||
rooms: stored.rooms,
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
return bookingState
|
||||
}
|
||||
|
||||
function getBookingWidgetState(): BookingWidgetState | null {
|
||||
if (typeof window === "undefined") {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const savedBookingWidgetState = sessionStorage.getItem(BOOKING_WIDGET_STATE)
|
||||
if (savedBookingWidgetState) {
|
||||
const stored = JSON.parse(savedBookingWidgetState)
|
||||
return stateSchema.parse(stored)
|
||||
}
|
||||
} catch {
|
||||
try {
|
||||
sessionStorage.removeItem(BOOKING_WIDGET_STATE)
|
||||
} catch {}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function setBookingWidgetState(state: BookingWidgetState): void {
|
||||
if (typeof window === "undefined") {
|
||||
return
|
||||
}
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
BOOKING_WIDGET_STATE,
|
||||
JSON.stringify(stateSchema.parse(state))
|
||||
)
|
||||
} catch {}
|
||||
}
|
||||
@@ -39,9 +39,9 @@ const typeHints = {
|
||||
filters: "COMMA_SEPARATED_ARRAY",
|
||||
packages: "COMMA_SEPARATED_ARRAY",
|
||||
} as const
|
||||
const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
|
||||
const childAgeSchema = z.coerce.number().catch(-1)
|
||||
const childBedSchema = z.coerce.number().catch(-1)
|
||||
export const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
|
||||
export const childAgeSchema = z.coerce.number().catch(-1)
|
||||
export const childBedSchema = z.coerce.number().catch(-1)
|
||||
const searchTypeSchema = z.enum(bookingSearchTypes).optional().catch(undefined)
|
||||
|
||||
export function parseBookingWidgetSearchParams(
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"./components/SidePeekAccordions/ParkingAccordionItem": "./lib/components/SidePeekAccordions/ParkingAccordionItem.tsx",
|
||||
"./global.d.ts": "./global.d.ts",
|
||||
"./hooks/useHandleBookingStatus": "./lib/hooks/useHandleBookingStatus.ts",
|
||||
"./hooks/useBookingWidgetState": "./lib/hooks/useBookingWidgetState.ts",
|
||||
"./pages/*": "./lib/pages/*.tsx",
|
||||
"./stores/enter-details/types": "./lib/stores/enter-details/types.ts",
|
||||
"./stores/hotels-map": "./lib/stores/hotels-map.ts",
|
||||
|
||||
1
packages/common/constants/bookingWidget.ts
Normal file
1
packages/common/constants/bookingWidget.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const BOOKING_WIDGET_STATE = "bookingWidgetState"
|
||||
@@ -27,14 +27,8 @@ export function selectHotel(lang: Lang) {
|
||||
export function selectHotelMap(lang: Lang) {
|
||||
return `${hotelreservation(lang)}/select-hotel/map`
|
||||
}
|
||||
|
||||
export function selectRateWithParams(
|
||||
lang: Lang,
|
||||
hotelId: string,
|
||||
fromdate: string,
|
||||
todate: string
|
||||
) {
|
||||
return `${hotelreservation(lang)}/select-rate?room%5B0%5D.adults=1&fromdate=${fromdate}&todate=${todate}&hotel=${hotelId}`
|
||||
export function selectRateWithParams(lang: Lang, params: URLSearchParams) {
|
||||
return `${hotelreservation(lang)}/select-rate?${params.toString()}`
|
||||
}
|
||||
|
||||
export function alternativeHotels(lang: Lang) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"./polyfills": "./polyfills/index.ts",
|
||||
"./constants/alert": "./constants/alert.ts",
|
||||
"./constants/booking": "./constants/booking.ts",
|
||||
"./constants/bookingWidget": "./constants/bookingWidget.ts",
|
||||
"./constants/currency": "./constants/currency.ts",
|
||||
"./constants/dateFormats": "./constants/dateFormats.ts",
|
||||
"./constants/facilities": "./constants/facilities.ts",
|
||||
|
||||
Reference in New Issue
Block a user