Merged in feature/sas-mypages (pull request #1302)
Feature/sas mypages * feat: Add SAS partner page under my pages * fix: feature toggle SAS Partner page in my pages * add translations for SAS account page * use 'flex-start' instead of 'start' * fix: flatten css * fix: don't use <SectionContainer /> on linkedAccounts page
This commit is contained in:
@@ -3,6 +3,8 @@ import { Suspense } from "react"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import Blocks from "@/components/Blocks"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import Preamble from "@/components/TempDesignSystem/Text/Preamble"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
@@ -27,13 +29,20 @@ export default async function MyPages({
|
||||
}
|
||||
|
||||
const { tracking, accountPage } = accountPageRes
|
||||
const { heading, preamble, content } = accountPage
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className={styles.blocks}>
|
||||
<Title>{accountPage.heading}</Title>
|
||||
{accountPage.content?.length ? (
|
||||
<Blocks blocks={accountPage.content} />
|
||||
<SectionHeader
|
||||
title={heading}
|
||||
preamble={preamble}
|
||||
headingAs="h1"
|
||||
headingLevel="h1"
|
||||
/>
|
||||
|
||||
{content?.length ? (
|
||||
<Blocks blocks={content} />
|
||||
) : (
|
||||
<p>{intl.formatMessage({ id: "No content published" })}</p>
|
||||
)}
|
||||
|
||||
11
app/[lang]/(partner)/(sas)/(protected)/loading.tsx
Normal file
11
app/[lang]/(partner)/(sas)/(protected)/loading.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
import { SASModal } from "./sas-x-scandic/components/SASModal"
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<SASModal>
|
||||
<LoadingSpinner />
|
||||
</SASModal>
|
||||
)
|
||||
}
|
||||
@@ -35,7 +35,8 @@ export default function CardsGrid({
|
||||
<SectionHeader
|
||||
title={cards_grid.title}
|
||||
preamble={cards_grid.preamble}
|
||||
topTitle={firstItem}
|
||||
headingAs={firstItem ? "h3" : "h4"}
|
||||
headingLevel={firstItem ? "h1" : "h2"}
|
||||
/>
|
||||
<Grids.Stackable columns={columns}>
|
||||
{cards_grid.cards.map((card, index) => {
|
||||
|
||||
@@ -26,7 +26,13 @@ export default async function Overview({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<SectionHeader
|
||||
link={link}
|
||||
preamble={subtitle}
|
||||
title={title}
|
||||
headingAs={"h3"}
|
||||
headingLevel={"h1"}
|
||||
/>
|
||||
<Hero color="red">
|
||||
<Friend membership={user.membership} name={user.name}>
|
||||
<MembershipNumber color="burgundy" membership={user.membership} />
|
||||
|
||||
@@ -26,7 +26,13 @@ export default async function PointsOverview({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<SectionHeader
|
||||
link={link}
|
||||
preamble={subtitle}
|
||||
title={title}
|
||||
headingAs={"h3"}
|
||||
headingLevel={"h1"}
|
||||
/>
|
||||
<Hero color="burgundy">
|
||||
<Friend membership={user.membership} name={user.name}>
|
||||
<MembershipNumber color="red" membership={user.membership} />
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import Image from "next/image"
|
||||
|
||||
import RocketLaunch from "@/components/Icons/RocketLaunch"
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./tierLevelCard.module.css"
|
||||
|
||||
import type { ComponentProps, ReactNode } from "react"
|
||||
|
||||
type BoostState = "boostedInOtherSystem" | "boostedInThisSystem" | "notBoosted"
|
||||
|
||||
type BaseProps = {
|
||||
points: number
|
||||
tier: string
|
||||
boostState: BoostState
|
||||
}
|
||||
|
||||
type BoostedInOther = BaseProps & {
|
||||
boostState: "boostedInOtherSystem"
|
||||
boostedTier: string
|
||||
boostExpiration: Date
|
||||
}
|
||||
|
||||
type BoostedInThis = BaseProps & {
|
||||
boostState: "boostedInThisSystem"
|
||||
}
|
||||
|
||||
type NotBoosted = BaseProps & {
|
||||
boostState: "notBoosted"
|
||||
}
|
||||
|
||||
const variants = cva(styles.tierlevelcard, {
|
||||
variants: {
|
||||
bonusSystem: {
|
||||
scandic: styles.scandic,
|
||||
sas: styles.sas,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type Props = VariantProps<typeof variants> &
|
||||
(BoostedInOther | BoostedInThis | NotBoosted)
|
||||
export async function TierLevelCard({
|
||||
points,
|
||||
tier,
|
||||
bonusSystem,
|
||||
...boosted
|
||||
}: Props) {
|
||||
const intl = await getIntl()
|
||||
|
||||
const className = variants({ bonusSystem })
|
||||
|
||||
return (
|
||||
<article className={className}>
|
||||
{boosted.boostState === "boostedInOtherSystem" && (
|
||||
<section className={styles.boostedInfo}>
|
||||
<div className={styles.boostedTier}>
|
||||
<Caption
|
||||
uppercase
|
||||
type="bold"
|
||||
color={bonusSystemSpecifics[bonusSystem!].color}
|
||||
>
|
||||
{boosted.boostedTier}
|
||||
</Caption>
|
||||
<Caption
|
||||
type="bold"
|
||||
color={bonusSystemSpecifics[bonusSystem!].color}
|
||||
>
|
||||
{intl.formatMessage({ id: "Level upgrade" })}
|
||||
</Caption>
|
||||
</div>
|
||||
<Caption color={bonusSystemSpecifics[bonusSystem!].color}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Upgrade expires {upgradeExpires, date, short}",
|
||||
},
|
||||
{ upgradeExpires: boosted.boostExpiration }
|
||||
)}
|
||||
</Caption>
|
||||
</section>
|
||||
)}
|
||||
<section className={styles.baseInfo}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.tierInfo}>
|
||||
<span>
|
||||
<Caption
|
||||
uppercase
|
||||
type="bold"
|
||||
color={bonusSystemSpecifics[bonusSystem!].color}
|
||||
>
|
||||
{tier}
|
||||
</Caption>
|
||||
</span>
|
||||
<div className={styles.logoContainer}>
|
||||
{bonusSystemSpecifics[bonusSystem!].logo}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{boosted.boostState === "boostedInThisSystem" && (
|
||||
<Footnote className={styles.footnote}>
|
||||
<RocketLaunch
|
||||
color={bonusSystemSpecifics[bonusSystem!].rocketLaunchColor}
|
||||
/>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: bonusSystemSpecifics[bonusSystem!].boostTextId,
|
||||
})}
|
||||
</span>
|
||||
</Footnote>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Subtitle>
|
||||
{intl.formatMessage(
|
||||
{ id: "{points, number} Bonus points" },
|
||||
{ points }
|
||||
)}
|
||||
</Subtitle>
|
||||
</section>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
export function TierLevelCardSkeleton({
|
||||
bonusSystem,
|
||||
}: {
|
||||
bonusSystem?: NonNullable<Props["bonusSystem"]>
|
||||
}) {
|
||||
const className = variants({ bonusSystem })
|
||||
|
||||
return (
|
||||
<article className={className}>
|
||||
<section className={styles.baseInfo}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.tierInfo}>
|
||||
<span>
|
||||
<SkeletonShimmer width={"50px"} height={"16px"} />
|
||||
</span>
|
||||
|
||||
{bonusSystem && (
|
||||
<div className={styles.eurobonusLogo}>
|
||||
{bonusSystemSpecifics[bonusSystem!].logo}
|
||||
</div>
|
||||
)}
|
||||
{!bonusSystem && <SkeletonShimmer width={"74px"} height={"16px"} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Subtitle>
|
||||
<SkeletonShimmer width={"240px"} height={"26px"} />
|
||||
</Subtitle>
|
||||
</section>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
type BonusSystemSpecifics = {
|
||||
boostTextId: string
|
||||
logo: ReactNode
|
||||
rocketLaunchColor: ComponentProps<typeof RocketLaunch>["color"]
|
||||
color: ComponentProps<typeof Caption>["color"]
|
||||
}
|
||||
|
||||
type BonusSystemMapping = {
|
||||
[bonusSystem in NonNullable<
|
||||
VariantProps<typeof variants>["bonusSystem"]
|
||||
>]: BonusSystemSpecifics
|
||||
}
|
||||
|
||||
const bonusSystemSpecifics: BonusSystemMapping = {
|
||||
scandic: {
|
||||
boostTextId: "Your Friends level has upgraded your Eurobonus level",
|
||||
rocketLaunchColor: "red",
|
||||
color: "burgundy",
|
||||
logo: (
|
||||
<Image
|
||||
alt="Scandic logo"
|
||||
height={16}
|
||||
src="/_static/img/scandic-logotype.svg"
|
||||
width={74}
|
||||
/>
|
||||
),
|
||||
},
|
||||
sas: {
|
||||
boostTextId: "Your Eurobonus level has upgraded your friends level",
|
||||
rocketLaunchColor: "blue",
|
||||
color: "uiTextActive",
|
||||
logo: (
|
||||
<>
|
||||
<Image
|
||||
alt="SAS logo"
|
||||
height={16}
|
||||
src="/_static/img/sas/sas-logotype.svg"
|
||||
width={44}
|
||||
/>
|
||||
<Caption uppercase type="bold">
|
||||
Eurobonus
|
||||
</Caption>
|
||||
</>
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
.tierlevelcard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
background: white;
|
||||
box-shadow: 0px 0px 8px 3px #0000001a;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
width: 100%;
|
||||
min-height: 176px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
max-width: 335px;
|
||||
}
|
||||
|
||||
&.scandic {
|
||||
background: white;
|
||||
color: var(--Main-Brand-Burgundy);
|
||||
|
||||
.boostedInfo {
|
||||
background: linear-gradient(86.64deg, #faf6f2 0%, #f4d5c8 100.91%);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
}
|
||||
|
||||
&.sas {
|
||||
background: #f0f4ff;
|
||||
color: var(--Main-Brand-DarkBlue);
|
||||
|
||||
.boostedInfo {
|
||||
background: linear-gradient(90deg, #f0f4ff 0%, #bdcdff 100%);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.boostedInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-half);
|
||||
box-shadow: 0px 0px 4px 2px #0000001a;
|
||||
|
||||
flex: 0;
|
||||
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.boostedTier {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.baseInfo {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
|
||||
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
|
||||
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.tierInfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-transform: uppercase;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.logoContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.footnote {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import Refresh from "@/components/Icons/Refresh"
|
||||
import { Loading } from "@/components/Loading"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
|
||||
import styles from "./levelupgradebutton.module.css"
|
||||
|
||||
export function LevelUpgradeButton() {
|
||||
const intl = useIntl()
|
||||
|
||||
const { mutate, isPending } =
|
||||
trpc.partner.sas.performLevelUpgrade.useMutation({
|
||||
onSuccess() {
|
||||
toast.success(intl.formatMessage({ id: "Level upgraded" }))
|
||||
},
|
||||
onError() {
|
||||
toast.error(intl.formatMessage({ id: "Failed to upgrade level" }))
|
||||
},
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
mutate()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
intent="primary"
|
||||
theme="primaryLight"
|
||||
onClick={handleClick}
|
||||
className={styles.button}
|
||||
>
|
||||
<div
|
||||
className={styles.textContainer}
|
||||
style={{ visibility: isPending ? "hidden" : "visible" }}
|
||||
>
|
||||
<Refresh color="currentColor" />
|
||||
{intl.formatMessage({ id: "Check for level upgrade" })}
|
||||
</div>
|
||||
{isPending && <Loading color="white" className={styles.loading} />}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import { Loading } from "@/components/Loading"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
|
||||
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" />
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href="#"
|
||||
onClick={handleClick}
|
||||
color="burgundy"
|
||||
variant="default"
|
||||
weight="bold"
|
||||
>
|
||||
{intl.formatMessage({ id: "Unlink accounts" })}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Suspense } from "react"
|
||||
import { setTimeout } from "timers/promises"
|
||||
|
||||
import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels"
|
||||
import { env } from "@/env/server"
|
||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
|
||||
import { TierLevelCard, TierLevelCardSkeleton } from "./Card/TierLevelCard"
|
||||
import { LevelUpgradeButton } from "./LevelUpgradeButton"
|
||||
import { UnlinkSAS } from "./UnlinkSAS"
|
||||
|
||||
import styles from "./linkedAccounts.module.css"
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
link?: { href: string; text: string }
|
||||
subtitle?: string
|
||||
}
|
||||
|
||||
export default async function SASLinkedAccount({
|
||||
title,
|
||||
subtitle,
|
||||
link,
|
||||
}: Props) {
|
||||
if (!env.SAS_ENABLED) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} />
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
<section className={styles.cardsContainer}>
|
||||
<Suspense fallback={<TierLevelCardsSkeleton />}>
|
||||
<TierLevelCards />
|
||||
</Suspense>
|
||||
</section>
|
||||
</SectionContainer>
|
||||
<div className={styles.mutationSection}>
|
||||
<LevelUpgradeButton />
|
||||
<UnlinkSAS />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function TierLevelCardsSkeleton() {
|
||||
return (
|
||||
<>
|
||||
<TierLevelCardSkeleton bonusSystem={"scandic"} />
|
||||
<TierLevelCardSkeleton bonusSystem={"sas"} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
async function TierLevelCards() {
|
||||
console.log("[SAS] Fetching tier level cards")
|
||||
await setTimeout(2_000)
|
||||
console.log("[SAS] AFTER Fetching tier level cards")
|
||||
|
||||
const user = await getProfile()
|
||||
if (!user || "error" in user) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sasPoints = 250_000
|
||||
const sfPoints = user.membership?.currentPoints || 0
|
||||
const sfLevelName =
|
||||
TIER_TO_FRIEND_MAP[user.membership?.membershipLevel ?? "L1"]
|
||||
|
||||
return (
|
||||
<>
|
||||
<TierLevelCard
|
||||
points={sfPoints}
|
||||
tier={sfLevelName}
|
||||
boostState="boostedInThisSystem"
|
||||
bonusSystem={"scandic"}
|
||||
/>
|
||||
<TierLevelCard
|
||||
points={sasPoints}
|
||||
tier="Silver"
|
||||
boostState="boostedInOtherSystem"
|
||||
bonusSystem={"sas"}
|
||||
boostExpiration={new Date("2022-12-31")}
|
||||
boostedTier="Gold"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
& .textContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
.divider {
|
||||
margin-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.divider {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cardsContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
justify-content: flex-start;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.mutationSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ export default function SectionWrapper({
|
||||
link={dynamic_content.link}
|
||||
preamble={dynamic_content.subtitle}
|
||||
title={dynamic_content.title}
|
||||
topTitle={firstItem}
|
||||
headingAs={firstItem ? "h3" : "h4"}
|
||||
headingLevel={firstItem ? "h1" : "h2"}
|
||||
/>
|
||||
) : null}
|
||||
{children}
|
||||
|
||||
@@ -11,6 +11,7 @@ import ExpiringPoints from "@/components/Blocks/DynamicContent/Points/ExpiringPo
|
||||
import PointsOverview from "@/components/Blocks/DynamicContent/Points/Overview"
|
||||
import CurrentRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/CurrentRewards"
|
||||
import NextLevelRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/NextLevel"
|
||||
import SASLinkedAccount from "@/components/Blocks/DynamicContent/SAS/LinkedAccounts"
|
||||
import SASTierComparisonBlock from "@/components/Blocks/DynamicContent/SASTierComparison"
|
||||
import SignupFormWrapper from "@/components/Blocks/DynamicContent/SignupFormWrapper"
|
||||
import SignUpVerification from "@/components/Blocks/DynamicContent/SignUpVerification"
|
||||
@@ -75,6 +76,8 @@ function DynamicContentBlocks(props: DynamicContentProps) {
|
||||
return <SoonestStays {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.upcoming_stays:
|
||||
return <UpcomingStays {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.sas_linked_account:
|
||||
return <SASLinkedAccount {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.sas_tier_comparison:
|
||||
return (
|
||||
<SASTierComparisonBlock
|
||||
|
||||
@@ -36,7 +36,12 @@ export default function ShortcutsList({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader preamble={subtitle} title={title} topTitle={firstItem} />
|
||||
<SectionHeader
|
||||
preamble={subtitle}
|
||||
title={title}
|
||||
headingAs={firstItem ? "h3" : "h4"}
|
||||
headingLevel={firstItem ? "h1" : "h2"}
|
||||
/>
|
||||
<section className={styles.section}>
|
||||
{columns.map(({ id, column }) => (
|
||||
<ShortcutsListItems
|
||||
|
||||
27
components/Icons/Refresh.tsx
Normal file
27
components/Icons/Refresh.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function RocketLaunch({
|
||||
className,
|
||||
color,
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={classNames}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M6.0249 12.0254C6.0249 12.2921 6.04157 12.5546 6.0749 12.8129C6.10824 13.0712 6.16657 13.3296 6.2499 13.5879C6.3249 13.8462 6.31657 14.0962 6.2249 14.3379C6.13324 14.5796 5.96657 14.7546 5.7249 14.8629C5.4749 14.9796 5.23115 14.9858 4.99365 14.8816C4.75615 14.7775 4.59574 14.5962 4.5124 14.3379C4.3874 13.9629 4.29574 13.5816 4.2374 13.1941C4.17907 12.8066 4.1499 12.4171 4.1499 12.0254C4.1499 9.83372 4.91032 7.97331 6.43115 6.44414C7.95199 4.91497 9.81657 4.15039 12.0249 4.15039H12.2749L11.3124 3.18789C11.1291 3.00456 11.0374 2.78372 11.0374 2.52539C11.0374 2.26706 11.1291 2.04622 11.3124 1.86289C11.4957 1.67956 11.7166 1.58789 11.9749 1.58789C12.2332 1.58789 12.4541 1.67956 12.6374 1.86289L15.1999 4.43789C15.3916 4.62956 15.4853 4.85039 15.4812 5.10039C15.477 5.35039 15.3791 5.57122 15.1874 5.76289L12.6374 8.31289C12.4541 8.49622 12.2332 8.58789 11.9749 8.58789C11.7166 8.58789 11.4957 8.49622 11.3124 8.31289C11.1291 8.12956 11.0374 7.90872 11.0374 7.65039C11.0374 7.39206 11.1291 7.17122 11.3124 6.98789L12.2749 6.02539H12.0249C10.3499 6.02539 8.93115 6.60664 7.76865 7.76914C6.60615 8.93164 6.0249 10.3504 6.0249 12.0254ZM17.9749 11.9254C17.9749 11.6587 17.9582 11.4004 17.9249 11.1504C17.8916 10.9004 17.8374 10.6504 17.7624 10.4004C17.6874 10.1421 17.6957 9.88789 17.7874 9.63789C17.8791 9.38789 18.0457 9.20872 18.2874 9.10039C18.5374 8.98372 18.7791 8.97539 19.0124 9.07539C19.2457 9.17539 19.4041 9.35456 19.4874 9.61289C19.6124 9.98789 19.7041 10.3691 19.7624 10.7566C19.8207 11.1441 19.8499 11.5337 19.8499 11.9254C19.8499 14.1171 19.0895 15.9775 17.5687 17.5066C16.0478 19.0358 14.1832 19.8004 11.9749 19.8004H11.7249L12.6874 20.7629C12.8707 20.9462 12.9624 21.1671 12.9624 21.4254C12.9624 21.6837 12.8707 21.9046 12.6874 22.0879C12.5041 22.2712 12.2832 22.3629 12.0249 22.3629C11.7666 22.3629 11.5457 22.2712 11.3624 22.0879L8.7999 19.5129C8.60824 19.3212 8.51449 19.1004 8.51865 18.8504C8.52282 18.6004 8.62074 18.3796 8.8124 18.1879L11.3624 15.6379C11.5457 15.4546 11.7666 15.3629 12.0249 15.3629C12.2832 15.3629 12.5041 15.4546 12.6874 15.6379C12.8707 15.8212 12.9624 16.0421 12.9624 16.3004C12.9624 16.5587 12.8707 16.7796 12.6874 16.9629L11.7249 17.9254H11.9749C13.6499 17.9254 15.0687 17.3441 16.2312 16.1816C17.3937 15.0191 17.9749 13.6004 17.9749 11.9254Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
27
components/Icons/RocketLaunch.tsx
Normal file
27
components/Icons/RocketLaunch.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function RocketLaunch({
|
||||
className,
|
||||
color,
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M5.6498 10.0249L7.5998 10.8499C7.83314 10.3832 8.0748 9.93319 8.3248 9.49986C8.5748 9.06653 8.8498 8.63319 9.1498 8.19986L7.7498 7.92486L5.6498 10.0249ZM9.1998 12.0999L12.0498 14.9249C12.7498 14.6582 13.4998 14.2499 14.2998 13.6999C15.0998 13.1499 15.8498 12.5249 16.5498 11.8249C17.7165 10.6582 18.629 9.36236 19.2873 7.93736C19.9456 6.51236 20.2331 5.19986 20.1498 3.99986C18.9498 3.91653 17.6331 4.20403 16.1998 4.86236C14.7665 5.52069 13.4665 6.4332 12.2998 7.59986C11.5998 8.29986 10.9748 9.04986 10.4248 9.84986C9.8748 10.6499 9.46647 11.3999 9.1998 12.0999ZM13.6498 10.4749C13.2665 10.0915 13.0748 9.62069 13.0748 9.06236C13.0748 8.50403 13.2665 8.0332 13.6498 7.64986C14.0331 7.26653 14.5081 7.07486 15.0748 7.07486C15.6415 7.07486 16.1165 7.26653 16.4998 7.64986C16.8831 8.0332 17.0748 8.50403 17.0748 9.06236C17.0748 9.62069 16.8831 10.0915 16.4998 10.4749C16.1165 10.8582 15.6415 11.0499 15.0748 11.0499C14.5081 11.0499 14.0331 10.8582 13.6498 10.4749ZM14.1248 18.4999L16.2248 16.3999L15.9498 14.9999C15.5165 15.2999 15.0831 15.5707 14.6498 15.8124C14.2165 16.054 13.7665 16.2915 13.2998 16.5249L14.1248 18.4999ZM21.9498 2.17486C22.2665 4.19153 22.0706 6.15403 21.3623 8.06236C20.654 9.9707 19.4331 11.7915 17.6998 13.5249L18.1998 15.9999C18.2665 16.3332 18.2498 16.6582 18.1498 16.9749C18.0498 17.2915 17.8831 17.5665 17.6498 17.7999L13.4498 21.9999L11.3498 17.0749L7.0748 12.7999L2.1498 10.6999L6.3248 6.49986C6.55814 6.26653 6.83731 6.09986 7.1623 5.99986C7.4873 5.89986 7.81647 5.88319 8.1498 5.94986L10.6248 6.44986C12.3581 4.71653 14.1748 3.49153 16.0748 2.77486C17.9748 2.05819 19.9331 1.85819 21.9498 2.17486ZM3.9248 15.9749C4.50814 15.3915 5.22064 15.0957 6.0623 15.0874C6.90397 15.079 7.61647 15.3665 8.1998 15.9499C8.78314 16.5332 9.07064 17.2457 9.0623 18.0874C9.05397 18.929 8.75814 19.6415 8.1748 20.2249C7.75814 20.6415 7.0623 20.9999 6.0873 21.2999C5.1123 21.5999 3.76647 21.8665 2.0498 22.0999C2.28314 20.3832 2.5498 19.0374 2.8498 18.0624C3.1498 17.0874 3.50814 16.3915 3.9248 15.9749ZM5.3498 17.3749C5.18314 17.5415 5.01647 17.8457 4.8498 18.2874C4.68314 18.729 4.56647 19.1749 4.4998 19.6249C4.9498 19.5582 5.39564 19.4457 5.8373 19.2874C6.27897 19.129 6.58314 18.9665 6.7498 18.7999C6.9498 18.5999 7.05814 18.3582 7.0748 18.0749C7.09147 17.7915 6.9998 17.5499 6.7998 17.3499C6.5998 17.1499 6.35814 17.054 6.0748 17.0624C5.79147 17.0707 5.5498 17.1749 5.3498 17.3749Z"
|
||||
fill="#003DF0"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -96,3 +96,8 @@
|
||||
.disabled * {
|
||||
fill: var(--Base-Text-Disabled);
|
||||
}
|
||||
|
||||
.currentColor,
|
||||
.currentColor * {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const config = {
|
||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||
uiTextPlaceholder: styles.uiTextPlaceholder,
|
||||
disabled: styles.disabled,
|
||||
currentColor: styles.currentColor,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
46
components/Loading/index.tsx
Normal file
46
components/Loading/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import styles from "./loading.module.css"
|
||||
|
||||
type Size = "small" | "medium" | "large"
|
||||
type Color = "white" | "burgundy"
|
||||
type Props = {
|
||||
size?: Size
|
||||
color?: Color
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function Loading({
|
||||
size = "medium",
|
||||
color = "burgundy",
|
||||
className,
|
||||
}: Props) {
|
||||
return (
|
||||
<svg
|
||||
width={sizes[size]}
|
||||
height={sizes[size]}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`${styles.loading} ${className}`}
|
||||
>
|
||||
<circle cx="10" cy="1.73913" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="16.087" cy="4.34783" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="18.2609" cy="10" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="16.087" cy="15.6522" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="10" cy="18.2609" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="3.91304" cy="15.6522" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="1.73913" cy="10" r="1.73913" fill={colors[color]} />
|
||||
<circle cx="3.91304" cy="4.34783" r="1.73913" fill={colors[color]} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const colors: { [color in Color]: string } = {
|
||||
white: "#fff",
|
||||
burgundy: "var(--Scandic-Brand-Burgundy)",
|
||||
}
|
||||
|
||||
const sizes: { [size in Size]: number } = {
|
||||
small: 16,
|
||||
medium: 20,
|
||||
large: 24,
|
||||
}
|
||||
43
components/Loading/loading.module.css
Normal file
43
components/Loading/loading.module.css
Normal file
@@ -0,0 +1,43 @@
|
||||
.loading {
|
||||
--animationDuration: 0.8s;
|
||||
--delayPerItem: calc(var(--animationDuration) / 8);
|
||||
|
||||
& circle {
|
||||
animation: fadeInOut var(--animationDuration) infinite linear both;
|
||||
transform-origin: center;
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: calc(var(--delayPerItem) * -7);
|
||||
}
|
||||
&:nth-child(3) {
|
||||
animation-delay: calc(var(--delayPerItem) * -6);
|
||||
}
|
||||
&:nth-child(4) {
|
||||
animation-delay: calc(var(--delayPerItem) * -5);
|
||||
}
|
||||
&:nth-child(5) {
|
||||
animation-delay: calc(var(--delayPerItem) * -4);
|
||||
}
|
||||
&:nth-child(6) {
|
||||
animation-delay: calc(var(--delayPerItem) * -3);
|
||||
}
|
||||
&:nth-child(7) {
|
||||
animation-delay: calc(var(--delayPerItem) * -2);
|
||||
}
|
||||
&:nth-child(8) {
|
||||
animation-delay: calc(var(--delayPerItem) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
0%,
|
||||
20%,
|
||||
80%,
|
||||
100% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,27 @@ import SectionLink from "../Link"
|
||||
|
||||
import styles from "./header.module.css"
|
||||
|
||||
import type { HeaderProps } from "@/types/components/myPages/header"
|
||||
import type { HeadingProps } from "@/components/TempDesignSystem/Text/Title/title"
|
||||
|
||||
export type HeaderProps = {
|
||||
link?: {
|
||||
href: string
|
||||
text: string
|
||||
}
|
||||
preamble?: string | null
|
||||
textTransform?: HeadingProps["textTransform"]
|
||||
title?: string | null
|
||||
headingLevel?: HeadingProps["level"]
|
||||
headingAs?: HeadingProps["as"]
|
||||
}
|
||||
|
||||
export default function SectionHeader({
|
||||
link,
|
||||
preamble,
|
||||
title,
|
||||
topTitle = false,
|
||||
textTransform,
|
||||
headingAs = "h4",
|
||||
headingLevel = "h2",
|
||||
}: HeaderProps) {
|
||||
if (!title && !preamble && !link) {
|
||||
return null
|
||||
@@ -21,9 +34,9 @@ export default function SectionHeader({
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<Title
|
||||
as={topTitle ? "h3" : "h4"}
|
||||
as={headingAs}
|
||||
className={styles.title}
|
||||
level={topTitle ? "h1" : "h2"}
|
||||
level={headingLevel}
|
||||
textTransform={textTransform}
|
||||
>
|
||||
{title}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Accessibility": "Tilgængelighed",
|
||||
"Accessibility at {hotel}": "Tilgængelighed på {hotel}",
|
||||
"Accessible room": "Tilgængelighedsrum",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Aktiv",
|
||||
"Activities": "Aktiviteter",
|
||||
@@ -94,6 +95,7 @@
|
||||
"Cancel": "Afbestille",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Skift værelse",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Check ind",
|
||||
"Check in from: {checkInTime}": "Indtjekning fra: {checkInTime}",
|
||||
"Check out": "Check ud",
|
||||
@@ -184,6 +186,8 @@
|
||||
"Extras to your booking": "Tillæg til din booking",
|
||||
"FAQ": "Ofte stillede spørgsmål",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskab ikke verificeret",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familievenlig",
|
||||
@@ -276,6 +280,7 @@
|
||||
"Level 6": "Niveau 6",
|
||||
"Level 7": "Niveau 7",
|
||||
"Level up to unlock": "Stig i niveau for at låse op",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Niveau {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -550,6 +555,8 @@
|
||||
"Tuesday": "Tirsdag",
|
||||
"Type of bed": "Sengtype",
|
||||
"Type of room": "Værelsestype",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Brug Bonus Cheque",
|
||||
"Use code/voucher": "Brug kode/voucher",
|
||||
"User information": "Brugeroplysninger",
|
||||
@@ -609,6 +616,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "Du finder alle dine gaver i 'Mine fordele'",
|
||||
"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 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!",
|
||||
@@ -666,6 +674,7 @@
|
||||
"{min} to {max} characters": "{min} til {max} tegn",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgængelig",
|
||||
"{number} km to city center": "{number} km til centrum",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} point",
|
||||
"{points} spendable points expiring by {date}": "{points} Brugbare point udløber den {date}",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Accessibility": "Zugänglichkeit",
|
||||
"Accessibility at {hotel}": "Barrierefreiheit im {hotel}",
|
||||
"Accessible room": "Barrierefreies Zimmer",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Aktiv",
|
||||
"Activities": "Aktivitäten",
|
||||
@@ -95,6 +96,7 @@
|
||||
"Cancel": "Stornieren",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Zimmer ändern",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Einchecken",
|
||||
"Check in from: {checkInTime}": "Check-in ab: {checkInTime}",
|
||||
"Check out": "Auschecken",
|
||||
@@ -185,6 +187,8 @@
|
||||
"Extras to your booking": "Extras zu Ihrer Buchung",
|
||||
"FAQ": "Häufig gestellte Fragen",
|
||||
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskab nicht verifiziert",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familienfreundlich",
|
||||
@@ -277,6 +281,7 @@
|
||||
"Level 6": "Level 6",
|
||||
"Level 7": "Level 7",
|
||||
"Level up to unlock": "Zum Freischalten aufsteigen",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Level {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -550,6 +555,8 @@
|
||||
"Tuesday": "Dienstag",
|
||||
"Type of bed": "Bettentyp",
|
||||
"Type of room": "Zimmerart",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Bonusscheck nutzen",
|
||||
"Use code/voucher": "Code/Gutschein nutzen",
|
||||
"User information": "Nutzerinformation",
|
||||
@@ -609,6 +616,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "Alle Ihre Geschenke finden Sie unter „Meine Vorteile“",
|
||||
"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 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!",
|
||||
@@ -666,6 +674,7 @@
|
||||
"{min} to {max} characters": "{min} zu {max} figuren",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} verfügbar",
|
||||
"{number} km to city center": "{number} km zum Stadtzentrum",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} punkte",
|
||||
"{points} spendable points expiring by {date}": "{points} Einlösbare punkte verfallen bis zum {date}",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"Accessibility": "Accessibility",
|
||||
"Accessibility at {hotel}": "Accessibility at {hotel}",
|
||||
"Accessible room": "Accessibility room",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Active",
|
||||
"Activities": "Activities",
|
||||
@@ -97,6 +98,7 @@
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Change room",
|
||||
"Changes can be made until {time}, {date} pending availability.": "Changes can be made until {time}, {date} pending availability.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Check in",
|
||||
"Check in from: {checkInTime}": "Check in from: {checkInTime}",
|
||||
"Check out": "Check out",
|
||||
@@ -187,6 +189,8 @@
|
||||
"Extras to your booking": "Extras to your booking",
|
||||
"FAQ": "FAQ",
|
||||
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Failed to verify membership",
|
||||
"Fair": "Fair",
|
||||
"Family friendly": "Family friendly",
|
||||
@@ -279,6 +283,7 @@
|
||||
"Level 6": "Level 6",
|
||||
"Level 7": "Level 7",
|
||||
"Level up to unlock": "Level up to unlock",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Level {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -554,6 +559,8 @@
|
||||
"Tuesday": "Tuesday",
|
||||
"Type of bed": "Type of bed",
|
||||
"Type of room": "Type of room",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Use Bonus Cheque",
|
||||
"Use code/voucher": "Use code/voucher",
|
||||
"User information": "User information",
|
||||
@@ -613,6 +620,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "You'll find all your gifts in 'My benefits'",
|
||||
"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 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!",
|
||||
@@ -668,6 +676,7 @@
|
||||
"{min} to {max} characters": "{min} to {max} characters",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} available",
|
||||
"{number} km to city center": "{number} km to city center",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} points",
|
||||
"{points} spendable points expiring by {date}": "{points} spendable points expiring by {date}",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Accessibility": "Saavutettavuus",
|
||||
"Accessibility at {hotel}": "Esteettömyys {hotel}",
|
||||
"Accessible room": "Esteetön huone",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Aktiivinen",
|
||||
"Activities": "Aktiviteetit",
|
||||
@@ -93,6 +94,7 @@
|
||||
"Cancel": "Peruuttaa",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Vaihda huonetta",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Sisäänkirjautuminen",
|
||||
"Check in from: {checkInTime}": "Sisäänkirjautuminen alkaen: {checkInTime}",
|
||||
"Check out": "Uloskirjautuminen",
|
||||
@@ -184,6 +186,8 @@
|
||||
"Extras to your booking": "Varauksessa lisäpalveluita",
|
||||
"FAQ": "Usein kysytyt kysymykset",
|
||||
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Jäsenyys ei verifioitu",
|
||||
"Fair": "Messukeskus",
|
||||
"Family friendly": "Perheystävällinen",
|
||||
@@ -276,6 +280,7 @@
|
||||
"Level 6": "Taso 6",
|
||||
"Level 7": "Taso 7",
|
||||
"Level up to unlock": "Nosta taso avataksesi lukituksen",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Taso {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -550,6 +555,8 @@
|
||||
"Tuesday": "Tiistai",
|
||||
"Type of bed": "Vuodetyyppi",
|
||||
"Type of room": "Huonetyyppi",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Käytä bonussekkiä",
|
||||
"Use code/voucher": "Käytä koodia/voucheria",
|
||||
"User information": "Käyttäjän tiedot",
|
||||
@@ -609,6 +616,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "You'll find all your gifts in 'My benefits'",
|
||||
"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 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!",
|
||||
@@ -666,6 +674,7 @@
|
||||
"{min} to {max} characters": "{min} to {max} hahmoja",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} saatavilla",
|
||||
"{number} km to city center": "{number} km Etäisyys kaupunkiin",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} pistettä",
|
||||
"{points} spendable points expiring by {date}": "{points} pistettä vanhenee {date} mennessä",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Accessibility": "Tilgjengelighet",
|
||||
"Accessibility at {hotel}": "Tilgjengelighet på {hotel}",
|
||||
"Accessible room": "Tilgjengelighetsrom",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Aktiv",
|
||||
"Activities": "Aktiviteter",
|
||||
@@ -93,6 +94,7 @@
|
||||
"Cancel": "Avbryt",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Endre rom",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Sjekk inn",
|
||||
"Check in from: {checkInTime}": "Innsjekking fra: {checkInTime}",
|
||||
"Check out": "Sjekk ut",
|
||||
@@ -183,6 +185,8 @@
|
||||
"Extras to your booking": "Tilvalg til bestillingen din",
|
||||
"FAQ": "Ofte stilte spørsmål",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskap ikke verifisert",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familievennlig",
|
||||
@@ -275,6 +279,7 @@
|
||||
"Level 6": "Nivå 6",
|
||||
"Level 7": "Nivå 7",
|
||||
"Level up to unlock": "Nivå opp for å låse opp",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Nivå {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -548,6 +553,8 @@
|
||||
"Tuesday": "Tirsdag",
|
||||
"Type of bed": "Sengtype",
|
||||
"Type of room": "Romtype",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Bruk bonussjekk",
|
||||
"Use code/voucher": "Bruk kode/voucher",
|
||||
"User information": "Brukerinformasjon",
|
||||
@@ -607,6 +614,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "Du finner alle gavene dine i 'Mine fordeler'",
|
||||
"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 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!",
|
||||
@@ -664,6 +672,7 @@
|
||||
"{min} to {max} characters": "{min} til {max} tegn",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgjengelig",
|
||||
"{number} km to city center": "{number} km til sentrum",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} poeng",
|
||||
"{points} spendable points expiring by {date}": "{points} Brukbare poeng utløper innen {date}",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Accessibility": "Tillgänglighet",
|
||||
"Accessibility at {hotel}": "Tillgänglighet på {hotel}",
|
||||
"Accessible room": "Tillgänglighetsrum",
|
||||
"Account unlinked, reloading": "Account unlinked, reloading",
|
||||
"Accounts are already linked": "Accounts are already linked",
|
||||
"Active": "Aktiv",
|
||||
"Activities": "Aktiviteter",
|
||||
@@ -93,6 +94,7 @@
|
||||
"Cancel": "Avbryt",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Ändra rum",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Checka in",
|
||||
"Check in from: {checkInTime}": "Incheckning från: {checkInTime}",
|
||||
"Check out": "Checka ut",
|
||||
@@ -183,6 +185,8 @@
|
||||
"Extras to your booking": "Extra tillval till din bokning",
|
||||
"FAQ": "FAQ",
|
||||
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
||||
"Failed to unlink account": "Failed to unlink account",
|
||||
"Failed to upgrade level": "Failed to upgrade level",
|
||||
"Failed to verify membership": "Medlemskap inte verifierat",
|
||||
"Fair": "Mässa",
|
||||
"Family friendly": "Familjevänligt",
|
||||
@@ -275,6 +279,7 @@
|
||||
"Level 6": "Nivå 6",
|
||||
"Level 7": "Nivå 7",
|
||||
"Level up to unlock": "Levla upp för att låsa upp",
|
||||
"Level upgraded": "Level upgraded",
|
||||
"Level {level}": "Nivå {level}",
|
||||
"Link my accounts": "Link my accounts",
|
||||
"Link your accounts": "Link your accounts",
|
||||
@@ -548,6 +553,8 @@
|
||||
"Tuesday": "Tisdag",
|
||||
"Type of bed": "Sängtyp",
|
||||
"Type of room": "Rumstyp",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Use Bonus Cheque": "Använd bonuscheck",
|
||||
"Use code/voucher": "Använd kod/voucher",
|
||||
"User information": "Användarinformation",
|
||||
@@ -607,6 +614,7 @@
|
||||
"You must accept the terms and conditions": "You must accept the terms and conditions",
|
||||
"You'll find all your gifts in 'My benefits'": "Du hittar alla dina gåvor i 'Mina förmåner'",
|
||||
"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 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!",
|
||||
@@ -666,6 +674,7 @@
|
||||
"{min} to {max} characters": "{min} till {max} tecken",
|
||||
"{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tillgängliga",
|
||||
"{number} km to city center": "{number} km till centrum",
|
||||
"{points, number} Bonus points": "{points, number} Bonus points",
|
||||
"{pointsAmount, number} points": "{pointsAmount, number} poäng",
|
||||
"{points} spendable points expiring by {date}": "{points} poäng förfaller {date}",
|
||||
"{price} {currency}": "{price} {currency}",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
query GetAccountPage($locale: String!, $uid: String!) {
|
||||
account_page(locale: $locale, uid: $uid) {
|
||||
heading
|
||||
preamble
|
||||
title
|
||||
url
|
||||
content {
|
||||
|
||||
@@ -43,6 +43,7 @@ export const accountPageSchema = z.object({
|
||||
account_page: z.object({
|
||||
content: discriminatedUnionArray(blocksSchema.options),
|
||||
heading: z.string().nullable(),
|
||||
preamble: z.string().nullable(),
|
||||
title: z.string(),
|
||||
url: z.string(),
|
||||
system: systemSchema.merge(
|
||||
|
||||
@@ -3,5 +3,13 @@ import { router } from "@/server/trpc"
|
||||
import { requestOtp } from "./otp/request/requestOtp"
|
||||
import { verifyOtp } from "./otp/verify/verifyOtp"
|
||||
import { linkAccount } from "./linkAccount"
|
||||
import { performLevelUpgrade } from "./performLevelUpgrade"
|
||||
import { unlinkAccount } from "./unlinkAccount"
|
||||
|
||||
export const sasRouter = router({ verifyOtp, requestOtp, linkAccount })
|
||||
export const sasRouter = router({
|
||||
verifyOtp,
|
||||
requestOtp,
|
||||
linkAccount,
|
||||
unlinkAccount,
|
||||
performLevelUpgrade,
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
@@ -14,7 +15,7 @@ export const linkAccount = protectedProcedure
|
||||
const sasAuthToken = getSasToken()
|
||||
|
||||
console.log("[SAS] link account")
|
||||
await timeout(1000)
|
||||
await setTimeout(1000)
|
||||
//TODO: Call actual API here
|
||||
console.log("[SAS] link account done")
|
||||
|
||||
@@ -22,7 +23,3 @@ export const linkAccount = protectedProcedure
|
||||
linkingState: "linked",
|
||||
}
|
||||
})
|
||||
|
||||
function timeout(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
20
server/routers/partners/sas/performLevelUpgrade.ts
Normal file
20
server/routers/partners/sas/performLevelUpgrade.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { TRPCError } from "@trpc/server"
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
const outputSchema = z.object({})
|
||||
|
||||
export const performLevelUpgrade = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.log("[SAS] perform upgrade")
|
||||
await setTimeout(1000)
|
||||
//TODO: Call actual API here
|
||||
|
||||
throw new TRPCError({
|
||||
message: "Unable to perform ugprade",
|
||||
code: "BAD_REQUEST",
|
||||
})
|
||||
})
|
||||
26
server/routers/partners/sas/unlinkAccount.ts
Normal file
26
server/routers/partners/sas/unlinkAccount.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { TRPCError } from "@trpc/server"
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
const outputSchema = z.object({
|
||||
// unlinked: z.boolean(),
|
||||
})
|
||||
|
||||
export const unlinkAccount = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.log("[SAS] unlink account")
|
||||
await setTimeout(1000)
|
||||
//TODO: Call actual API here
|
||||
|
||||
throw new TRPCError({
|
||||
message: "Unable to unlink account",
|
||||
code: "BAD_REQUEST",
|
||||
})
|
||||
|
||||
return {
|
||||
unlinked: true,
|
||||
}
|
||||
})
|
||||
@@ -16,6 +16,7 @@ export namespace DynamicContentEnum {
|
||||
sign_up_verification = "sign_up_verification",
|
||||
soonest_stays = "soonest_stays",
|
||||
upcoming_stays = "upcoming_stays",
|
||||
sas_linked_account = "sas_linked_account",
|
||||
sas_tier_comparison = "sas_tier_comparison",
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ export namespace DynamicContentEnum {
|
||||
components.sign_up_verification,
|
||||
components.soonest_stays,
|
||||
components.upcoming_stays,
|
||||
components.sas_linked_account,
|
||||
components.sas_tier_comparison,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod"
|
||||
import type { z } from "zod"
|
||||
|
||||
import {
|
||||
import type {
|
||||
accountPageRefsSchema,
|
||||
accountPageSchema,
|
||||
blocksSchema,
|
||||
|
||||
Reference in New Issue
Block a user