Merged in feat/sw-3472-booking-flow-parameterization (pull request #2811)

feat(SW-3272): Add BookingFlowConfig

* Add BookingFlowConfig

* Rename "provider" to BookingFlowConfig

* Change bookingCode to boolean

* Fix error


Approved-by: Joakim Jäderberg
Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-09-19 11:56:50 +00:00
parent 7c92a8fc9a
commit 7adb9ded46
10 changed files with 178 additions and 65 deletions

View File

@@ -7,6 +7,7 @@ import "../../globals.css"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import Script from "next/script"
import { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlowConfig"
import { BookingFlowContextProvider } from "@scandic-hotels/booking-flow/BookingFlowContextProvider"
import { BookingFlowTrackingProvider } from "@scandic-hotels/booking-flow/BookingFlowTrackingProvider"
import { NuqsAdapter } from "@scandic-hotels/booking-flow/utils/nuqs"
@@ -56,6 +57,8 @@ type RootLayoutProps = {
bookingwidget: React.ReactNode
}
const bookingFlowConfig = { bookingCodeEnabled: false } as const
export default async function RootLayout(props: RootLayoutProps) {
const params = await props.params
const lang = params.lang
@@ -87,36 +90,38 @@ export default async function RootLayout(props: RootLayoutProps) {
<NuqsAdapter>
<TrpcProvider>
<RACRouterProvider>
<BookingFlowContextProvider
data={{
// TODO
isLoggedIn: false,
}}
>
<BookingFlowTrackingProvider
trackingFunctions={{
trackBookingSearchClick,
trackAccordionItemOpen,
trackOpenSidePeek,
trackGenericEvent,
trackGlaSaveCardAttempt,
trackLoginClick,
trackPaymentEvent,
trackUpdatePaymentMethod,
trackBreakfastSelection,
trackBedSelection,
<BookingFlowConfig config={bookingFlowConfig}>
<BookingFlowContextProvider
data={{
// TODO
isLoggedIn: false,
}}
>
<SiteWideAlert />
<Header />
{props.bookingwidget}
<main>{children}</main>
<Footer />
<ToastHandler />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowTrackingProvider>
</BookingFlowContextProvider>
<BookingFlowTrackingProvider
trackingFunctions={{
trackBookingSearchClick,
trackAccordionItemOpen,
trackOpenSidePeek,
trackGenericEvent,
trackGlaSaveCardAttempt,
trackLoginClick,
trackPaymentEvent,
trackUpdatePaymentMethod,
trackBreakfastSelection,
trackBedSelection,
}}
>
<SiteWideAlert />
<Header />
{props.bookingwidget}
<main>{children}</main>
<Footer />
<ToastHandler />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowTrackingProvider>
</BookingFlowContextProvider>
</BookingFlowConfig>
</RACRouterProvider>
</TrpcProvider>
</NuqsAdapter>

View File

@@ -8,11 +8,13 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import Script from "next/script"
import { SessionProvider } from "next-auth/react"
import { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlowConfig"
import StorageCleaner from "@scandic-hotels/booking-flow/components/EnterDetails/StorageCleaner"
import { NuqsAdapter } from "@scandic-hotels/booking-flow/utils/nuqs"
import { Lang } from "@scandic-hotels/common/constants/language"
import { ToastHandler } from "@scandic-hotels/design-system/ToastHandler"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import TrpcProvider from "@/lib/trpc/Provider"
import { SessionRefresher } from "@/components/Auth/TokenRefresher"
@@ -72,20 +74,22 @@ export default async function RootLayout(
<NuqsAdapter>
<TrpcProvider>
<RACRouterProvider>
<BookingFlowProviders>
<RouteChange />
<SitewideAlert />
<Header />
{bookingwidget}
{children}
<Footer />
<ToastHandler />
<SessionRefresher />
<StorageCleaner />
<CookieBotConsent />
<UserExists />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowProviders>
<BookingFlowConfig config={bookingFlowConfig}>
<BookingFlowProviders>
<RouteChange />
<SitewideAlert />
<Header />
{bookingwidget}
{children}
<Footer />
<ToastHandler />
<SessionRefresher />
<StorageCleaner />
<CookieBotConsent />
<UserExists />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowProviders>
</BookingFlowConfig>
</RACRouterProvider>
</TrpcProvider>
</NuqsAdapter>

View File

@@ -8,11 +8,13 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import Script from "next/script"
import { SessionProvider } from "next-auth/react"
import { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlowConfig"
import StorageCleaner from "@scandic-hotels/booking-flow/components/EnterDetails/StorageCleaner"
import { NuqsAdapter } from "@scandic-hotels/booking-flow/utils/nuqs"
import { Lang } from "@scandic-hotels/common/constants/language"
import { ToastHandler } from "@scandic-hotels/design-system/ToastHandler"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import TrpcProvider from "@/lib/trpc/Provider"
import { SessionRefresher } from "@/components/Auth/TokenRefresher"
@@ -56,13 +58,15 @@ export default async function RootLayout(
>
<NuqsAdapter>
<TrpcProvider>
<RouteChange />
{children}
<ToastHandler />
<SessionRefresher />
<StorageCleaner />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
<BookingFlowConfig config={bookingFlowConfig}>
<RouteChange />
{children}
<ToastHandler />
<SessionRefresher />
<StorageCleaner />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowConfig>
</TrpcProvider>
</NuqsAdapter>
</ClientIntlProvider>

View File

@@ -7,11 +7,13 @@ import "@scandic-hotels/design-system/style.css"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import Script from "next/script"
import { BookingFlowConfig } from "@scandic-hotels/booking-flow/BookingFlowConfig"
import StorageCleaner from "@scandic-hotels/booking-flow/components/EnterDetails/StorageCleaner"
import { NuqsAdapter } from "@scandic-hotels/booking-flow/utils/nuqs"
import { Lang } from "@scandic-hotels/common/constants/language"
import { ToastHandler } from "@scandic-hotels/design-system/ToastHandler"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import TrpcProvider from "@/lib/trpc/Provider"
import TokenRefresher from "@/components/Auth/TokenRefresher"
@@ -56,13 +58,15 @@ export default async function RootLayout(
>
<NuqsAdapter>
<TrpcProvider>
<RouteChange />
{children}
<ToastHandler />
<TokenRefresher />
<StorageCleaner />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
<BookingFlowConfig config={bookingFlowConfig}>
<RouteChange />
{children}
<ToastHandler />
<TokenRefresher />
<StorageCleaner />
<CookieBotConsent />
<ReactQueryDevtools initialIsOpen={false} />
</BookingFlowConfig>
</TrpcProvider>
</NuqsAdapter>
</ClientIntlProvider>

View File

@@ -0,0 +1 @@
export const bookingFlowConfig = { bookingCodeEnabled: true } as const

View File

@@ -0,0 +1,47 @@
import "server-only"
import { cache } from "react"
import { BookingFlowConfigContextProvider } from "./bookingFlowConfigContext"
export type BookingFlowConfig = {
bookingCodeEnabled: boolean
}
const getRef = cache(() => ({
current: undefined as BookingFlowConfig | undefined,
}))
function setBookingFlowConfig(newConfig: BookingFlowConfig) {
getRef().current = newConfig
}
export function getBookingFlowConfig(): BookingFlowConfig {
const contextConfig = getRef().current
if (!contextConfig) {
throw new Error("BookingFlowConfig not set")
}
return contextConfig
}
/*
* Sets up both a server side context and a client side context
* for the booking flow config.
*/
export async function BookingFlowConfig({
children,
config,
}: {
children: React.ReactNode
config: BookingFlowConfig
}) {
setBookingFlowConfig(config)
return (
<BookingFlowConfigContextProvider config={config}>
{children}
</BookingFlowConfigContextProvider>
)
}

View File

@@ -0,0 +1,39 @@
"use client"
import { createContext, useContext } from "react"
import type { BookingFlowConfig } from "./bookingFlowConfig"
type BookingFlowConfigContextData = {
config: BookingFlowConfig
}
const BookingFlowConfigContext = createContext<
BookingFlowConfigContextData | undefined
>(undefined)
export const useBookingFlowConfig = (): BookingFlowConfigContextData => {
const context = useContext(BookingFlowConfigContext)
if (!context) {
throw new Error(
"useBookingFlowConfig must be used within a BookingFlowConfigContextProvider. Did you forget to use BookingFlowConfig in the consuming app?"
)
}
return context
}
export function BookingFlowConfigContextProvider({
children,
config,
}: {
children: React.ReactNode
config: BookingFlowConfig
}) {
return (
<BookingFlowConfigContext.Provider value={{ config }}>
{children}
</BookingFlowConfigContext.Provider>
)
}

View File

@@ -5,15 +5,18 @@ import { useIntl } from "react-intl"
import Caption from "@scandic-hotels/design-system/Caption"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFlowConfigContext"
import BookingCode from "../BookingCode"
import RewardNight from "../RewardNight"
import styles from "./voucher.module.css"
export default function Voucher() {
const { config } = useBookingFlowConfig()
return (
<div className={styles.optionsContainer}>
<BookingCode />
{config.bookingCodeEnabled && <BookingCode />}
<div className={styles.options}>
<div className={styles.option}>
<RewardNight />
@@ -25,6 +28,7 @@ export default function Voucher() {
export function VoucherSkeleton() {
const intl = useIntl()
const { config } = useBookingFlowConfig()
const vouchers = intl.formatMessage({
defaultMessage: "Code / Voucher",
@@ -35,14 +39,16 @@ export function VoucherSkeleton() {
return (
<div className={styles.optionsContainer}>
<div className={styles.voucherSkeletonContainer}>
<label>
<Caption type="bold" color="red" asChild>
<span>{vouchers}</span>
</Caption>
</label>
<SkeletonShimmer width="100%" display="block" />
</div>
{config.bookingCodeEnabled && (
<div className={styles.voucherSkeletonContainer}>
<label>
<Caption type="bold" color="red" asChild>
<span>{vouchers}</span>
</Caption>
</label>
<SkeletonShimmer width="100%" display="block" />
</div>
)}
<div className={styles.options}>
<div className={cx(styles.option, styles.showOnTablet)}>

View File

@@ -11,6 +11,7 @@
"test:watch": "vitest"
},
"exports": {
"./BookingFlowConfig": "./lib/bookingFlowConfig/bookingFlowConfig.tsx",
"./BookingFlowContextProvider": "./lib/components/BookingFlowContextProvider.tsx",
"./BookingFlowTrackingProvider": "./lib/components/BookingFlowTrackingProvider.tsx",
"./BookingWidget": "./lib/components/BookingWidget/index.tsx",
@@ -77,6 +78,7 @@
"react-hook-form": "^7.56.2",
"react-intl": "^7.1.11",
"react-to-print": "^3.1.0",
"server-only": "^0.0.1",
"usehooks-ts": "3.1.1",
"zustand": "^4.5.2"
},

View File

@@ -5906,6 +5906,7 @@ __metadata:
react-hook-form: "npm:^7.56.2"
react-intl: "npm:^7.1.11"
react-to-print: "npm:^3.1.0"
server-only: "npm:^0.0.1"
typescript: "npm:5.8.3"
usehooks-ts: "npm:3.1.1"
vitest: "npm:^3.2.4"