From 7639daf79288013ca4ebe12cf3c0d0694f0f3cb6 Mon Sep 17 00:00:00 2001 From: Linus Flood Date: Thu, 15 Jan 2026 14:02:28 +0000 Subject: [PATCH] Merged in feat/3614-basicInfo (pull request #3427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(SW-3614): use new loyalty prop in basicProfile * feat(SW-3614): use new loyalty prop in basicProfile * PR fixes Approved-by: Matilda Landström --- .../(live)/(protected)/my-pages/layout.tsx | 14 +++++++---- .../(protected)/sas-x-scandic/link/page.tsx | 10 ++++---- .../SAS/LinkAccountBanner/index.tsx | 10 ++++---- .../lib/trpc/memoizedRequests/index.ts | 18 +++++++++------ .../navigation/mypages/getPrimaryLinks.ts | 18 +++++++-------- .../lib/routers/navigation/mypages/index.ts | 6 ++--- .../partners/sas/performLevelUpgrade.ts | 4 ++-- packages/trpc/lib/routers/user/helpers.ts | 1 + packages/trpc/lib/routers/user/output.ts | 23 +++++++++++++++---- .../routers/user/query/userTrackingInfo.ts | 13 +++++++---- .../lib/routers/user/utils/getBasicUser.ts | 9 ++++++-- packages/trpc/lib/types/user.ts | 3 +++ 12 files changed, 83 insertions(+), 46 deletions(-) diff --git a/apps/scandic-web/app/[lang]/(live)/(protected)/my-pages/layout.tsx b/apps/scandic-web/app/[lang]/(live)/(protected)/my-pages/layout.tsx index 85d1cd06f..346d2af71 100644 --- a/apps/scandic-web/app/[lang]/(live)/(protected)/my-pages/layout.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(protected)/my-pages/layout.tsx @@ -1,8 +1,12 @@ import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK" -import { getEurobonusMembership } from "@scandic-hotels/trpc/routers/user/helpers" +import { + getEurobonusMembership, + scandicMembershipTypes, +} from "@scandic-hotels/trpc/routers/user/helpers" import { env } from "@/env/server" import { + getBasicProfileSafely, getProfileSafely, getProfilingConsent, } from "@/lib/trpc/memoizedRequests" @@ -85,10 +89,10 @@ async function MyPagesLayoutBase({ breadcrumbs, children, }: MyPagesLayoutProps) { - const profile = await getProfileSafely() - const eurobonusMembership = profile?.loyalty - ? getEurobonusMembership(profile.loyalty) - : null + const profile = await getBasicProfileSafely() + const eurobonusMembership = profile?.loyalty?.memberships?.find( + (m) => m.membershipType === scandicMembershipTypes.SAS_EB + ) return (
diff --git a/apps/scandic-web/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/link/page.tsx b/apps/scandic-web/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/link/page.tsx index 2b57ba93f..2bcc0c952 100644 --- a/apps/scandic-web/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/link/page.tsx +++ b/apps/scandic-web/app/[lang]/(partner)/(sas)/(protected)/sas-x-scandic/link/page.tsx @@ -1,9 +1,9 @@ import { redirect } from "next/navigation" import React from "react" -import { getEurobonusMembership } from "@scandic-hotels/trpc/routers/user/helpers" +import { scandicMembershipTypes } from "@scandic-hotels/trpc/routers/user/helpers" -import { getProfileSafely } from "@/lib/trpc/memoizedRequests" +import { getBasicProfileSafely } from "@/lib/trpc/memoizedRequests" import { SASModal } from "../components/SASModal" import { LinkAccountForm } from "./LinkAccountForm" @@ -12,11 +12,13 @@ export default async function SASxScandicLinkPage( props: PageProps<"/[lang]/sas-x-scandic/link"> ) { const params = await props.params - const profile = await getProfileSafely() + const profile = await getBasicProfileSafely() if (!profile || !profile.loyalty) return null - const eurobonusMembership = getEurobonusMembership(profile.loyalty) + const eurobonusMembership = profile.loyalty.memberships?.some( + (m) => m.membershipType === scandicMembershipTypes.SAS_EB + ) if (eurobonusMembership) { redirect(`/${params.lang}/sas-x-scandic/error?errorCode=alreadyLinked`) diff --git a/apps/scandic-web/components/Blocks/DynamicContent/SAS/LinkAccountBanner/index.tsx b/apps/scandic-web/components/Blocks/DynamicContent/SAS/LinkAccountBanner/index.tsx index d8ba87c54..c5ab133fb 100644 --- a/apps/scandic-web/components/Blocks/DynamicContent/SAS/LinkAccountBanner/index.tsx +++ b/apps/scandic-web/components/Blocks/DynamicContent/SAS/LinkAccountBanner/index.tsx @@ -1,9 +1,9 @@ import ButtonLink from "@scandic-hotels/design-system/ButtonLink" import Image from "@scandic-hotels/design-system/Image" import { Typography } from "@scandic-hotels/design-system/Typography" -import { getEurobonusMembership } from "@scandic-hotels/trpc/routers/user/helpers" +import { scandicMembershipTypes } from "@scandic-hotels/trpc/routers/user/helpers" -import { getProfile } from "@/lib/trpc/memoizedRequests" +import { getBasicProfileSafely } from "@/lib/trpc/memoizedRequests" import { getIntl } from "@/i18n" import desktopBackground from "@/public/_static/img/sas/sas-scandic-link-account-banner-desktop.png" @@ -17,12 +17,14 @@ export default async function SASLinkAccountBanner( props: DynamicContentProps["dynamic_content"] ) { const intl = await getIntl() - const user = await getProfile() + const user = await getBasicProfileSafely() if (!user || "error" in user || !user.loyalty) { return null } - const sasMembership = getEurobonusMembership(user.loyalty) + const sasMembership = user.loyalty.memberships?.some( + (m) => m.membershipType === scandicMembershipTypes.SAS_EB + ) if (sasMembership) { return null } diff --git a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts index 6f474239d..f5d993c1a 100644 --- a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts +++ b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts @@ -22,6 +22,17 @@ export const getBasicProfile = cache(async function getMemoizedBasicProfile() { return caller.user.getBasic() }) +export const getBasicProfileSafely = cache( + async function getMemoizedBasicProfile() { + try { + const caller = await serverClient() + return caller.user.getBasic() + } catch { + return null + } + } +) + export const getProfileSafely = cache( async function getMemoizedProfileSafely() { const caller = await serverClient() @@ -59,13 +70,6 @@ export const getMembershipLevelSafely = cache( } ) -export const getMembershipCards = cache( - async function getMemoizedMembershipCards() { - const caller = await serverClient() - return caller.user.membershipCards() - } -) - export const getHotelsByCSFilter = cache(async function getMemoizedHotels( input: GetHotelsByCSFilterInput ) { diff --git a/packages/trpc/lib/routers/navigation/mypages/getPrimaryLinks.ts b/packages/trpc/lib/routers/navigation/mypages/getPrimaryLinks.ts index 2a8d9b486..72373c069 100644 --- a/packages/trpc/lib/routers/navigation/mypages/getPrimaryLinks.ts +++ b/packages/trpc/lib/routers/navigation/mypages/getPrimaryLinks.ts @@ -3,22 +3,25 @@ import { cache } from "react" import * as routes from "@scandic-hotels/common/constants/routes/myPages" import { safeTry } from "@scandic-hotels/common/utils/safeTry" -import { getEurobonusMembership } from "../../user/helpers" +import { scandicMembershipTypes } from "../../user/helpers" import type { Lang } from "@scandic-hotels/common/constants/language" -import type { UserLoyalty } from "../../../types/user" +import type { BasicUserProfile } from "../../../types/user" import type { MyPagesLink } from "./MyPagesLink" export const getPrimaryLinks = cache( async ({ lang, - userLoyalty, + basicUserLoyalty, }: { lang: Lang - userLoyalty?: UserLoyalty + basicUserLoyalty?: BasicUserProfile["loyalty"] }): Promise => { - const showSASLink = userLoyalty ? isScandicXSASActive(userLoyalty) : false + const showSASLink = basicUserLoyalty?.memberships?.some( + (m) => m.membershipType === scandicMembershipTypes.SAS_EB + ) + const [showTeamMemberLink] = await safeTry(showTeamMemberCard()) const menuItems: MyPagesLink[] = [ @@ -64,11 +67,6 @@ export const getPrimaryLinks = cache( } ) -const isScandicXSASActive = (loyalty: UserLoyalty) => { - const eurobonusMembership = getEurobonusMembership(loyalty) - return Boolean(eurobonusMembership) -} - const showTeamMemberCard = cache(async () => { async function getIsTeamMember() { // TODO: Implement this check diff --git a/packages/trpc/lib/routers/navigation/mypages/index.ts b/packages/trpc/lib/routers/navigation/mypages/index.ts index 05f3a4bd4..d4ec1579b 100644 --- a/packages/trpc/lib/routers/navigation/mypages/index.ts +++ b/packages/trpc/lib/routers/navigation/mypages/index.ts @@ -6,7 +6,7 @@ import { safeTry } from "@scandic-hotels/common/utils/safeTry" import { safeProtectedProcedure } from "../../../procedures" import { isValidSession } from "../../../utils/session" -import { getVerifiedUser } from "../../user/utils/getVerifiedUser" +import { getBasicUser } from "../../user/utils/getBasicUser" import { getPrimaryLinks } from "./getPrimaryLinks" import { getSecondaryLinks } from "./getSecondaryLinks" @@ -40,14 +40,14 @@ export const myPagesNavigation = safeProtectedProcedure } const [user, error] = await safeTry( - getVerifiedUser({ token: ctx.session.token }) + getBasicUser({ token: ctx.session.token }) ) if (!user || error) { return null } const [primaryLinks, secondaryLinks] = await Promise.all([ - getPrimaryLinks({ lang, userLoyalty: user.loyalty }), + getPrimaryLinks({ lang, basicUserLoyalty: user.loyalty }), getSecondaryLinks({ lang }), ]) diff --git a/packages/trpc/lib/routers/partners/sas/performLevelUpgrade.ts b/packages/trpc/lib/routers/partners/sas/performLevelUpgrade.ts index d3c48635a..f08ecc1a0 100644 --- a/packages/trpc/lib/routers/partners/sas/performLevelUpgrade.ts +++ b/packages/trpc/lib/routers/partners/sas/performLevelUpgrade.ts @@ -35,10 +35,10 @@ export const performLevelUpgrade = protectedProcedure const [profile, error] = await safeTry( getBasicUser({ token: ctx.session.token }) ) - if (!profile || error) { + if (!profile || error || !profile.loyalty?.tier) { return { tierMatchState: "error" } } - const currentLevel = profile.tier + const currentLevel = profile.loyalty.tier sasLogger.debug("tier match started") diff --git a/packages/trpc/lib/routers/user/helpers.ts b/packages/trpc/lib/routers/user/helpers.ts index 1f2ead6ca..e0bddbcc5 100644 --- a/packages/trpc/lib/routers/user/helpers.ts +++ b/packages/trpc/lib/routers/user/helpers.ts @@ -8,6 +8,7 @@ import type { export enum scandicMembershipTypes { SCANDIC_NATIVE = "SCANDIC_NATIVE", SAS_EB = "SAS_EB", + "OTHER" = "OTHER", } export function isScandicNativeMembership( diff --git a/packages/trpc/lib/routers/user/output.ts b/packages/trpc/lib/routers/user/output.ts index 09aff0e48..860479771 100644 --- a/packages/trpc/lib/routers/user/output.ts +++ b/packages/trpc/lib/routers/user/output.ts @@ -2,7 +2,7 @@ import { z } from "zod" import { countriesMap } from "../../constants/countries" import { imageSchema } from "../../routers/hotels/schemas/image" -import { getFriendsMembership } from "./helpers" +import { getFriendsMembership, scandicMembershipTypes } from "./helpers" const scandicFriendsTier = z.enum(["L1", "L2", "L3", "L4", "L5", "L6", "L7"]) const sasEurobonusTier = z.enum(["EBB", "EBS", "EBG", "EBD", "EBP"]) @@ -23,7 +23,7 @@ const otherMembershipSchema = z export const sasMembershipSchema = z .object({ - type: z.literal("SAS_EB"), + type: z.literal(scandicMembershipTypes.SAS_EB), tier: sasEurobonusTier, nextTier: sasEurobonusTier.nullish(), spendablePoints: z.number().nullish(), @@ -43,7 +43,7 @@ export const sasMembershipSchema = z export const friendsMembershipSchema = z .object({ - type: z.literal("SCANDIC_NATIVE"), + type: z.literal(scandicMembershipTypes.SCANDIC_NATIVE), tier: scandicFriendsTier, nextTier: scandicFriendsTier.nullish(), pointsToNextTier: z.number().nullish(), @@ -140,8 +140,6 @@ export const getBasicUserSchema = z.object({ lastName: z.string(), phoneNumber: z.string().optional(), profileId: z.string().optional(), - membershipNumber: z.string(), - tier: scandicFriendsTier, address: z .object({ city: z.string().optional(), @@ -152,6 +150,21 @@ export const getBasicUserSchema = z.object({ }) .optional() .nullable(), + loyalty: z + .object({ + tier: scandicFriendsTier, + tierExpires: z.string(), + memberships: z.array( + z.object({ + membershipType: z + .nativeEnum(scandicMembershipTypes) + .catch(scandicMembershipTypes.OTHER), + membershipNumber: z.string(), + }) + ), + }) + .optional() + .nullable(), }) export const creditCardSchema = z diff --git a/packages/trpc/lib/routers/user/query/userTrackingInfo.ts b/packages/trpc/lib/routers/user/query/userTrackingInfo.ts index 072cdd5ba..13257929d 100644 --- a/packages/trpc/lib/routers/user/query/userTrackingInfo.ts +++ b/packages/trpc/lib/routers/user/query/userTrackingInfo.ts @@ -3,6 +3,7 @@ import { createCounter } from "@scandic-hotels/common/telemetry" import { safeProtectedProcedure } from "../../../procedures" import { isValidSession } from "../../../utils/session" import { getEuroBonusProfileData } from "../../partners/sas/getEuroBonusProfile" +import { scandicMembershipTypes } from "../helpers" import { getBasicUser } from "../utils/getBasicUser" import type { LoginType } from "@scandic-hotels/common/constants/loginType" @@ -62,8 +63,10 @@ async function getScandicFriendsUserTrackingData(session: Session | null) { loginStatus: "logged in", loginType: session.token.loginType as LoginType, memberId: verifiedUserData.profileId, - membershipNumber: verifiedUserData.membershipNumber, - memberLevel: verifiedUserData?.tier, + membershipNumber: verifiedUserData.loyalty?.memberships?.find( + (m) => m.membershipType === scandicMembershipTypes.SCANDIC_NATIVE + )?.membershipNumber, + memberLevel: verifiedUserData.loyalty?.tier, loginAction: "login success", memberType, } @@ -134,7 +137,9 @@ async function getScandicFriendsDataHelper(scandicUserToken: string | null) { return { memberId: verifiedUserData.profileId, - membershipNumber: verifiedUserData.membershipNumber, - memberLevel: verifiedUserData.tier, + membershipNumber: verifiedUserData.loyalty?.memberships?.find( + (m) => m.membershipType === scandicMembershipTypes.SCANDIC_NATIVE + )?.membershipNumber, + memberLevel: verifiedUserData.loyalty?.tier, } } diff --git a/packages/trpc/lib/routers/user/utils/getBasicUser.ts b/packages/trpc/lib/routers/user/utils/getBasicUser.ts index b12b260ae..d9f4006ce 100644 --- a/packages/trpc/lib/routers/user/utils/getBasicUser.ts +++ b/packages/trpc/lib/routers/user/utils/getBasicUser.ts @@ -9,6 +9,7 @@ import { serverErrorByStatus, sessionExpiredError, } from "../../../errors" +import { scandicMembershipTypes } from "../helpers" import { getBasicUserSchema } from "../output" import type z from "zod" @@ -63,15 +64,19 @@ export const getBasicUser = cache( function addUserToSentry(apiJson: unknown) { const typedData = apiJson as DeepPartial> + const memberShipNumber = typedData?.loyalty?.memberships?.find( + (m) => m?.membershipType === scandicMembershipTypes.SCANDIC_NATIVE + )?.membershipNumber + if ( typeof typedData?.profileId === "undefined" || - typeof typedData?.membershipNumber === "undefined" + typeof memberShipNumber === "undefined" ) { return } Sentry.setUser({ id: typedData?.profileId, - username: typedData?.membershipNumber, + username: memberShipNumber, }) } diff --git a/packages/trpc/lib/types/user.ts b/packages/trpc/lib/types/user.ts index 2bad5cd9c..da792fcfc 100644 --- a/packages/trpc/lib/types/user.ts +++ b/packages/trpc/lib/types/user.ts @@ -4,6 +4,7 @@ import type { getFriendsMembership } from "../routers/user/helpers" import type { creditCardSchema, friendsMembershipSchema, + getBasicUserSchema, getUserSchema, sasMembershipSchema, userLoyaltySchema, @@ -23,6 +24,8 @@ export type Membership = UserLoyalty["memberships"][number] export type NativeFriendsMembership = z.output export type EurobonusMembership = z.output +export type BasicUserProfile = z.output + export type FriendsMembership = ReturnType export type EurobonusTier = EurobonusMembership["tier"]