Merged in chore/move-enter-details (pull request #2778)

Chore/move enter details

Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2025-09-11 07:16:24 +00:00
parent 15711cb3a4
commit 7dee6d5083
238 changed files with 1656 additions and 1602 deletions

View File

@@ -1,6 +1,9 @@
import { z } from "zod"
import { findMyBooking } from "@scandic-hotels/common/constants/routes/findMyBooking"
import {
type FindMyBookingRoute,
findMyBookingRoutes,
} from "@scandic-hotels/common/constants/routes/findMyBookingRoutes"
import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/imageVault"
@@ -168,9 +171,9 @@ export interface RawMetadataSchema extends z.output<typeof rawMetadataSchema> {}
// Several pages are not currently routed within contentstack context.
// This function is used to generate the urls for these pages.
export function getNonContentstackUrls(lang: Lang, pathName: string) {
if (Object.values(findMyBooking).includes(pathName)) {
if (isFindMyBookingUrl(pathName)) {
const urls: LanguageSwitcherData = {}
return Object.entries(findMyBooking).reduce((acc, [lang, url]) => {
return Object.entries(findMyBookingRoutes).reduce((acc, [lang, url]) => {
acc[lang as Lang] = { url }
return urls
}, urls)
@@ -203,3 +206,9 @@ const baseUrls: LanguageSwitcherData = {
function hotelreservation(lang: Lang) {
return `/${lang}/hotelreservation`
}
function isFindMyBookingUrl(value: string): value is FindMyBookingRoute {
return Object.values(
findMyBookingRoutes as unknown as readonly string[]
).includes(value)
}

View File

@@ -488,6 +488,7 @@ export const locationsSchema = z.object({
),
})
export type BreakfastPackages = z.output<typeof breakfastPackagesSchema>
export const breakfastPackagesSchema = z
.object({
data: z.object({
@@ -503,8 +504,6 @@ export const breakfastPackagesSchema = z
.transform(({ data }) =>
data.attributes.packages.filter((pkg) => pkg.code?.match(/^(BRF\d+)$/gm))
)
export interface BreakfastPackages
extends z.output<typeof breakfastPackagesSchema> {}
export const ancillaryPackagesSchema = z
.object({

View File

@@ -51,6 +51,7 @@ export const packageSchema = z.object({
requestedPrice: packagePriceSchema,
})
export type BreakfastPackage = z.output<typeof breakfastPackageSchema>
export const breakfastPackageSchema = z.object({
code: z.string(),
description: z.string(),
@@ -61,8 +62,6 @@ export const breakfastPackageSchema = z.object({
PackageTypeEnum.BreakfastChildren,
]),
})
export interface BreakfastPackage
extends z.output<typeof breakfastPackageSchema> {}
export const ancillaryPackageSchema = z.object({
categoryName: z.string(),

View File

@@ -61,6 +61,7 @@ export const productTypePriceSchema = z
})
.merge(partialPriceSchema)
export type ProductTypePointsSchema = z.output<typeof productTypePriceSchema>
export const productTypePointsSchema = z
.object({
localPrice: redemptionSchema,

View File

@@ -5,9 +5,7 @@ import { Lang } from "@scandic-hotels/common/constants/language"
import { signUpSchema } from "./schemas"
// Query
export const userTrackingInput = z.object({
lang: z.nativeEnum(Lang).optional(),
})
export const staysInput = z
.object({
cursor: z

View File

@@ -1,38 +1,34 @@
import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../.."
import * as api from "../../api"
import { Transactions } from "../../enums/transactions"
import { router } from "../../.."
import * as api from "../../../api"
import { Transactions } from "../../../enums/transactions"
import {
languageProtectedProcedure,
protectedProcedure,
safeProtectedProcedure,
} from "../../procedures"
} from "../../../procedures"
import {
getFriendsMembership,
getMembershipCards,
} from "../../routers/user/helpers"
import { getVerifiedUser } from "../../routers/user/utils"
import { toApiLang } from "../../utils"
import { isValidSession } from "../../utils/session"
} from "../../../routers/user/helpers"
import { getVerifiedUser } from "../../../routers/user/utils"
import { toApiLang } from "../../../utils"
import { isValidSession } from "../../../utils/session"
import {
friendTransactionsInput,
getSavedPaymentCardsInput,
staysInput,
userTrackingInput,
} from "./input"
import { getFriendTransactionsSchema } from "./output"
} from "../input"
import { getFriendTransactionsSchema } from "../output"
import {
getCreditCards,
getPreviousStays,
getUpcomingStays,
parsedUser,
updateStaysBookingUrl,
} from "./utils"
import type { LoginType } from "@scandic-hotels/common/constants/loginType"
import type { TrackingUserData } from "../types"
} from "../utils"
import { userTrackingInfo } from "./userTrackingInfo"
export const userQueryRouter = router({
get: protectedProcedure
@@ -135,81 +131,7 @@ export const userQueryRouter = router({
const membershipLevel = getFriendsMembership(verifiedData.data.loyalty)
return membershipLevel
}),
userTrackingInfo: safeProtectedProcedure
.input(userTrackingInput)
.query(async function ({ ctx, input }) {
const { lang } = input
const language = lang || ctx.lang
const userTrackingInfoCounter = createCounter("user", "userTrackingInfo")
const metricsUserTrackingInfo = userTrackingInfoCounter.init()
metricsUserTrackingInfo.start()
const notLoggedInUserTrackingData: TrackingUserData = {
loginStatus: "Non-logged in",
}
if (!isValidSession(ctx.session)) {
metricsUserTrackingInfo.success({
reason: "invalid session",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
try {
const verifiedUserData = await getVerifiedUser({ session: ctx.session })
if (
!verifiedUserData ||
"error" in verifiedUserData ||
!verifiedUserData.data.loyalty
) {
metricsUserTrackingInfo.success({
reason: "invalid user data",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
const previousStaysData = await getPreviousStays(
ctx.session.token.access_token,
1,
language
)
if (!previousStaysData) {
metricsUserTrackingInfo.success({
reason: "no previous stays data",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
const membership = getFriendsMembership(verifiedUserData.data.loyalty)
const loggedInUserTrackingData: TrackingUserData = {
loginStatus: "logged in",
loginType: ctx.session.token.loginType as LoginType,
memberId: verifiedUserData.data.profileId,
membershipNumber: membership?.membershipNumber,
memberLevel: membership?.membershipLevel,
noOfNightsStayed: previousStaysData.links?.totalCount ?? 0,
totalPointsAvailableToSpend: membership?.currentPoints,
loginAction: "login success",
}
metricsUserTrackingInfo.success({
reason: "valid logged in",
data: loggedInUserTrackingData,
})
return loggedInUserTrackingData
} catch (error) {
metricsUserTrackingInfo.fail(error)
return notLoggedInUserTrackingData
}
}),
userTrackingInfo,
stays: router({
previous: languageProtectedProcedure

View File

@@ -0,0 +1,92 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { safeProtectedProcedure } from "../../../procedures"
import { isValidSession } from "../../../utils/session"
import { getFriendsMembership } from "../helpers"
import { getPreviousStays, getVerifiedUser } from "../utils"
import type { LoginType } from "@scandic-hotels/common/constants/loginType"
import type { TrackingUserData } from "../../types"
export const userTrackingInput = z.object({
lang: z.nativeEnum(Lang).optional(),
})
export const userTrackingInfo = safeProtectedProcedure
.input(userTrackingInput)
.query(async function ({ ctx, input }) {
const { lang } = input
const language = lang || ctx.lang
const userTrackingInfoCounter = createCounter("user", "userTrackingInfo")
const metricsUserTrackingInfo = userTrackingInfoCounter.init()
metricsUserTrackingInfo.start()
const notLoggedInUserTrackingData: TrackingUserData = {
loginStatus: "Non-logged in",
}
if (!isValidSession(ctx.session)) {
metricsUserTrackingInfo.success({
reason: "invalid session",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
try {
const verifiedUserData = await getVerifiedUser({ session: ctx.session })
if (
!verifiedUserData ||
"error" in verifiedUserData ||
!verifiedUserData.data.loyalty
) {
metricsUserTrackingInfo.success({
reason: "invalid user data",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
const previousStaysData = await getPreviousStays(
ctx.session.token.access_token,
1,
language
)
if (!previousStaysData) {
metricsUserTrackingInfo.success({
reason: "no previous stays data",
data: notLoggedInUserTrackingData,
})
return notLoggedInUserTrackingData
}
const membership = getFriendsMembership(verifiedUserData.data.loyalty)
const loggedInUserTrackingData: TrackingUserData = {
loginStatus: "logged in",
loginType: ctx.session.token.loginType as LoginType,
memberId: verifiedUserData.data.profileId,
membershipNumber: membership?.membershipNumber,
memberLevel: membership?.membershipLevel,
noOfNightsStayed: previousStaysData.links?.totalCount ?? 0,
totalPointsAvailableToSpend: membership?.currentPoints,
loginAction: "login success",
}
metricsUserTrackingInfo.success({
reason: "valid logged in",
data: loggedInUserTrackingData,
})
return loggedInUserTrackingData
} catch (error) {
metricsUserTrackingInfo.fail(error)
return notLoggedInUserTrackingData
}
})

View File

@@ -27,6 +27,7 @@
"./routers/contentstack/*": "./lib/routers/contentstack/*.ts",
"./routers/hotels/*": "./lib/routers/hotels/*.ts",
"./routers/booking/*": "./lib/routers/booking/*.ts",
"./routers/user/query": "./lib/routers/user/query/index.ts",
"./routers/user/*": "./lib/routers/user/*.ts",
"./routers/partners/*": "./lib/routers/partners/*.ts",
"./routers/autocomplete/*": "./lib/routers/autocomplete/*.ts",