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"
|
"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 { selectRateWithParams } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||||
import { dt } from "@scandic-hotels/common/dt"
|
|
||||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||||
|
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
@@ -15,9 +16,15 @@ export default function PricesAndAvailabilityButton({
|
|||||||
label,
|
label,
|
||||||
}: PricesAndAvailabilityProps) {
|
}: PricesAndAvailabilityProps) {
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const fromdate = dt().format("YYYY-MM-DD")
|
const { fromDate, toDate, rooms } = useBookingWidgetState()
|
||||||
const todate = dt().add(1, "day").format("YYYY-MM-DD")
|
|
||||||
const selectRateURL = selectRateWithParams(lang, hotelId, fromdate, todate)
|
const params = serializeBookingSearchParams({
|
||||||
|
hotelId,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
rooms,
|
||||||
|
})
|
||||||
|
const selectRateURL = selectRateWithParams(lang, params)
|
||||||
|
|
||||||
const { name, roomTypes } = room
|
const { name, roomTypes } = room
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { trackBookingSearchClick } from "@scandic-hotels/tracking/booking"
|
import { trackBookingSearchClick } from "@scandic-hotels/tracking/booking"
|
||||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
|
||||||
|
import { setBookingWidgetState } from "../../../hooks/useBookingWidgetState"
|
||||||
import useLang from "../../../hooks/useLang"
|
import useLang from "../../../hooks/useLang"
|
||||||
import {
|
import {
|
||||||
BookingCodeFilterEnum,
|
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
|
// Followed current url structure to keep searchtype=redemption param incase of reward night
|
||||||
...(data.redemption ? { searchType: SEARCH_TYPE_REDEMPTION } : {}),
|
...(data.redemption ? { searchType: SEARCH_TYPE_REDEMPTION } : {}),
|
||||||
})
|
})
|
||||||
|
setBookingWidgetState({
|
||||||
|
fromDate: data.date.fromDate,
|
||||||
|
toDate: data.date.toDate,
|
||||||
|
rooms: data.rooms,
|
||||||
|
})
|
||||||
onClose()
|
onClose()
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
|
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 { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
|
||||||
import { useBookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfigContext"
|
import { useBookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfigContext"
|
||||||
|
import { useBookingWidgetState } from "../../hooks/useBookingWidgetState"
|
||||||
import useLang from "../../hooks/useLang"
|
import useLang from "../../hooks/useLang"
|
||||||
import {
|
import {
|
||||||
type bookingCodeSchema,
|
type bookingCodeSchema,
|
||||||
@@ -54,6 +55,7 @@ export default function BookingWidgetClient({
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
const { bookingCodeEnabled } = useBookingFlowConfig()
|
const { bookingCodeEnabled } = useBookingFlowConfig()
|
||||||
|
const storedBookingWidgetState = useBookingWidgetState()
|
||||||
|
|
||||||
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
|
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
|
||||||
|
|
||||||
@@ -207,6 +209,24 @@ export default function BookingWidgetClient({
|
|||||||
}
|
}
|
||||||
}, [methods, selectedBookingCode])
|
}, [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) {
|
if (shouldShowSkeleton) {
|
||||||
return <BookingWidgetSkeleton type={type} />
|
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",
|
filters: "COMMA_SEPARATED_ARRAY",
|
||||||
packages: "COMMA_SEPARATED_ARRAY",
|
packages: "COMMA_SEPARATED_ARRAY",
|
||||||
} as const
|
} as const
|
||||||
const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
|
export const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
|
||||||
const childAgeSchema = z.coerce.number().catch(-1)
|
export const childAgeSchema = z.coerce.number().catch(-1)
|
||||||
const childBedSchema = z.coerce.number().catch(-1)
|
export const childBedSchema = z.coerce.number().catch(-1)
|
||||||
const searchTypeSchema = z.enum(bookingSearchTypes).optional().catch(undefined)
|
const searchTypeSchema = z.enum(bookingSearchTypes).optional().catch(undefined)
|
||||||
|
|
||||||
export function parseBookingWidgetSearchParams(
|
export function parseBookingWidgetSearchParams(
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"./components/SidePeekAccordions/ParkingAccordionItem": "./lib/components/SidePeekAccordions/ParkingAccordionItem.tsx",
|
"./components/SidePeekAccordions/ParkingAccordionItem": "./lib/components/SidePeekAccordions/ParkingAccordionItem.tsx",
|
||||||
"./global.d.ts": "./global.d.ts",
|
"./global.d.ts": "./global.d.ts",
|
||||||
"./hooks/useHandleBookingStatus": "./lib/hooks/useHandleBookingStatus.ts",
|
"./hooks/useHandleBookingStatus": "./lib/hooks/useHandleBookingStatus.ts",
|
||||||
|
"./hooks/useBookingWidgetState": "./lib/hooks/useBookingWidgetState.ts",
|
||||||
"./pages/*": "./lib/pages/*.tsx",
|
"./pages/*": "./lib/pages/*.tsx",
|
||||||
"./stores/enter-details/types": "./lib/stores/enter-details/types.ts",
|
"./stores/enter-details/types": "./lib/stores/enter-details/types.ts",
|
||||||
"./stores/hotels-map": "./lib/stores/hotels-map.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) {
|
export function selectHotelMap(lang: Lang) {
|
||||||
return `${hotelreservation(lang)}/select-hotel/map`
|
return `${hotelreservation(lang)}/select-hotel/map`
|
||||||
}
|
}
|
||||||
|
export function selectRateWithParams(lang: Lang, params: URLSearchParams) {
|
||||||
export function selectRateWithParams(
|
return `${hotelreservation(lang)}/select-rate?${params.toString()}`
|
||||||
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 alternativeHotels(lang: Lang) {
|
export function alternativeHotels(lang: Lang) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"./polyfills": "./polyfills/index.ts",
|
"./polyfills": "./polyfills/index.ts",
|
||||||
"./constants/alert": "./constants/alert.ts",
|
"./constants/alert": "./constants/alert.ts",
|
||||||
"./constants/booking": "./constants/booking.ts",
|
"./constants/booking": "./constants/booking.ts",
|
||||||
|
"./constants/bookingWidget": "./constants/bookingWidget.ts",
|
||||||
"./constants/currency": "./constants/currency.ts",
|
"./constants/currency": "./constants/currency.ts",
|
||||||
"./constants/dateFormats": "./constants/dateFormats.ts",
|
"./constants/dateFormats": "./constants/dateFormats.ts",
|
||||||
"./constants/facilities": "./constants/facilities.ts",
|
"./constants/facilities": "./constants/facilities.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user