Merged in chore/booking-flow-cleaning (pull request #3354)

chore: Clean booking-flow

* Clean booking-flow

* Fix type issue


Approved-by: Joakim Jäderberg
Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-12-17 13:04:26 +00:00
parent eb3b18b35e
commit 6ee262ad89
10 changed files with 70 additions and 32 deletions

View File

@@ -0,0 +1,25 @@
# @scandic-hotels/booking-flow
Shared package containing the hotel booking flow functionality used by `scandic-web` and partner sites.
## Configuration
To use the booking flow, two main contexts must be set up: `BookingFlowConfig` and `BookingFlowContext`. Both receive injected values from the consuming app.
**`BookingFlowConfig`** is static configuration that controls features and behavior (e.g. booking codes enabled), while **`BookingFlowContext`** is dynamic state provided by the consuming site (e.g. user authentication status).
The **config** sets up both a server side and client side context provider. You can use `getBookingFlowConfig` server side and `useBookingFlowConfig` client side to access the config values.
The **context** is only provided client side, and can be accessed using the `useBookingFlowContext` hook.
There's also some setup required for **tRPC** to work with the booking flow. See the [tRPC context](../trpc/lib/context.ts) and how it's used in the consuming apps for details.
## Translations
Translations for the booking flow components work similarly to other parts of the app, using `react-intl`. See [translations.md](../../docs/translations.md) for more information on how translations are set up in the apps.
We currently do not dependency inject any translation messages from the consuming apps, so for now all booking-flow translation messages for the different sites are included in all app bundles. This should be improved in the future.
## Other
Components that are currently exported but are intended for external use should probably move to design-system or similar in the future.

View File

@@ -33,6 +33,7 @@ export const useGetPointsCurrency = () => {
case "partner-sas":
return CurrencyEnum.EUROBONUS
default:
const _exhaustiveCheck: never = config.variant
throw new Error(`Unknown variant: ${config.variant}`)
}
}

View File

@@ -10,8 +10,8 @@ type BaseUser = {
export type BookingFlowUser =
| (BaseUser & {
type: "partner-sas"
partnerLoyaltyNumber: `EB${string}`
type: "partner"
partnerLoyaltyNumber: string
isLinked: boolean
})
| (BaseUser & {

View File

@@ -55,17 +55,23 @@ export function getErrorMessage(
"Multi-room booking is not available with this booking code.",
})
case bookingWidgetErrors.MULTIROOM_REWARD_NIGHT_UNAVAILABLE:
return variant === "partner-sas"
? intl.formatMessage({
id: "partnerSas.error.multiroomRewardNightUnavailable",
defaultMessage:
"Multi-room bookings are not available with EuroBonus points.",
})
: intl.formatMessage({
switch (variant) {
case "scandic":
return intl.formatMessage({
id: "error.multiroomRewardNightUnavailable",
defaultMessage:
"Multi-room booking is not available with reward night.",
})
case "partner-sas":
return intl.formatMessage({
id: "partnerSas.error.multiroomRewardNightUnavailable",
defaultMessage:
"Multi-room bookings are not available with EuroBonus points.",
})
default:
const _exhaustiveCheck: never = variant
throw new Error(`Unknown variant: ${variant}`)
}
case bookingWidgetErrors.CODE_VOUCHER_REWARD_NIGHT_UNAVAILABLE:
return intl.formatMessage({
id: "error.codeVoucherRewardNightUnavailable",

View File

@@ -165,6 +165,9 @@ function getRewardMessage(config: BookingFlowConfig, intl: IntlShape): string {
id: "bookingWidget.reward.rewardNight",
defaultMessage: "Reward Night",
})
default:
const _exhaustiveCheck: never = config.variant
throw new Error(`Unknown variant: ${config.variant}`)
}
}

View File

@@ -25,15 +25,19 @@ function Message() {
const intl = useIntl()
const config = useBookingFlowConfig()
if (config.variant === "partner-sas") {
return intl.formatMessage({
id: "signup.toGetTheScandicMemberPrice",
defaultMessage: "Enter ID or join to get the Scandic Friends price.",
})
switch (config.variant) {
case "partner-sas":
return intl.formatMessage({
id: "signup.toGetTheScandicMemberPrice",
defaultMessage: "Enter ID or join to get the Scandic Friends price.",
})
case "scandic":
return intl.formatMessage({
id: "signup.joinOrLoginForMemberPricing",
defaultMessage: "Join or log in while booking for member pricing.",
})
default:
const _exhaustiveCheck: never = config.variant
throw new Error(`Unknown variant: ${config.variant}`)
}
return intl.formatMessage({
id: "signup.joinOrLoginForMemberPricing",
defaultMessage: "Join or log in while booking for member pricing.",
})
}

View File

@@ -1,4 +1,3 @@
// TODO should probably not need to export this outside package after moving entire booking flow
import { create } from "zustand"
export enum BookingCodeFilterEnum {

View File

@@ -242,7 +242,6 @@ export function serializeBookingSearchParams(
})
}
// TODO duplicated until full booking flow is migrated to booking-flow package
export type DetailsBooking = {
hotelId: string
fromDate: string

View File

@@ -13,16 +13,17 @@ import type { TrackingUserData } from "../../types"
export const userTrackingInfo = safeProtectedProcedure.query(async function ({
ctx,
}) {
if (ctx.app === "partner-sas") {
const scandicUserToken = await ctx.getScandicUserToken()
return getSasEurobonusUserTrackingData(ctx.session, scandicUserToken)
switch (ctx.app) {
case "partner-sas": {
const scandicUserToken = await ctx.getScandicUserToken()
return getSasEurobonusUserTrackingData(ctx.session, scandicUserToken)
}
case "scandic-web":
return getScandicFriendsUserTrackingData(ctx.session)
default:
const _exhaustiveCheck: never = ctx.app
return { loginStatus: "Error" } as const
}
if (ctx.app === "scandic-web") {
return getScandicFriendsUserTrackingData(ctx.session)
}
return { loginStatus: "Error" } as const
})
async function getScandicFriendsUserTrackingData(session: Session | null) {