Merged in feat/SW-1488-unlink-sas-account (pull request #1349)
Implement unlink SAS flow Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -78,7 +78,10 @@ export async function GET(
|
||||
httpOnly: true,
|
||||
})
|
||||
|
||||
if (stateResult.data.intent === "link") {
|
||||
if (
|
||||
stateResult.data.intent === "link" ||
|
||||
stateResult.data.intent === "unlink"
|
||||
) {
|
||||
const [data, error] = await safeTry(
|
||||
serverClient().partner.sas.requestOtp({})
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ export default async function SasXScandicLayout({
|
||||
<Link className={styles.backLink} href={profileOverview[params.lang]}>
|
||||
<ArrowLeft height={20} width={20} />
|
||||
<span className={styles.long}>
|
||||
{intl.formatMessage({ id: "Back to Scandichotels.com" })}
|
||||
{intl.formatMessage({ id: "Back to scandichotels.com" })}
|
||||
</span>
|
||||
<span className={styles.short}>
|
||||
{intl.formatMessage({ id: "Back" })}
|
||||
|
||||
@@ -18,8 +18,11 @@ import type { LangParams, PageArgs, SearchParams } from "@/types/params"
|
||||
import type { State } from "../sasUtils"
|
||||
|
||||
const searchParamsSchema = z.object({
|
||||
intent: z.enum(["link"]),
|
||||
intent: z.enum(["link", "unlink"]),
|
||||
})
|
||||
|
||||
type Intent = z.infer<typeof searchParamsSchema>["intent"]
|
||||
|
||||
export default async function SASxScandicLoginPage({
|
||||
searchParams,
|
||||
params,
|
||||
@@ -42,11 +45,19 @@ export default async function SASxScandicLoginPage({
|
||||
const clientId = env.SAS_AUTH_CLIENTID
|
||||
const sasLoginHostname = env.SAS_AUTH_ENDPOINT
|
||||
const audience = "eb-partner-api"
|
||||
// TODO check if this is correct scopes
|
||||
const scope = encodeURIComponent("openid profile email")
|
||||
|
||||
const loginLink = `${sasLoginHostname}/oauth/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${urlState}&audience=${audience}`
|
||||
|
||||
const intentDescriptions: Record<Intent, string> = {
|
||||
link: intl.formatMessage({
|
||||
id: "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
|
||||
}),
|
||||
unlink: intl.formatMessage({
|
||||
id: "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
}),
|
||||
}
|
||||
|
||||
return (
|
||||
<SASModal>
|
||||
<Redirect url={loginLink} timeout={3000} />
|
||||
@@ -60,11 +71,7 @@ export default async function SASxScandicLoginPage({
|
||||
<Title as="h2" level="h1" textTransform="regular">
|
||||
{intl.formatMessage({ id: "Redirecting you to SAS" })}
|
||||
</Title>
|
||||
<Body textAlign="center">
|
||||
{intl.formatMessage({
|
||||
id: "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
|
||||
})}
|
||||
</Body>
|
||||
<Body textAlign="center">{intentDescriptions[parsedParams.intent]}</Body>
|
||||
<Footnote textAlign="center">
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ import { cookies } from "next/headers"
|
||||
import { redirect } from "next/navigation"
|
||||
import { z } from "zod"
|
||||
|
||||
import { myPages } from "@/constants/routes/myPages"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import { getIntl } from "@/i18n"
|
||||
@@ -12,17 +13,21 @@ import OneTimePasswordForm, {
|
||||
type OnSubmitHandler,
|
||||
} from "./OneTimePasswordForm"
|
||||
|
||||
import type { ReactNode } from "react"
|
||||
|
||||
import type { LangParams, PageArgs, SearchParams } from "@/types/params"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
const otpError = z.enum(["invalidCode", "expiredCode"])
|
||||
const intent = z.enum(["link", "unlink"])
|
||||
const searchParamsSchema = z.object({
|
||||
intent: z.enum(["link"]),
|
||||
intent: intent,
|
||||
to: z.string(),
|
||||
error: otpError.optional(),
|
||||
})
|
||||
|
||||
export type OtpError = z.infer<typeof otpError>
|
||||
type Intent = z.infer<typeof intent>
|
||||
|
||||
export default async function SASxScandicOneTimePasswordPage({
|
||||
searchParams,
|
||||
@@ -88,26 +93,37 @@ export default async function SASxScandicOneTimePasswordPage({
|
||||
switch (intent) {
|
||||
case "link":
|
||||
return handleLinkAccount({ lang: params.lang })
|
||||
case "unlink":
|
||||
return handleUnlinkAccount({ lang: params.lang })
|
||||
}
|
||||
}
|
||||
|
||||
const maskedContactInfo = () => (
|
||||
<>
|
||||
<br />
|
||||
<strong>{to}</strong>
|
||||
<br />
|
||||
</>
|
||||
)
|
||||
const intentDescriptions: Record<Intent, ReactNode> = {
|
||||
link: intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
},
|
||||
{ maskedContactInfo }
|
||||
),
|
||||
unlink: intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
},
|
||||
{ maskedContactInfo }
|
||||
),
|
||||
}
|
||||
|
||||
return (
|
||||
<OneTimePasswordForm
|
||||
heading={intl.formatMessage({ id: "Verification code" })}
|
||||
ingress={intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
},
|
||||
{
|
||||
maskedContactInfo: () => (
|
||||
<>
|
||||
<br />
|
||||
<strong>{to}</strong>
|
||||
<br />
|
||||
</>
|
||||
),
|
||||
}
|
||||
)}
|
||||
ingress={intentDescriptions[intent]}
|
||||
footnote={intl.formatMessage({
|
||||
id: "This verifcation is needed for additional security.",
|
||||
})}
|
||||
@@ -168,3 +184,34 @@ async function handleLinkAccount({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUnlinkAccount({
|
||||
lang,
|
||||
}: {
|
||||
lang: Lang
|
||||
}): ReturnType<OnSubmitHandler> {
|
||||
const [res, error] = await safeTry(serverClient().partner.sas.unlinkAccount())
|
||||
if (!res || error) {
|
||||
console.error("[SAS] unlink account error", error)
|
||||
return {
|
||||
url: `/${lang}/sas-x-scandic/error`,
|
||||
}
|
||||
}
|
||||
|
||||
switch (res.linkingState) {
|
||||
case "unlinked":
|
||||
return {
|
||||
url: `/${lang}/sas-x-scandic/unlink/success`,
|
||||
type: "replace",
|
||||
}
|
||||
case "notLinked":
|
||||
return {
|
||||
url: myPages[lang],
|
||||
}
|
||||
case "error":
|
||||
return {
|
||||
url: `/${lang}/sas-x-scandic/error`,
|
||||
type: "replace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { z } from "zod"
|
||||
|
||||
export const SAS_TOKEN_STORAGE_KEY = "sas-x-scandic-token"
|
||||
|
||||
// TODO nonce??
|
||||
export const stateSchema = z.object({
|
||||
intent: z.literal("link"),
|
||||
intent: z.enum(["link", "unlink"]),
|
||||
})
|
||||
export type State = z.infer<typeof stateSchema>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from "react"
|
||||
|
||||
import { overview } from "@/constants/routes/myPages"
|
||||
|
||||
import CheckCircle from "@/components/Icons/CheckCircle"
|
||||
import { Redirect } from "@/components/Redirect"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import { SASModal } from "../../components/SASModal"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function SASxScandicUnlinkSuccessPage({
|
||||
params,
|
||||
}: PageArgs<LangParams>) {
|
||||
const intl = await getIntl()
|
||||
|
||||
return (
|
||||
<SASModal>
|
||||
<Redirect url={overview[params.lang]} timeout={3000} />
|
||||
<CheckCircle height={64} width={64} color="uiSemanticSuccess" />
|
||||
<Title as="h2" level="h1" textAlign="center">
|
||||
{intl.formatMessage({ id: "Your accounts are now unlinked" })}
|
||||
</Title>
|
||||
<div>
|
||||
<Body textAlign="center">
|
||||
{intl.formatMessage({
|
||||
id: "Redirecting you to My Pages.",
|
||||
})}
|
||||
</Body>
|
||||
</div>
|
||||
</SASModal>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +1,34 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import Dialog from "@/components/Dialog"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
import { Loading } from "@/components/Loading"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import type { LangParams } from "@/types/params"
|
||||
|
||||
export function UnlinkSAS() {
|
||||
const intl = useIntl()
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate, isPending } = trpc.partner.sas.unlinkAccount.useMutation({
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage({ id: "Account unlinked, reloading" }))
|
||||
// TODO: reload page
|
||||
router.push("/en/scandic-friends/my-pages")
|
||||
},
|
||||
onError() {
|
||||
toast.error(intl.formatMessage({ id: "Failed to unlink account" }))
|
||||
},
|
||||
})
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
event.preventDefault()
|
||||
mutate()
|
||||
}
|
||||
|
||||
if (isPending) {
|
||||
return <Loading color="burgundy" />
|
||||
}
|
||||
const params = useParams<LangParams>()
|
||||
|
||||
return (
|
||||
<Link
|
||||
href="#"
|
||||
onClick={handleClick}
|
||||
color="burgundy"
|
||||
variant="default"
|
||||
weight="bold"
|
||||
>
|
||||
{intl.formatMessage({ id: "Unlink accounts" })}
|
||||
</Link>
|
||||
<Dialog
|
||||
titleText={intl.formatMessage({
|
||||
id: "Are you sure you want to unlink your account?",
|
||||
})}
|
||||
// TODO update copy
|
||||
bodyText={intl.formatMessage({
|
||||
id: "We could not connect your accounts to give you access. Please contact us and we’ll help you resolve this issue.",
|
||||
})}
|
||||
cancelButtonText={intl.formatMessage({ id: "Go back" })}
|
||||
proceedText={intl.formatMessage({ id: "Yes, unlink my accounts" })}
|
||||
proceedHref={`/${params.lang}/sas-x-scandic/login?intent=unlink`}
|
||||
trigger={
|
||||
<Button intent="text" theme="base">
|
||||
{intl.formatMessage({ id: "Unlink accounts" })}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ export default async function SASLinkedAccount({
|
||||
</section>
|
||||
</SectionContainer>
|
||||
<div className={styles.mutationSection}>
|
||||
<LevelUpgradeButton />
|
||||
<UnlinkSAS />
|
||||
<LevelUpgradeButton />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
& .textContainer {
|
||||
display: flex;
|
||||
@@ -10,6 +11,10 @@
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
.mutationSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: column-reverse;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
|
||||
|
||||
@@ -24,13 +24,11 @@ export default function Dialog({
|
||||
proceedOnClick = () => {},
|
||||
proceedText,
|
||||
titleText,
|
||||
triggerButtonText,
|
||||
trigger,
|
||||
}: DialogProps) {
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button intent="secondary" size="small" theme="base">
|
||||
{triggerButtonText}
|
||||
</Button>
|
||||
{trigger}
|
||||
<ModalOverlay className={styles.overlay} isDismissable>
|
||||
<Modal>
|
||||
<AriaDialog role="alertdialog">
|
||||
|
||||
@@ -133,7 +133,11 @@ export default function Form({ user }: EditFormProps) {
|
||||
proceedHref={profile[lang]}
|
||||
proceedText={intl.formatMessage({ id: "Yes, discard changes" })}
|
||||
titleText={intl.formatMessage({ id: "Discard unsaved changes?" })}
|
||||
triggerButtonText={intl.formatMessage({ id: "Discard changes" })}
|
||||
trigger={
|
||||
<Button intent="secondary" size="small" theme="base">
|
||||
{intl.formatMessage({ id: "Discard changes" })}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
disabled={!isValid || methods.formState.isSubmitting}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import Dialog from "@/components/Dialog"
|
||||
import { DeleteIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
|
||||
import type { DeleteCreditCardConfirmationProps } from "@/types/components/myPages/myProfile/creditCards"
|
||||
@@ -52,7 +53,11 @@ export default function DeleteCreditCardConfirmation({
|
||||
proceedOnClick={handleProceedDeleteCard}
|
||||
proceedText={intl.formatMessage({ id: "Yes, remove my card" })}
|
||||
titleText={intl.formatMessage({ id: "Remove card from member profile" })}
|
||||
triggerButtonText={<DeleteIcon color="burgundy" />}
|
||||
trigger={
|
||||
<Button intent="secondary" size="small" theme="base">
|
||||
<DeleteIcon color="burgundy" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"Locations": "Placeringer",
|
||||
"Log in": "Log på",
|
||||
"Log in here": "Log ind her",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Log på/Tilmeld dig",
|
||||
"Log out": "Log ud",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -441,6 +442,7 @@
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Vær opmærksom på, at dette er påkrævet, og at dit kort kun vil blive opkrævet i tilfælde af en no-show.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Point",
|
||||
@@ -674,6 +676,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din booking er bekræftet, men vi kunne ikke verificere dit medlemskab. Hvis du har booket med et medlemstilbud, skal du enten vise dit eksisterende medlemskab ved check-in, blive medlem eller betale prisdifferencen ved check-in. Tilmelding er foretrukket online før opholdet.",
|
||||
"Your card was successfully removed!": "Dit kort blev fjernet!",
|
||||
"Your card was successfully saved!": "Dit kort blev gemt!",
|
||||
|
||||
@@ -320,6 +320,7 @@
|
||||
"Locations": "Orte",
|
||||
"Log in": "Anmeldung",
|
||||
"Log in here": "Hier einloggen",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Log in/Anmelden",
|
||||
"Log out": "Ausloggen",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -442,6 +443,7 @@
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Bitte beachten Sie, dass dies erforderlich ist und dass Ihr Kreditkartenkonto nur in einem No-Show-Fall belastet wird.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Punkte",
|
||||
@@ -674,6 +676,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Ihre Buchung ist bestätigt, aber wir konnten Ihr Mitglied nicht verifizieren. Wenn Sie mit einem Mitgliederrabatt gebucht haben, müssen Sie entweder Ihr vorhandenes Mitgliedschaftsnummer bei der Anreise präsentieren, ein Mitglied werden oder die Preisdifferenz bei der Anreise bezahlen. Die Anmeldung ist vorzugsweise online vor der Aufenthaltsdauer erfolgreich.",
|
||||
"Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!",
|
||||
"Your card was successfully saved!": "Ihre Karte wurde erfolgreich gespeichert!",
|
||||
|
||||
@@ -323,6 +323,7 @@
|
||||
"Locations": "Locations",
|
||||
"Log in": "Log in",
|
||||
"Log in here": "Log in here",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Log in/Join",
|
||||
"Log out": "Log out",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -446,6 +447,7 @@
|
||||
"Phone number": "Phone number",
|
||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Please note that this is mandatory, and that your card will only be charged in the event of a no-show.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Points",
|
||||
@@ -680,6 +682,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.",
|
||||
"Your card was successfully removed!": "Your card was successfully removed!",
|
||||
"Your card was successfully saved!": "Your card was successfully saved!",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"Locations": "Sijainnit",
|
||||
"Log in": "Kirjaudu sisään",
|
||||
"Log in here": "Kirjaudu sisään",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Kirjaudu sisään/Liittyä",
|
||||
"Log out": "Kirjaudu ulos",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -441,6 +442,7 @@
|
||||
"Phone number": "Puhelinnumero",
|
||||
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Huomaa, että tämä on pakollinen, ja että maksukorttiisi kirjataan vain, jos varausmyyntiä ei tapahtu.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Pisteet",
|
||||
@@ -674,6 +676,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Varauksesi on vahvistettu, mutta jäsenyytesi ei voitu vahvistaa. Jos olet bookeutunut jäsenyysalennoilla, sinun on joko esitettävä olemassa olevan jäsenyysnumero tarkistukseen, tulla jäseneksi tai maksamaan hinnan eron hotellissa. Jäsenyyden tilittäminen on suositeltavampaa tehdä verkkoon ennen majoittumista.",
|
||||
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
|
||||
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
|
||||
|
||||
@@ -318,6 +318,7 @@
|
||||
"Locations": "Steder",
|
||||
"Log in": "Logg Inn",
|
||||
"Log in here": "Logg inn her",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Logg på/Bli med",
|
||||
"Log out": "Logg ut",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -440,6 +441,7 @@
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Vær oppmerksom på at dette er påkrevd, og at ditt kredittkort kun vil bli belastet i tilfelle av en no-show.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Poeng",
|
||||
@@ -672,6 +674,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bestilling er bekreftet, men vi kunne ikke verifisere medlemskapet ditt. Hvis du har booke ut med et medlemsrabatt, må du enten presentere eksisterende medlemsnummer ved check-in, bli medlem eller betale prisdifferansen ved hotellet. Registrering er foretrukket gjort online før oppholdet.",
|
||||
"Your card was successfully removed!": "Kortet ditt ble fjernet!",
|
||||
"Your card was successfully saved!": "Kortet ditt ble lagret!",
|
||||
|
||||
@@ -318,6 +318,7 @@
|
||||
"Locations": "Platser",
|
||||
"Log in": "Logga in",
|
||||
"Log in here": "Logga in här",
|
||||
"Log in to your SAS Eurobonus account to confirm account unlinking.": "Log in to your SAS Eurobonus account to confirm account unlinking.",
|
||||
"Log in/Join": "Logga in/Gå med",
|
||||
"Log out": "Logga ut",
|
||||
"Long {long} ∙ Lat {lat}": "Long {long} ∙ Lat {lat}",
|
||||
@@ -440,6 +441,7 @@
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
|
||||
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
|
||||
"Please note that this is mandatory, and that your card will only be charged in the event of a no-show.": "Vänligen notera att detta är obligatoriskt, och att ditt kreditkort endast debiteras i händelse av en no-show.",
|
||||
"Please try and change your search for this destination or see alternative hotels.": "Please try and change your search for this destination or see alternative hotels.",
|
||||
"Points": "Poäng",
|
||||
@@ -672,6 +674,7 @@
|
||||
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
||||
"Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level",
|
||||
"Your accounts are connected": "Your accounts are connected",
|
||||
"Your accounts are now unlinked": "Your accounts are now unlinked",
|
||||
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bokning är bekräftad, men vi kunde inte verifiera ditt medlemskap. Om du har bokat med ett medlemsrabatt måste du antingen presentera ditt befintliga medlemsnummer vid check-in, bli medlem eller betala prisdifferensen vid hotell. Registrering är föredragen gjord online före vistelsen.",
|
||||
"Your card was successfully removed!": "Ditt kort har tagits bort!",
|
||||
"Your card was successfully saved!": "Ditt kort har sparats!",
|
||||
|
||||
@@ -163,6 +163,8 @@ export namespace endpoints {
|
||||
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`
|
||||
|
||||
// TODO: Remove once new endpoints are out in production.
|
||||
export const reward = `${base.path.profile}/${version}/${base.enitity.Profile}/reward`
|
||||
export const tierRewards = `${base.path.profile}/${version}/${base.enitity.Profile}/tierRewards`
|
||||
|
||||
@@ -1,27 +1,50 @@
|
||||
import { TRPCError } from "@trpc/server"
|
||||
import { z } from "zod"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
import { timeout } from "@/utils/timeout"
|
||||
import { getSasToken } from "./getSasToken"
|
||||
|
||||
const outputSchema = z.object({
|
||||
// unlinked: z.boolean(),
|
||||
linkingState: z.enum(["unlinked", "notLinked", "error"]),
|
||||
})
|
||||
|
||||
export const unlinkAccount = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.log("[SAS] unlink account")
|
||||
await timeout(1000)
|
||||
//TODO: Call actual API here
|
||||
.mutation(async function ({ ctx }) {
|
||||
const sasAuthToken = getSasToken()
|
||||
|
||||
throw new TRPCError({
|
||||
message: "Unable to unlink account",
|
||||
code: "BAD_REQUEST",
|
||||
const apiResponse = await api.post(api.endpoints.v1.Profile.unlink, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||
},
|
||||
body: {
|
||||
partner: "sas_eb",
|
||||
partnerSpecific: {
|
||||
eurobonusAccessToken: sasAuthToken,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
unlinked: true,
|
||||
if (apiResponse.status === 204) {
|
||||
console.log("[SAS] unlink account success")
|
||||
return { linkingState: "unlinked" }
|
||||
}
|
||||
|
||||
if (apiResponse.status === 400) {
|
||||
const result = await apiResponse.json()
|
||||
|
||||
console.log("[SAS] unlink account error with response", result)
|
||||
return { linkingState: "error" }
|
||||
}
|
||||
|
||||
if (apiResponse.status === 404) {
|
||||
console.log("[SAS] tried unlinking an account that was not linked")
|
||||
return { linkingState: "notLinked" }
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[SAS] unlink account error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
|
||||
)
|
||||
return { linkingState: "error" }
|
||||
})
|
||||
|
||||
@@ -6,5 +6,5 @@ export interface DialogProps {
|
||||
proceedOnClick?: (close: () => void) => void
|
||||
proceedText: string
|
||||
titleText: string
|
||||
triggerButtonText: React.ReactNode
|
||||
trigger: React.ReactNode
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user