Merged in feature/SW-3516-pass-eurobonus-number-on-booking (pull request #2902)

* feat(SW-3516): Include partnerLoyaltyNumber on bookings

- Added user context to BookingFlowProviders for user state management.
- Updated booking input and output schemas to accommodate new user data.
- Refactored booking mutation logic to include user-related information.
- Improved type definitions for better TypeScript support across booking components.


Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2025-10-08 10:48:42 +00:00
parent b685c928e4
commit 17df3ee71a
18 changed files with 510 additions and 370 deletions

View File

@@ -1,17 +1,83 @@
"use client"
import { useSession } from "next-auth/react"
import { BookingFlowContextProvider } from "@scandic-hotels/booking-flow/BookingFlowContextProvider"
import { logger } from "@scandic-hotels/common/logger"
import { trpc } from "@scandic-hotels/trpc/client"
import { useIsUserLoggedIn } from "../hooks/useIsUserLoggedIn"
import type { ReactNode } from "react"
import type { Session } from "next-auth"
import type { ComponentProps, ReactNode } from "react"
export function BookingFlowProviders({ children }: { children: ReactNode }) {
const isLoggedIn = useIsUserLoggedIn()
const user = useBookingFlowUser()
return (
<BookingFlowContextProvider data={{ isLoggedIn }}>
<BookingFlowContextProvider
data={{
isLoggedIn: user.state === "loaded" && !!user.data,
user,
}}
>
{children}
</BookingFlowContextProvider>
)
}
type BookingFlowContextData = ComponentProps<
typeof BookingFlowContextProvider
>["data"]
type BookingFlowUser = BookingFlowContextData["user"]
function useBookingFlowUser(): BookingFlowUser {
const { data: session } = useSession()
const hasValidSession = isValidClientSession(session)
const {
data: euroBonusProfile,
isError,
isLoading,
} = trpc.partner.sas.getEuroBonusProfile.useQuery(undefined, {
enabled: hasValidSession,
})
if (isLoading) {
return { state: "loading", data: undefined }
}
if (isError || !euroBonusProfile) {
return { state: "error", data: undefined }
}
return {
state: "loaded",
data: {
type: "partner-sas",
partnerLoyaltyNumber: `EB${euroBonusProfile.eurobonusNumber}`,
firstName: euroBonusProfile.firstName || null,
lastName: euroBonusProfile.lastName || null,
email: euroBonusProfile.email,
},
}
}
function isValidClientSession(session: Session | null) {
if (!session) {
return false
}
if (session.error) {
logger.error(`Session error: ${session.error}`)
return false
}
if (session.token.error) {
logger.error(`Session token error: ${session.token.error}`)
return false
}
if (session.token.expires_at && session.token.expires_at < Date.now()) {
logger.error(`Session expired: ${session.token.expires_at}`)
return false
}
return true
}

View File

@@ -1,32 +1,6 @@
import { useSession } from "next-auth/react"
import { logger } from "@scandic-hotels/common/logger"
import type { Session } from "next-auth"
import { useBookingFlowContext } from "@scandic-hotels/booking-flow/hooks/useBookingFlowContext"
export function useIsUserLoggedIn() {
const { data: session } = useSession()
const isUserLoggedIn = isValidClientSession(session)
return isUserLoggedIn
}
function isValidClientSession(session: Session | null) {
if (!session) {
return false
}
if (session.error) {
logger.error(`Session error: ${session.error}`)
return false
}
if (session.token.error) {
logger.error(`Session token error: ${session.token.error}`)
return false
}
if (session.token.expires_at && session.token.expires_at < Date.now()) {
logger.error(`Session expired: ${session.token.expires_at}`)
return false
}
return true
const { isLoggedIn } = useBookingFlowContext()
return isLoggedIn
}

View File

@@ -14,6 +14,7 @@ export async function createAppContext() {
const headersList = await headers()
const ctx = createContext({
app: "partner-sas",
lang: headersList.get("x-lang") as Lang,
pathname: headersList.get("x-pathname")!,
uid: headersList.get("x-uid"),

View File

@@ -1,17 +1,50 @@
"use client"
import { BookingFlowContextProvider } from "@scandic-hotels/booking-flow/BookingFlowContextProvider"
import { trpc } from "@scandic-hotels/trpc/client"
import { useIsUserLoggedIn } from "@/hooks/useIsUserLoggedIn"
import type { ReactNode } from "react"
import type { ComponentProps, ReactNode } from "react"
export function BookingFlowProviders({ children }: { children: ReactNode }) {
const isLoggedIn = useIsUserLoggedIn()
const user = useBookingFlowUser()
return (
<BookingFlowContextProvider data={{ isLoggedIn }}>
<BookingFlowContextProvider data={{ isLoggedIn, user }}>
{children}
</BookingFlowContextProvider>
)
}
type BookingFlowContextData = ComponentProps<
typeof BookingFlowContextProvider
>["data"]
type BookingFlowUser = BookingFlowContextData["user"]
function useBookingFlowUser(): BookingFlowUser {
const isLoggedIn = useIsUserLoggedIn()
const { data, isError, isLoading } = trpc.user.getSafely.useQuery(undefined, {
enabled: isLoggedIn,
})
if (isLoading) {
return { state: "loading", data: undefined }
}
if (isError || !data) {
return { state: "error", data: undefined }
}
return {
state: "loaded",
data: {
type: "scandic",
partnerLoyaltyNumber: null,
membershipNumber: data.membershipNumber,
firstName: data.firstName || null,
lastName: data.lastName || null,
email: data.email,
},
}
}

View File

@@ -24,6 +24,7 @@ export async function createAppContext() {
const loginType = headersList.get("loginType")
const ctx = createContext({
app: "scandic-web",
lang: headersList.get("x-lang") as Lang,
pathname: headersList.get("x-pathname")!,
uid: headersList.get("x-uid"),