Merged in feat/LOY-431-profile-v2 (pull request #3202)

Feat/LOY-431: Switch to V2 of Profile endpoint

* feat(LOY-431): switch to v2 of profile endpoint

* feat(LOY-431): use CreditCard

* feat(LOY-431): remove hotelinformation from friendTransaction schema

* chore(LOY-431): add hotel data request to transactions

* fix(LOY-431): use v1 of friendTransactions


Approved-by: Linus Flood
Approved-by: Erik Tiekstra
Approved-by: Anton Gunnarsson
This commit is contained in:
Matilda Landström
2025-11-28 13:58:06 +00:00
parent c29b724317
commit 22dd2f60fe
15 changed files with 98 additions and 81 deletions

View File

@@ -156,7 +156,7 @@ export const editProfile = protectedServerActionProcedure
) )
} }
const apiResponse = await api.patch(api.endpoints.v1.Profile.profile, { const apiResponse = await api.patch(api.endpoints.v2.Profile.profile, {
body, body,
cache: "no-store", cache: "no-store",
headers: { headers: {

View File

@@ -4,8 +4,8 @@ import { usePathname } from "next/navigation"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import Link from "@scandic-hotels/design-system/OldDSLink"
import Table from "@scandic-hotels/design-system/Table" import Table from "@scandic-hotels/design-system/Table"
import { TextLink } from "@scandic-hotels/design-system/TextLink"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { Transactions } from "@scandic-hotels/trpc/enums/transactions" import { Transactions } from "@scandic-hotels/trpc/enums/transactions"
@@ -15,7 +15,11 @@ import useLang from "@/hooks/useLang"
import AwardPoints from "../../../AwardPoints" import AwardPoints from "../../../AwardPoints"
import type { RowProps } from "@/types/components/myPages/myPage/earnAndBurn" import type { Transaction } from "@/types/components/myPages/myPage/earnAndBurn"
interface RowProps {
transaction: Transaction
}
export default function Row({ transaction }: RowProps) { export default function Row({ transaction }: RowProps) {
const intl = useIntl() const intl = useIntl()
@@ -23,6 +27,7 @@ export default function Row({ transaction }: RowProps) {
const pathName = usePathname() const pathName = usePathname()
const isWebview = webviews.includes(pathName) const isWebview = webviews.includes(pathName)
const { hotelName, city } = transaction
const nightsMsg = intl.formatMessage( const nightsMsg = intl.formatMessage(
{ {
id: "booking.numberOfNights", id: "booking.numberOfNights",
@@ -39,8 +44,8 @@ export default function Row({ transaction }: RowProps) {
id: "earnAndBurn.journeyTable.pointsActivity", id: "earnAndBurn.journeyTable.pointsActivity",
defaultMessage: "Point activity", defaultMessage: "Point activity",
}) })
: transaction.hotelName && transaction.city : hotelName && city
? `${transaction.hotelName}, ${transaction.city} ${nightsMsg}` ? `${hotelName}, ${city} ${nightsMsg}`
: `${nightsMsg}` : `${nightsMsg}`
switch (transaction.type) { switch (transaction.type) {
@@ -107,9 +112,9 @@ export default function Row({ transaction }: RowProps) {
transaction.type === Transactions.rewardType.rewardNight) transaction.type === Transactions.rewardType.rewardNight)
) { ) {
return ( return (
<Link textDecoration="underline" href={transaction.bookingUrl}> <TextLink href={transaction.bookingUrl}>
{transaction.confirmationNumber} {transaction.confirmationNumber}
</Link> </TextLink>
) )
} }

View File

@@ -9,7 +9,11 @@ import Row from "./Row"
import styles from "./clientTable.module.css" import styles from "./clientTable.module.css"
import type { ClientTableProps } from "@/types/components/myPages/myPage/earnAndBurn" import type { Transactions } from "@/types/components/myPages/myPage/earnAndBurn"
interface ClientTableProps {
transactions: Transactions
}
export default function ClientTable({ transactions }: ClientTableProps) { export default function ClientTable({ transactions }: ClientTableProps) {
const intl = useIntl() const intl = useIntl()

View File

@@ -7,17 +7,8 @@ type TransactionResponse = Awaited<
ReturnType<UserQueryRouter["transaction"]["friendTransactions"]> ReturnType<UserQueryRouter["transaction"]["friendTransactions"]>
> >
type TransactionsNonNullResponseObject = NonNullable<TransactionResponse> type TransactionsNonNullResponseObject = NonNullable<TransactionResponse>
type Transactions = export type Transactions =
NonNullable<TransactionsNonNullResponseObject>["data"]["transactions"] NonNullable<TransactionsNonNullResponseObject>["data"]["transactions"]
type Transaction = export type Transaction = Transactions[number]
NonNullable<TransactionsNonNullResponseObject>["data"]["transactions"][number]
export interface ClientTableProps {
transactions: Transactions
}
export interface RowProps {
transaction: Transaction
}
export type AwardPointsVariantProps = VariantProps<typeof awardPointsVariants> export type AwardPointsVariantProps = VariantProps<typeof awardPointsVariants>

View File

@@ -18,6 +18,7 @@ export namespace endpoints {
Breakfast = "breakfast", Breakfast = "breakfast",
Cities = "Cities", Cities = "Cities",
Countries = "Countries", Countries = "Countries",
CreditCard = "CreditCard",
Hotels = "Hotels", Hotels = "Hotels",
Locations = "Locations", Locations = "Locations",
Packages = "packages", Packages = "packages",
@@ -173,42 +174,6 @@ export namespace endpoints {
* https://tstapi.scandichotels.com/profile/swagger/v1/index.html * https://tstapi.scandichotels.com/profile/swagger/v1/index.html
*/ */
export namespace Profile { export namespace Profile {
export const invalidateSessions = `${base.path.profile}/${version}/${base.enitity.Profile}/invalidateSessions`
export const membership = `${base.path.profile}/${version}/${base.enitity.Profile}/membership`
export const profile = `${base.path.profile}/${version}/${base.enitity.Profile}`
export const subscriberId = `${base.path.profile}/${version}/${base.enitity.Profile}/SubscriberId`
export const link = `${base.path.profile}/${version}/${base.enitity.Profile}/link`
export const unlink = `${base.path.profile}/${version}/${base.enitity.Profile}/Unlink`
export const matchTier = `${base.path.profile}/${version}/${base.enitity.Profile}/MatchTier`
export const pointTransfer = `${base.path.profile}/${version}/${base.enitity.Profile}/PointTransfer/Partner`
export function deleteProfile(profileId: string) {
return `${profile}/${profileId}`
}
export const creditCards = `${base.path.profile}/${version}/${base.enitity.Profile}/creditCards`
export namespace CreditCards {
export const initiateSaveCard = `${creditCards}/initiateSaveCard`
export function deleteCreditCard(creditCardId: string) {
return `${creditCards}/${creditCardId}`
}
export function transaction(transactionId: string) {
return `${creditCards}/${transactionId}`
}
}
export namespace Reward {
export const allTiers = `${base.path.profile}/${version}/${base.enitity.Reward}/allTiers`
export const reward = `${base.path.profile}/${version}/${base.enitity.Reward}`
export const redeem = `${base.path.profile}/${version}/${base.enitity.Reward}/redeem`
export const unwrap = `${base.path.profile}/${version}/${base.enitity.Reward}/unwrap`
export function claim(rewardId: string) {
return `${base.path.profile}/${version}/${base.enitity.Reward}/Claim/${rewardId}`
}
}
export const enum Transaction { export const enum Transaction {
friendTransactions = `${base.path.profile}/${version}/${base.enitity.Transaction}/friendTransactions`, friendTransactions = `${base.path.profile}/${version}/${base.enitity.Transaction}/friendTransactions`,
} }
@@ -224,14 +189,60 @@ export namespace endpoints {
*/ */
export namespace Profile { export namespace Profile {
export const profile = `${base.path.profile}/${version}/${base.enitity.Profile}` export const profile = `${base.path.profile}/${version}/${base.enitity.Profile}`
export const basicProfile = `${profile}/BasicInfo` export const basicProfile = `${profile}/BasicInfo`
export const promoCampaign = `${profile}/Promotion` export const promoCampaign = `${profile}/Promotion`
export const membership = `${profile}/membership`
export const subscriberId = `${profile}/SubscriberId`
export const invalidateSessions = `${profile}/invalidateSessions`
export const link = `${profile}/link`
export const unlink = `${profile}/Unlink`
export const matchTier = `${profile}/MatchTier`
export const pointTransfer = `${profile}/PointTransfer/Partner`
export function deleteProfile(profileId: string) {
return `${profile}/${profileId}`
}
export function teamMemberCard(employeeId: string) { export function teamMemberCard(employeeId: string) {
return `${profile}/${employeeId}/TeamMemberCard` return `${profile}/${employeeId}/TeamMemberCard`
} }
export namespace CreditCard {
export const creditCard = `${base.path.profile}/${version}/${base.enitity.CreditCard}`
export const initiateSaveCard = `${creditCard}/initiateSaveCard`
export function deleteCreditCard(creditCardId: string) {
return `${creditCard}/${creditCardId}`
}
export function transaction(transactionId: string) {
return `${creditCard}/${transactionId}`
}
}
export namespace Reward {
export const reward = `${base.path.profile}/${version}/${base.enitity.Reward}`
export const allTiers = `${reward}/allTiers`
export const redeem = `${reward}/redeem`
export const unwrap = `${reward}/unwrap`
export function claim(rewardId: string) {
return `${reward}/Claim/${rewardId}`
}
}
/* Currently no hotelInformation in v2 */
export const enum Transaction {
friendTransactions = `${base.path.profile}/${version}/${base.enitity.Transaction}/friendTransactions`,
}
} }
} }
} }
export type Endpoint = string export type Endpoint = string

View File

@@ -188,7 +188,7 @@ export const rewardQueryRouter = router({
metricsGetCurrentReward.start() metricsGetCurrentReward.start()
const apiResponse = await api.get( const apiResponse = await api.get(
api.endpoints.v1.Profile.Reward.reward, api.endpoints.v2.Profile.Reward.reward,
{ {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
@@ -246,7 +246,7 @@ export const rewardQueryRouter = router({
metricsGetSurprises.start() metricsGetSurprises.start()
const endpoint = api.endpoints.v1.Profile.Reward.reward const endpoint = api.endpoints.v2.Profile.Reward.reward
const apiResponse = await api.get(endpoint, { const apiResponse = await api.get(endpoint, {
cache: undefined, cache: undefined,
@@ -316,7 +316,7 @@ export const rewardQueryRouter = router({
metricsGetUnwrapSurprise.start() metricsGetUnwrapSurprise.start()
const apiResponse = await api.post( const apiResponse = await api.post(
api.endpoints.v1.Profile.Reward.unwrap, api.endpoints.v2.Profile.Reward.unwrap,
{ {
body: { body: {
rewardId, rewardId,
@@ -367,7 +367,7 @@ export const rewardQueryRouter = router({
metricGetRedeem.start() metricGetRedeem.start()
const apiResponse = await api.post( const apiResponse = await api.post(
api.endpoints.v1.Profile.Reward.redeem, api.endpoints.v2.Profile.Reward.redeem,
{ {
body: { body: {
rewardId, rewardId,

View File

@@ -48,7 +48,7 @@ export async function getCachedAllTierRewards(token: string) {
metricsGetApiRewardAllTiers.start() metricsGetApiRewardAllTiers.start()
const apiResponse = await api.get( const apiResponse = await api.get(
api.endpoints.v1.Profile.Reward.allTiers, api.endpoints.v2.Profile.Reward.allTiers,
{ {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,

View File

@@ -29,7 +29,7 @@ export const linkAccount = protectedProcedure
sasLogger.debug("[SAS] link account") sasLogger.debug("[SAS] link account")
const apiResponse = await api.post(api.endpoints.v1.Profile.link, { const apiResponse = await api.post(api.endpoints.v2.Profile.link, {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
}, },

View File

@@ -42,7 +42,7 @@ export const performLevelUpgrade = protectedProcedure
sasLogger.debug("tier match started") sasLogger.debug("tier match started")
const apiResponse = await api.post(api.endpoints.v1.Profile.matchTier, { const apiResponse = await api.post(api.endpoints.v2.Profile.matchTier, {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
}, },

View File

@@ -25,7 +25,7 @@ export const transferPoints = protectedProcedure
const { referenceId } = await getOTPState() const { referenceId } = await getOTPState()
const apiResponse = await api.post( const apiResponse = await api.post(
api.endpoints.v1.Profile.pointTransfer, api.endpoints.v2.Profile.pointTransfer,
{ {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,

View File

@@ -17,7 +17,7 @@ export const unlinkAccount = protectedProcedure
const sasAuthToken = await getSasToken() const sasAuthToken = await getSasToken()
const { referenceId } = await getOTPState() const { referenceId } = await getOTPState()
const apiResponse = await api.post(api.endpoints.v1.Profile.unlink, { const apiResponse = await api.post(api.endpoints.v2.Profile.unlink, {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
}, },

View File

@@ -30,7 +30,7 @@ export const userMutationRouter = router({
JSON.stringify({ query: { language: input.language } }) JSON.stringify({ query: { language: input.language } })
) )
const apiResponse = await api.post( const apiResponse = await api.post(
api.endpoints.v1.Profile.CreditCards.initiateSaveCard, api.endpoints.v2.Profile.CreditCard.initiateSaveCard,
{ {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
@@ -85,7 +85,7 @@ export const userMutationRouter = router({
JSON.stringify({}) JSON.stringify({})
) )
const apiResponse = await api.post( const apiResponse = await api.post(
api.endpoints.v1.Profile.CreditCards.transaction(input.transactionId), api.endpoints.v2.Profile.CreditCard.transaction(input.transactionId),
{ {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
@@ -121,7 +121,7 @@ export const userMutationRouter = router({
JSON.stringify({ query: {} }) JSON.stringify({ query: {} })
) )
const apiResponse = await api.remove( const apiResponse = await api.remove(
api.endpoints.v1.Profile.CreditCards.deleteCreditCard( api.endpoints.v2.Profile.CreditCard.deleteCreditCard(
input.creditCardId input.creditCardId
), ),
{ {
@@ -165,7 +165,7 @@ export const userMutationRouter = router({
metricsGeneratePreferencesLink.start() metricsGeneratePreferencesLink.start()
const apiResponse = await api.get(api.endpoints.v1.Profile.subscriberId, { const apiResponse = await api.get(api.endpoints.v2.Profile.subscriberId, {
headers: { headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`, Authorization: `Bearer ${ctx.session.token.access_token}`,
}, },

View File

@@ -309,14 +309,14 @@ export const userQueryRouter = router({
awardPoints: attributes.awardPoints, awardPoints: attributes.awardPoints,
checkinDate: attributes.checkinDate, checkinDate: attributes.checkinDate,
checkoutDate: attributes.checkoutDate, checkoutDate: attributes.checkoutDate,
city: attributes.hotelInformation?.city,
confirmationNumber: attributes.confirmationNumber, confirmationNumber: attributes.confirmationNumber,
hotelName: attributes.hotelInformation?.name,
nights: attributes.nights, nights: attributes.nights,
pointsCalculated: attributes.pointsCalculated, pointsCalculated: attributes.pointsCalculated,
hotelId: attributes.hotelOperaId, hotelId: attributes.hotelOperaId,
transactionDate: attributes.transactionDate, transactionDate: attributes.transactionDate,
bookingUrl: attributes.bookingUrl, bookingUrl: attributes.bookingUrl,
hotelName: attributes.hotelInformation?.name,
city: attributes.hotelInformation?.city,
} }
}), }),
}, },

View File

@@ -22,11 +22,14 @@ export const getCreditCards = cache(
metricsGetCreditCards.start() metricsGetCreditCards.start()
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, { const apiResponse = await api.get(
headers: { api.endpoints.v2.Profile.CreditCard.creditCard,
Authorization: `Bearer ${session.token.access_token}`, {
}, headers: {
}) Authorization: `Bearer ${session.token.access_token}`,
},
}
)
if (!apiResponse.ok) { if (!apiResponse.ok) {
await metricsGetCreditCards.httpError(apiResponse) await metricsGetCreditCards.httpError(apiResponse)

View File

@@ -135,11 +135,14 @@ export const getCreditCards = cache(
metricsGetCreditCards.start() metricsGetCreditCards.start()
const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, { const apiResponse = await api.get(
headers: { api.endpoints.v2.Profile.CreditCard.creditCard,
Authorization: `Bearer ${session.token.access_token}`, {
}, headers: {
}) Authorization: `Bearer ${session.token.access_token}`,
},
}
)
if (!apiResponse.ok) { if (!apiResponse.ok) {
await metricsGetCreditCards.httpError(apiResponse) await metricsGetCreditCards.httpError(apiResponse)