Merged in feature/sas-login (pull request #1256)

First steps towards the SAS partnership

* otp flow now pretends to do the linking

* Update LinkAccountForm header

* Update redirect times

* Clean up comments

* Set maxAge on sas cookies

* make all SAS routes protected

* Merge remote-tracking branch 'refs/remotes/origin/feature/sas-login' into feature/sas-login

* Require auth for sas link flow

* Fix resend otp

* Add error support to OneTimePasswordForm

* Add Sentry to SAS error boundary

* Move SAS_REQUEST_OTP_STATE_STORAGE_COOKIE_NAME

* Add missing translations

* Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/sas-login

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* Add TooManyCodesError component

* Refactor GenericError to support new errors

* Add FailedAttemptsError

* remove removed component <VWOScript/>

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* remove local cookie-bot reference

* Fix sas campaign logo scaling

* feature toggle the SAS stuff

* Merge branch 'feature/sas-login' of bitbucket.org:scandic-swap/web into feature/sas-login

* fix: use env vars for SAS endpoints


Approved-by: Linus Flood
This commit is contained in:
Joakim Jäderberg
2025-02-05 14:43:14 +00:00
parent e3b1bfc414
commit 46ebbbba8f
62 changed files with 2606 additions and 89 deletions

View File

@@ -0,0 +1,35 @@
"use client"
import Link from "next/link"
import { useIntl } from "react-intl"
import ErrorCircleFilledIcon from "@/components/Icons/ErrorCircleFilled"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { SASModal, SASModalContactBlock, SASModalDivider } from "./SASModal"
export function AlreadyLinkedError() {
const intl = useIntl()
return (
<SASModal>
<ErrorCircleFilledIcon height={64} width={64} color="red" />
<Title as="h2" level="h1" textAlign="center" textTransform="regular">
{intl.formatMessage({ id: "Accounts are already linked" })}
</Title>
<Body textAlign="center">
{/* TODO copy */}
{intl.formatMessage({
id: "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
})}
</Body>
{/* TODO link to SASxScandic page on My Pages */}
<Button theme="base" asChild>
<Link href="#">{intl.formatMessage({ id: "View your account" })}</Link>
</Button>
<SASModalDivider />
<SASModalContactBlock />
</SASModal>
)
}

View File

@@ -0,0 +1,34 @@
"use client"
import { useIntl } from "react-intl"
import ErrorCircleFilledIcon from "@/components/Icons/ErrorCircleFilled"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { SASModal, SASModalContactBlock, SASModalDivider } from "./SASModal"
export function DateOfBirthError() {
const intl = useIntl()
return (
<SASModal>
<ErrorCircleFilledIcon height={64} width={64} color="red" />
<Title as="h2" level="h1" textAlign="center" textTransform="regular">
{intl.formatMessage({ id: "Date of birth not matching" })}
</Title>
<Body textAlign="center">
{/* TODO copy */}
{intl.formatMessage({
id: "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
})}
</Body>
{/* TODO link to where? */}
<Button theme="base">
{intl.formatMessage({ id: "View your account" })}
</Button>
<SASModalDivider />
<SASModalContactBlock />
</SASModal>
)
}

View File

@@ -0,0 +1,28 @@
"use client"
import { useIntl } from "react-intl"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import { GenericError } from "./GenericError"
export function FailedAttemptsError() {
const intl = useIntl()
return (
<GenericError
title={intl.formatMessage({ id: "Too many failed attempts" })}
variant="info"
>
<Body textAlign="center">
{intl.formatMessage({
id: "Please wait 1 hour before trying again.",
})}
</Body>
<Button theme="base" disabled>
{intl.formatMessage({ id: "Send new code" })}
</Button>
</GenericError>
)
}

View File

@@ -0,0 +1,39 @@
"use client"
import Image from "next/image"
import ErrorCircleFilledIcon from "@/components/Icons/ErrorCircleFilled"
import Title from "@/components/TempDesignSystem/Text/Title"
import { SASModal } from "./SASModal"
import type { ReactNode } from "react"
export function GenericError({
title,
variant = "error",
children,
}: {
title: ReactNode
variant?: "error" | "info"
children: ReactNode
}) {
return (
<SASModal>
{variant === "error" ? (
<ErrorCircleFilledIcon height={64} width={64} color="red" />
) : (
<Image
src="/_static/img/scandic-loyalty-time.svg"
alt=""
width={140}
height={110}
style={{ marginTop: 16 }}
/>
)}
<Title as="h3" level="h1" textAlign="center" textTransform="regular">
{title}
</Title>
{children}
</SASModal>
)
}

View File

@@ -0,0 +1,45 @@
.container {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--Spacing-x3);
background-color: white;
width: 100%;
padding: var(--Spacing-x3) var(--Spacing-x3) var(--Spacing-x4);
text-align: center;
border-radius: var(--Corner-radius-Medium) var(--Corner-radius-Medium) 0 0;
margin-top: auto;
@media screen and (min-width: 768px) {
& {
border-radius: var(--Corner-radius-Medium);
margin-top: initial;
width: 560px;
}
}
}
.divider {
width: 100%;
position: relative;
& > span {
position: relative;
padding: 0 var(--Spacing-x2);
background-color: white;
}
&::before {
position: absolute;
bottom: calc(50% - 1px);
content: "";
display: block;
height: 1px;
width: 100%;
background-color: var(--Base-Border-Subtle);
}
}
.contactBlockTitle {
margin-bottom: var(--Spacing-x1);
}

View File

@@ -0,0 +1,53 @@
"use client"
import { useIntl } from "react-intl"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import styles from "./SASModal.module.css"
export function SASModal({ children }: { children: React.ReactNode }) {
return <section className={styles.container}>{children}</section>
}
export function SASModalDivider() {
const intl = useIntl()
return (
<div className={styles.divider}>
<Body asChild color="uiTextPlaceholder">
<span>{intl.formatMessage({ id: "or" })}</span>
</Body>
</div>
)
}
export function SASModalContactBlock() {
const intl = useIntl()
const phone = intl.formatMessage({ id: "+46 8 517 517 00" })
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<Title
level="h4"
as="h3"
textTransform="regular"
className={styles.contactBlockTitle}
>
{intl.formatMessage({ id: "Contact our memberservice" })}
</Title>
<Link
href={`tel:${phone.replaceAll(" ", "")}`}
textDecoration="underline"
>
{phone}
</Link>
<Link href="mailto:member@scandichotels.com" textDecoration="underline">
member@scandichotels.com
</Link>
</div>
)
}

View File

@@ -0,0 +1,28 @@
"use client"
import { useIntl } from "react-intl"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import { GenericError } from "./GenericError"
export function TooManyCodesError() {
const intl = useIntl()
return (
<GenericError
title={intl.formatMessage({ id: "Youve requested too many codes" })}
variant="info"
>
<Body textAlign="center">
{intl.formatMessage({
id: "Please wait 1 hour before trying again.",
})}
</Body>
<Button theme="base" disabled>
{intl.formatMessage({ id: "Send new code" })}
</Button>
</GenericError>
)
}