Merged in fix/sw-3551-rsc-bookingflowconfig (pull request #2988)

fix(SW-3551): Fix issue with BookingConfigProvider in RSC

* wip move config to pages

* Move config providing to pages
This commit is contained in:
Anton Gunnarsson
2025-10-22 07:04:21 +00:00
parent 2a28681259
commit c435cdba68
44 changed files with 398 additions and 237 deletions

View File

@@ -54,7 +54,7 @@ export default function BookingWidgetClient({
const [originalOverflowY, setOriginalOverflowY] = useState<string | null>(
null
)
const { bookingCodeEnabled } = useBookingFlowConfig()
const bookingFlowConfig = useBookingFlowConfig()
const storedBookingWidgetState = useBookingWidgetState()
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
@@ -128,7 +128,7 @@ export default function BookingWidgetClient({
toDate: toDate.format("YYYY-MM-DD"),
},
bookingCode: {
value: bookingCodeEnabled ? selectedBookingCode : "",
value: bookingFlowConfig.bookingCodeEnabled ? selectedBookingCode : "",
remember: false,
},
redemption: data.searchType === SEARCH_TYPE_REDEMPTION,
@@ -228,7 +228,7 @@ export default function BookingWidgetClient({
}, [data, methods, storedBookingWidgetState])
if (shouldShowSkeleton) {
return <BookingWidgetSkeleton type={type} />
return <BookingWidgetSkeleton type={type} config={bookingFlowConfig} />
}
const classNames = bookingWidgetContainerVariants({

View File

@@ -1,3 +1,4 @@
import { BookingFlowConfig } from "../../../bookingFlowConfig/bookingFlowConfig"
import {
getPageSettingsBookingCode,
isBookingWidgetHidden,
@@ -9,6 +10,7 @@ import type { BookingWidgetProps } from ".."
export async function FloatingBookingWidget({
booking,
lang,
config,
}: Omit<BookingWidgetProps, "type">) {
const isHidden = await isBookingWidgetHidden(lang)
@@ -22,9 +24,11 @@ export async function FloatingBookingWidget({
}
return (
<FloatingBookingWidgetClient
data={booking}
pageSettingsBookingCodePromise={pageSettingsBookingCodePromise}
/>
<BookingFlowConfig config={config}>
<FloatingBookingWidgetClient
data={booking}
pageSettingsBookingCodePromise={pageSettingsBookingCodePromise}
/>
</BookingFlowConfig>
)
}

View File

@@ -1,30 +1,34 @@
"use client"
import { BookingFlowConfigContextProvider } from "../../bookingFlowConfig/bookingFlowConfigContext"
import { BookingWidgetFormSkeleton } from "./BookingWidgetForm"
import { MobileToggleButtonSkeleton } from "./MobileToggleButton"
import { bookingWidgetContainerVariants } from "./variant"
import styles from "./bookingWidget.module.css"
import type { BookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfig"
import type { BookingWidgetClientProps } from "./Client"
export function BookingWidgetSkeleton({
type = "full",
config,
}: {
type?: BookingWidgetClientProps["type"]
config: BookingFlowConfig
}) {
const classNames = bookingWidgetContainerVariants({
type,
})
return (
<>
<BookingFlowConfigContextProvider config={config}>
<section className={classNames} style={{ top: 0 }}>
<MobileToggleButtonSkeleton />
<div className={styles.formContainer}>
<BookingWidgetFormSkeleton type={type} />
</div>
</section>
</>
</BookingFlowConfigContextProvider>
)
}

View File

@@ -1,5 +1,6 @@
import { Suspense } from "react"
import { BookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfig"
import {
getPageSettingsBookingCode,
isBookingWidgetHidden,
@@ -39,13 +40,16 @@ export type BookingWidgetProps = {
type?: BookingWidgetType
booking: BookingWidgetSearchData
lang: Lang
config: BookingFlowConfig
}
export async function BookingWidget(props: BookingWidgetProps) {
export async function BookingWidget({ config, ...props }: BookingWidgetProps) {
return (
<Suspense fallback={<BookingWidgetSkeleton />}>
<InternalBookingWidget {...props} />
</Suspense>
<BookingFlowConfig config={config}>
<Suspense fallback={<BookingWidgetSkeleton config={config} />}>
<InternalBookingWidget {...props} />
</Suspense>
</BookingFlowConfig>
)
}
@@ -53,7 +57,7 @@ async function InternalBookingWidget({
lang,
type,
booking,
}: BookingWidgetProps) {
}: Omit<BookingWidgetProps, "config">) {
const isHidden = await isBookingWidgetHidden(lang)
if (isHidden) {

View File

@@ -5,7 +5,7 @@ import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { env } from "../../env/server"
import { getBookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { MapContainer } from "../components/MapContainer"
import {
getFiltersFromHotels,
@@ -27,9 +27,11 @@ import type { NextSearchParams } from "../types"
export async function AlternativeHotelsMapPage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
@@ -103,29 +105,31 @@ export async function AlternativeHotelsMapPage({
isBookingCodeRateAvailable,
isRedemption: redemption,
isRedemptionAvailable: isRedemptionAvailability,
config: getBookingFlowConfig(),
config,
})
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
return (
<MapContainer>
<Suspense key={booking.hotelId} fallback={<SelectHotelMapSkeleton />}>
<SelectHotelMap
apiKey={googleMapsApiKey}
mapId={googleMapId}
hotels={hotels}
cityCoordinates={cityCoordinates}
bookingCode={booking.bookingCode}
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
isAlternativeHotels={true}
filterList={filterList}
/>
<TrackingSDK
hotelInfo={hotelsTrackingData}
pageData={pageTrackingData}
/>
</Suspense>
</MapContainer>
<BookingFlowConfig config={config}>
<MapContainer>
<Suspense key={booking.hotelId} fallback={<SelectHotelMapSkeleton />}>
<SelectHotelMap
apiKey={googleMapsApiKey}
mapId={googleMapId}
hotels={hotels}
cityCoordinates={cityCoordinates}
bookingCode={booking.bookingCode}
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
isAlternativeHotels={true}
filterList={filterList}
/>
<TrackingSDK
hotelInfo={hotelsTrackingData}
pageData={pageTrackingData}
/>
</Suspense>
</MapContainer>
</BookingFlowConfig>
)
}

View File

@@ -7,7 +7,7 @@ import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAn
import { NoAvailabilityTracking } from "@scandic-hotels/tracking/NoAvailabilityTracking"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { getBookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { AlternativeHotelsPageTitle } from "../components/AlternativeHotelsPageTitle"
import FnFNotAllowedAlert from "../components/FnFNotAllowedAlert"
import { SelectHotel } from "../components/SelectHotel"
@@ -25,9 +25,11 @@ export { SelectHotelSkeleton as AlternativeHotelsPageSkeleton } from "../compone
export async function AlternativeHotelsPage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const booking = parseSelectHotelSearchParams(searchParams)
@@ -108,7 +110,7 @@ export async function AlternativeHotelsPage({
isBookingCodeRateAvailable,
isRedemption: searchDetails.redemption,
isRedemptionAvailable: isRedemptionAvailability,
config: getBookingFlowConfig(),
config,
})
const suspenseKey = stringify(searchParams)
@@ -118,7 +120,7 @@ export async function AlternativeHotelsPage({
(booking.bookingCode && hotels.length > 0 && !isBookingCodeRateAvailable)
)
return (
<>
<BookingFlowConfig config={config}>
<SelectHotel
bookingCode={booking.bookingCode}
city={searchDetails.city}
@@ -142,6 +144,6 @@ export async function AlternativeHotelsPage({
shouldTrackNoAvailability={shouldTrackNoAvailability}
/>
</Suspense>
</>
</BookingFlowConfig>
)
}

View File

@@ -3,6 +3,7 @@ import { notFound, redirect } from "next/navigation"
import { decrypt } from "@scandic-hotels/trpc/utils/encryption"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { BookingConfirmation } from "../components/BookingConfirmation"
import { getBookingConfirmation } from "../trpc/memoizedRequests/getBookingConfirmation"
import { MEMBERSHIP_FAILED_ERROR } from "../types/membershipFailedError"
@@ -16,10 +17,12 @@ export async function BookingConfirmationPage({
intl,
lang,
searchParams,
config,
}: {
intl: IntlShape
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const refId = searchParams.RefId?.toString()
@@ -46,10 +49,12 @@ export async function BookingConfirmationPage({
searchParams.errorCode === MEMBERSHIP_FAILED_ERROR
return (
<BookingConfirmation
intl={intl}
refId={refId}
membershipFailedError={membershipFailedError}
/>
<BookingFlowConfig config={config}>
<BookingConfirmation
intl={intl}
refId={refId}
membershipFailedError={membershipFailedError}
/>
</BookingFlowConfig>
)
}

View File

@@ -4,6 +4,7 @@ import { Suspense } from "react"
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import HotelHeader from "../components/EnterDetails/Header"
import Payment from "../components/EnterDetails/Payment"
import Multiroom from "../components/EnterDetails/Room/Multiroom"
@@ -29,9 +30,11 @@ import type { NextSearchParams } from "../types"
export async function EnterDetailsPage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const selectRoomParams = new URLSearchParams(
searchParams as Record<string, string>
@@ -116,53 +119,55 @@ export async function EnterDetailsPage({
// beneath footer to be able to show entire footer upon
// scrolling down to the bottom of the page
return (
<main data-footer-spacing>
<EnterDetailsProvider
booking={booking}
breakfastPackages={breakfastPackages}
lang={lang}
rooms={rooms}
searchParamsStr={selectRoomParams.toString()}
user={user}
vat={hotel.vat}
roomCategories={hotelData.roomCategories}
>
<HotelHeader hotelData={hotelData} />
<div className={styles.container}>
<div className={styles.content}>
<RoomProvider idx={0} room={firstRoom}>
<RoomOne user={user} />
</RoomProvider>
{multirooms.map((room, idx) => (
// Need to start idx from 1 since first room is
// rendered above
<RoomProvider key={idx + 1} idx={idx + 1} room={room}>
<Multiroom />
</RoomProvider>
))}
<Suspense>
<Payment
otherPaymentOptions={
hotel.merchantInformationData.alternatePaymentOptions
}
supportedCards={hotel.merchantInformationData.cards}
/>
</Suspense>
</div>
<aside className={styles.summary}>
<MobileSummary isUserLoggedIn={!!user} />
<DesktopSummary isUserLoggedIn={!!user} />
</aside>
</div>
<EnterDetailsTrackingWrapper
<BookingFlowConfig config={config}>
<main data-footer-spacing>
<EnterDetailsProvider
booking={booking}
hotel={hotel}
isMember={!!user}
breakfastPackages={breakfastPackages}
lang={lang}
rooms={rooms}
/>
</EnterDetailsProvider>
</main>
searchParamsStr={selectRoomParams.toString()}
user={user}
vat={hotel.vat}
roomCategories={hotelData.roomCategories}
>
<HotelHeader hotelData={hotelData} />
<div className={styles.container}>
<div className={styles.content}>
<RoomProvider idx={0} room={firstRoom}>
<RoomOne user={user} />
</RoomProvider>
{multirooms.map((room, idx) => (
// Need to start idx from 1 since first room is
// rendered above
<RoomProvider key={idx + 1} idx={idx + 1} room={room}>
<Multiroom />
</RoomProvider>
))}
<Suspense>
<Payment
otherPaymentOptions={
hotel.merchantInformationData.alternatePaymentOptions
}
supportedCards={hotel.merchantInformationData.cards}
/>
</Suspense>
</div>
<aside className={styles.summary}>
<MobileSummary isUserLoggedIn={!!user} />
<DesktopSummary isUserLoggedIn={!!user} />
</aside>
</div>
<EnterDetailsTrackingWrapper
booking={booking}
hotel={hotel}
isMember={!!user}
lang={lang}
rooms={rooms}
/>
</EnterDetailsProvider>
</main>
</BookingFlowConfig>
)
}

View File

@@ -11,6 +11,7 @@ import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCod
import { getBooking } from "@scandic-hotels/trpc/routers/booking/utils"
import { encrypt } from "@scandic-hotels/trpc/utils/encryption"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { HandleErrorCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleErrorCallback"
import { HandleSuccessCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback"
import { serverClient } from "../trpc"
@@ -24,12 +25,14 @@ type PaymentCallbackPageProps = {
searchParams: NextSearchParams
userAccessToken: string | null
status: PaymentCallbackStatusEnum
config: BookingFlowConfig
}
export async function PaymentCallbackPage({
lang,
userAccessToken,
searchParams,
status,
config,
}: PaymentCallbackPageProps) {
const { confirmationNumber } = searchParams
@@ -51,12 +54,14 @@ export async function PaymentCallbackPage({
if (status === PaymentCallbackStatusEnum.Cancel) {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
return (
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
</BookingFlowConfig>
)
}
@@ -92,12 +97,14 @@ export async function PaymentCallbackPage({
)
return (
<HandleSuccessCallback
refId={refId}
sig={sig}
successRedirectUrl={confirmationUrl}
cardType={booking.guaranteeInfo?.cardType}
/>
<BookingFlowConfig config={config}>
<HandleSuccessCallback
refId={refId}
sig={sig}
successRedirectUrl={confirmationUrl}
cardType={booking.guaranteeInfo?.cardType}
/>
</BookingFlowConfig>
)
}
@@ -141,11 +148,13 @@ export async function PaymentCallbackPage({
}
return (
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
</BookingFlowConfig>
)
}

View File

@@ -6,7 +6,7 @@ import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { env } from "../../env/server"
import { getBookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { MapContainer } from "../components/MapContainer"
import {
getFiltersFromHotels,
@@ -28,9 +28,11 @@ import type { NextSearchParams } from "../types"
export async function SelectHotelMapPage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
@@ -104,7 +106,7 @@ export async function SelectHotelMapPage({
isBookingCodeRateAvailable,
isRedemption: redemption,
isRedemptionAvailable: isRedemptionAvailability,
config: getBookingFlowConfig(),
config,
})
const filterList = getFiltersFromHotels(hotels, isBookingCodeRateAvailable)
@@ -112,22 +114,24 @@ export async function SelectHotelMapPage({
const suspenseKey = stringify(searchParams)
return (
<MapContainer>
<Suspense key={suspenseKey} fallback={<SelectHotelMapSkeleton />}>
<SelectHotelMap
apiKey={googleMapsApiKey}
mapId={googleMapId}
hotels={hotels}
cityCoordinates={cityCoordinates}
bookingCode={booking.bookingCode}
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
filterList={filterList}
/>
<TrackingSDK
hotelInfo={hotelsTrackingData}
pageData={pageTrackingData}
/>
</Suspense>
</MapContainer>
<BookingFlowConfig config={config}>
<MapContainer>
<Suspense key={suspenseKey} fallback={<SelectHotelMapSkeleton />}>
<SelectHotelMap
apiKey={googleMapsApiKey}
mapId={googleMapId}
hotels={hotels}
cityCoordinates={cityCoordinates}
bookingCode={booking.bookingCode}
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
filterList={filterList}
/>
<TrackingSDK
hotelInfo={hotelsTrackingData}
pageData={pageTrackingData}
/>
</Suspense>
</MapContainer>
</BookingFlowConfig>
)
}

View File

@@ -7,7 +7,7 @@ import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAn
import { NoAvailabilityTracking } from "@scandic-hotels/tracking/NoAvailabilityTracking"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { getBookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import FnFNotAllowedAlert from "../components/FnFNotAllowedAlert"
import { SelectHotel } from "../components/SelectHotel"
import { getHotels } from "../components/SelectHotel/helpers"
@@ -24,9 +24,11 @@ export { SelectHotelSkeleton as SelectHotelPageSkeleton } from "../components/Se
export async function SelectHotelPage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const booking = parseSelectHotelSearchParams(searchParams)
@@ -94,7 +96,7 @@ export async function SelectHotelPage({
isBookingCodeRateAvailable,
isRedemption: redemption,
isRedemptionAvailable: isRedemptionAvailability,
config: getBookingFlowConfig(),
config,
})
const suspenseKey = stringify(searchParams)
@@ -105,7 +107,7 @@ export async function SelectHotelPage({
)
return (
<>
<BookingFlowConfig config={config}>
<SelectHotel
bookingCode={booking.bookingCode}
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
@@ -126,6 +128,6 @@ export async function SelectHotelPage({
shouldTrackNoAvailability={shouldTrackNoAvailability}
/>
</Suspense>
</>
</BookingFlowConfig>
)
}

View File

@@ -3,6 +3,7 @@ import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { SelectRate } from "../components/SelectRate"
import { SelectRateTracking } from "../components/SelectRate/Tracking/SelectRateTracking"
import { SelectRateProvider } from "../contexts/SelectRate/SelectRateContext"
@@ -28,9 +29,11 @@ const singleRoomRateTypes = combineRegExps(
export async function SelectRatePage({
lang,
searchParams,
config,
}: {
lang: Lang
searchParams: NextSearchParams
config: BookingFlowConfig
}) {
const booking = parseSelectRateSearchParams(searchParams)
@@ -71,12 +74,12 @@ export async function SelectRatePage({
}
return (
<>
<BookingFlowConfig config={config}>
<SelectRateProvider hotelData={hotelData}>
<SelectRate hotelData={hotelData} booking={booking} />
<SelectRateTracking hotelData={hotelData} booking={booking} />
</SelectRateProvider>
</>
</BookingFlowConfig>
)
}

View File

@@ -11,10 +11,7 @@ export async function getUserPointsBalance(
const verifiedUser =
session.token.loginType === "eurobonus"
? await getEuroBonusProfileData({
accessToken: session.token.access_token,
loginType: session.token.loginType,
})
? await getEuroBonusProfileSafely(session)
: await getVerifiedUser({ session })
if (!verifiedUser || "error" in verifiedUser) {
@@ -28,3 +25,14 @@ export async function getUserPointsBalance(
return points ?? 0
}
async function getEuroBonusProfileSafely(session: Session) {
try {
return await getEuroBonusProfileData({
accessToken: session.token.access_token,
loginType: session.token.loginType,
})
} catch (_error) {
return undefined
}
}