Merged in feat/sw-1291-show-sas-membership-data (pull request #1503)

Show SAS membership data in Linked Accounts

* Rip out old styling

* Desktop version of new linked accounts design

* Use new design system tokens

* Refactor SASLinkedAccount to handle all states

* Improve small screen styling

* Add intl etc

* Skeletons

* Tiny fixes

* Add i18n keys to all languages


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-03-10 10:13:18 +00:00
parent 9280bb3f1c
commit 393546d35d
12 changed files with 432 additions and 349 deletions

View File

@@ -1,207 +0,0 @@
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>
</>
),
},
}

View File

@@ -1,93 +0,0 @@
.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);
}

View File

@@ -4,6 +4,7 @@ import { useParams } from "next/navigation"
import { useIntl } from "react-intl"
import Dialog from "@/components/Dialog"
import { ChevronRightSmallIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import type { LangParams } from "@/types/params"
@@ -27,6 +28,7 @@ export function UnlinkSAS() {
trigger={
<Button intent="text" theme="base">
{intl.formatMessage({ id: "Unlink accounts" })}
<ChevronRightSmallIcon color="burgundy" />
</Button>
}
/>

View File

@@ -1,20 +1,30 @@
import { Suspense } from "react"
import { cx } from "class-variance-authority"
import { type ReactNode, Suspense } from "react"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels"
import { env } from "@/env/server"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { DiamondIcon, InfoCircleIcon, LinkIcon } from "@/components/Icons"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import { timeout } from "@/utils/timeout"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import { getIntl } from "@/i18n"
import {
getEurobonusMembership,
getFriendsMembership,
scandicMemberships,
} from "@/utils/user"
import { TierLevelCard, TierLevelCardSkeleton } from "./Card/TierLevelCard"
import { LevelUpgradeButton } from "./LevelUpgradeButton"
import { UnlinkSAS } from "./UnlinkSAS"
import styles from "./linkedAccounts.module.css"
import type { Membership } from "@/types/user"
type Props = {
title?: string
link?: { href: string; text: string }
@@ -30,65 +40,271 @@ export default async function SASLinkedAccount({
return null
}
const intl = await getIntl()
return (
<>
<div className={styles.container}>
<SectionContainer>
<SectionHeader link={link} preamble={subtitle} title={title} />
<SectionLink link={link} variant="mobile" />
<section className={styles.cardsContainer}>
<Suspense fallback={<TierLevelCardsSkeleton />}>
<TierLevelCards />
<Suspense fallback={<MatchedAccountInfoSkeleton />}>
<MatchedAccountInfo />
</Suspense>
</section>
</SectionContainer>
<div className={styles.mutationSection}>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p className={styles.caption}>
<InfoCircleIcon height={20} width={20} />
{intl.formatMessage({
id: "Changes in tier match can take up to 24 hours to be displayed.",
})}
</p>
</Typography>
<UnlinkSAS />
<LevelUpgradeButton />
</div>
</>
</div>
)
}
function TierLevelCardsSkeleton() {
return (
<>
<TierLevelCardSkeleton bonusSystem={"scandic"} />
<TierLevelCardSkeleton bonusSystem={"sas"} />
</>
)
}
async function TierLevelCards() {
console.log("[SAS] Fetching tier level cards")
await timeout(2_000)
console.log("[SAS] AFTER Fetching tier level cards")
async function MatchedAccountInfo() {
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"]
const intl = await getIntl()
const eurobonusMembership = getEurobonusMembership(user.memberships)
const friendsMembership = user.membership
if (!eurobonusMembership || !friendsMembership) {
return null
}
const sasLevelName = eurobonusMembership.membershipLevel || "-"
const sasMembershipNumber = eurobonusMembership.membershipNumber
const sasTierExpirationDate = eurobonusMembership.tierExpirationDate
const scandicLevelName = TIER_TO_FRIEND_MAP[friendsMembership.membershipLevel]
const scandicExpirationDate = friendsMembership.tierExpirationDate
const matchState = calculateMatchState(user.memberships)
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"
/>
</>
<section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>SAS EuroBonus</p>
</Typography>
</div>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Tier status" })}</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>{sasLevelName}</p>
</Typography>
</div>
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>{intl.formatMessage({ id: "Member number" })}</Label>
<Typography variant="Body/Paragraph/mdBold">
<p className={styles.textRight}>EB{sasMembershipNumber}</p>
</Typography>
</div>
</div>
<div className={styles.tierMatchStatus}>
<TierMatchMessage
matchState={matchState}
scandicLevelName={scandicLevelName}
sasLevelName={sasLevelName}
/>
<TierMatchExpiration
matchState={matchState}
sasExpirationDate={sasTierExpirationDate}
scandicExpirationDate={scandicExpirationDate}
/>
</div>
</section>
)
}
async function MatchedAccountInfoSkeleton() {
const intl = await getIntl()
return (
<section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>SAS EuroBonus</p>
</Typography>
</div>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Tier status" })}</Label>
<SkeletonShimmer width="6ch" height="24px" />
</div>
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>{intl.formatMessage({ id: "Member number" })}</Label>
<SkeletonShimmer width="10ch" height="24px" />
</div>
</div>
<div className={styles.tierMatchStatus}>
<TierMatchMessageSkeleton />
</div>
</section>
)
}
type TierMatchMessageProps = {
matchState: MatchState
scandicLevelName: string
sasLevelName: string
}
async function TierMatchMessage({
matchState,
sasLevelName,
scandicLevelName,
}: TierMatchMessageProps) {
const intl = await getIntl()
const messageValues = {
sasLevelName: sasLevelName,
scandicLevelName: scandicLevelName,
sasMark: (text: ReactNode) => (
<Typography variant="Body/Paragraph/mdBold">
<span className={styles.sasMark}>{text}</span>
</Typography>
),
scandicMark: (text: ReactNode) => (
<Typography variant="Body/Paragraph/mdBold">
<span className={styles.scandicMark}>{text}</span>
</Typography>
),
}
const messageMap: Record<MatchState, ReactNode> = {
boostedBySAS: intl.formatMessage(
{
id: "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
},
messageValues
),
boostedByScandic: intl.formatMessage(
{
id: "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
},
messageValues
),
noBoost: intl.formatMessage(
{
id: "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
},
messageValues
),
}
const iconMap: Record<MatchState, ReactNode> = {
boostedBySAS: (
<DiamondIcon height={20} width={20} color="uiTextMediumContrast" />
),
boostedByScandic: (
<DiamondIcon height={20} width={20} color="uiTextMediumContrast" />
),
noBoost: <LinkIcon height={20} width={20} color="uiTextMediumContrast" />,
}
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Tier match status" })}</Label>
<div className={styles.tierMatchText}>
<div className={styles.iconWrapper}>{iconMap[matchState]}</div>
<Typography variant="Body/Paragraph/mdRegular">
<p>{messageMap[matchState]}</p>
</Typography>
</div>
</div>
)
}
async function TierMatchMessageSkeleton() {
const intl = await getIntl()
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Tier match status" })}</Label>
<div className={styles.tierMatchText}>
<SkeletonShimmer width="250px" height="24px" />
</div>
</div>
)
}
type TierMatchExpirationProps = {
matchState: MatchState
sasExpirationDate: string | undefined
scandicExpirationDate: string | undefined
}
async function TierMatchExpiration({
matchState,
sasExpirationDate,
scandicExpirationDate,
}: TierMatchExpirationProps) {
if (matchState === "noBoost") {
return null
}
const intl = await getIntl()
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Upgrade valid until" })}</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>
{matchState === "boostedBySAS"
? scandicExpirationDate
: sasExpirationDate}
</p>
</Typography>
</div>
)
}
function Label({ children }: { children: ReactNode }) {
return (
<Typography variant="Tag/sm">
<p className={styles.label}>{children}</p>
</Typography>
)
}
type MatchState = "boostedBySAS" | "boostedByScandic" | "noBoost"
function calculateMatchState(memberships: Membership[]): MatchState {
const eurobonusMembership = getEurobonusMembership(memberships)
const friendsMembership = getFriendsMembership(memberships)
const nativeMembership = memberships.find(
(x) => x.membershipType === scandicMemberships.scandic_native_tiers
)
if (!eurobonusMembership || !friendsMembership || !nativeMembership) {
return "noBoost"
}
const nativeLevel = nativeMembership.membershipLevel
const friendsLevel = friendsMembership.membershipLevel
if (nativeLevel !== friendsLevel) {
return "boostedBySAS"
}
// TODO check if SAS have been boosted by Scandic when API is available
const isBoostedByScandic = false
if (isBoostedByScandic) {
return "boostedByScandic"
}
return "noBoost"
}

View File

@@ -1,9 +1,13 @@
.divider {
margin-top: var(--Spacing-x2);
.container {
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
}
@media screen and (min-width: 768px) {
.divider {
.divider {
margin-top: var(--Spacing-x2);
@media screen and (min-width: 768px) {
display: none;
}
}
@@ -21,11 +25,117 @@
.mutationSection {
display: flex;
flex-direction: column-reverse;
flex-direction: column;
justify-content: space-between;
gap: var(--Spacing-x2);
align-items: center;
align-items: flex-end;
@media screen and (min-width: 768px) {
align-items: center;
flex-direction: row;
}
}
.matchedAccountSection {
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
padding: var(--Spacing-x4) var(--Spacing-x-one-and-half);
width: 100%;
background-color: var(--Surface-Primary-On-Surface-Default);
border: 1px solid var(--Border-Default);
border-radius: var(--Corner-radius-Medium);
box-shadow:
0px 0px 4px 2px rgba(0, 0, 0, 0.1),
0px 4px 4px 0px rgba(255, 255, 255, 0.29) inset;
@media screen and (min-width: 768px) {
padding: var(--Spacing-x4) var(--Spacing-x3);
}
}
.accountDetails {
display: flex;
width: 100%;
justify-content: space-between;
gap: var(--Spacing-x1);
@media screen and (min-width: 768px) {
gap: var(--Spacing-x7);
}
}
.accountMemberNumber {
@media screen and (min-width: 768px) {
margin-left: auto;
}
}
.tierMatchStatus {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
width: 100%;
justify-content: space-between;
border: 1px solid var(--Border-Divider-Accent);
border-radius: var(--Corner-radius-Small);
padding: var(--Spacing-x2);
background:
linear-gradient(
0deg,
var(--Surface-Brand-Primary-1-Default, --Scandic-Peach-10),
var(--Surface-Brand-Primary-1-Default, --Scandic-Peach-10)
),
linear-gradient(
180deg,
rgba(242, 236, 230, 0.05) 0%,
rgba(143, 67, 80, 0.05) 100%
);
@media screen and (min-width: 768px) {
flex-direction: row;
gap: var(--Spacing-x1);
}
}
.textRight {
text-align: right;
}
.stack {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-half);
}
.caption {
display: flex;
align-items: center;
gap: var(--Spacing-x-half);
color: var(--Text-Tertiary);
align-self: flex-start;
}
.sasMark {
color: var(--Scandic-Blue-70);
}
.scandicMark {
color: var(--Scandic-Red-Default);
}
.tierMatchText {
display: flex;
align-items: center;
gap: var(--Spacing-x1);
}
.label {
color: var(--Text-Tertiary);
text-transform: uppercase;
}
.iconWrapper {
display: flex;
align-items: center;
}

View File

@@ -3,6 +3,9 @@
"/night per adult": "/nat per voksen",
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
"<b>Total price</b> (incl VAT)": "<b>Samlet pris</b> (inkl. moms)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/nat per voksen",
"<strong>Status</strong> Paid": "<strong>Status</strong> Betalt",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Ikke betalt",
@@ -126,6 +129,7 @@
"Cancelled": "Annulleret",
"Change room": "Skift værelse",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Ændringer kan gøres indtil {time} på {date}, under forudsætning af tilgængelighed. Priserne for værelserne kan variere.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Check ind",
"Check in from: {checkInTime}": "Indtjekning fra: {checkInTime}",
@@ -365,6 +369,7 @@
"Lighting": "Belysning",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Beliggenhed",
"Location in hotel": "Plassering på hotellet",
"Locations": "Placeringer",
@@ -392,6 +397,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Medlemsnummer",
"Member number": "Member number",
"Member price": "Medlemspris",
"Member price activated": "Medlemspris aktiveret",
"Member price from": "Medlemspris fra",
@@ -660,6 +666,8 @@
"This room is not available": "Dette værelse er ikke tilgængeligt",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Torsdag",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Tider",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "For at få medlemsprisen <span>{price}</span>, log ind eller tilmeld dig, når du udfylder bookingen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For at sikre din reservation, beder vi om at du giver os dine betalingsoplysninger. Du kan så være sikker på, at ingen gebyrer vil blive opkrævet på dette tidspunkt.",
@@ -682,6 +690,7 @@
"Unpaid": "Ikke betalt",
"Until {time}, {date}": "Indtil {time} den {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Opgrader dit ophold",
"Use Bonus Cheque": "Brug Bonus Cheque",
"Use bonus cheque": "Brug Bonus Cheque",

View File

@@ -3,6 +3,9 @@
"/night per adult": "/Nacht pro Erwachsenem",
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
"<b>Total price</b> (incl VAT)": "<b>Gesamtpreis</b> (inkl. MwSt.)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/Nacht pro Erwachsenem",
"<strong>Status</strong> Paid": "<strong>Status</strong> Bezahlt",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Unbezahlt",
@@ -127,6 +130,7 @@
"Cancelled": "Storniert",
"Change room": "Zimmer ändern",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Änderungen können bis {time} am {date} vorgenommen werden, vorausgesetzt, dass die Zimmer noch verfügbar sind. Die Zimmerpreise können variieren.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Einchecken",
"Check in from: {checkInTime}": "Check-in ab: {checkInTime}",
@@ -366,6 +370,7 @@
"Lighting": "Beleuchtung",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Ort",
"Location in hotel": "Lage im Hotel",
"Locations": "Orte",
@@ -393,6 +398,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Mitgliedsnummer",
"Member number": "Member number",
"Member price": "Mitgliederpreis",
"Member price activated": "Mitgliederpreis aktiviert",
"Member price from": "Mitgliederpreis ab",
@@ -658,6 +664,8 @@
"This room is not available": "Dieses Zimmer ist nicht verfügbar",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Donnerstag",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Zeiten",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "Um den Mitgliederpreis von <span>{price}</span> zu erhalten, loggen Sie sich ein oder treten Sie Scandic Friends bei, wenn Sie die Buchung abschließen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Um Ihre Reservierung zu sichern, bitten wir Sie, Ihre Zahlungskarteninformationen zu geben. Sie können sicher sein, dass keine Gebühren zu diesem Zeitpunkt erhoben werden.",
@@ -680,6 +688,7 @@
"Unpaid": "Nicht bezahlt",
"Until {time}, {date}": "Bis {time} am {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Werten Sie Ihren Aufenthalt auf",
"Use Bonus Cheque": "Bonusscheck nutzen",
"Use bonus cheque": "Bonusscheck nutzen",

View File

@@ -3,6 +3,9 @@
"/night per adult": "/night per adult",
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
"<b>Total price</b> (incl VAT)": "<b>Total price</b> (incl VAT)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult",
"<strong>Status</strong> Paid": "<strong>Status</strong> Paid",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Unpaid",
@@ -130,6 +133,7 @@
"Category": "Category",
"Change room": "Change room",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Changes can be made until {time} on {date}, subject to availability. Room rates may vary.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Check in",
"Check in from: {checkInTime}": "Check in from: {checkInTime}",
@@ -371,6 +375,7 @@
"Lighting": "Lighting",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Location",
"Location (shown in local language)": "Location (shown in local language)",
"Location in hotel": "Location in hotel",
@@ -399,6 +404,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Member no.",
"Member number": "Member number",
"Member price": "Member price",
"Member price activated": "Member price activated",
"Member price from": "Member price from",
@@ -666,6 +672,8 @@
"This room is not available": "This room is not available",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Thursday",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Times",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "To get the member price <span>{price}</span>, log in or join when completing the booking.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
@@ -688,6 +696,7 @@
"Unpaid": "Unpaid",
"Until {time}, {date}": "Until {time}, {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Upgrade your stay",
"Use Bonus Cheque": "Use Bonus Cheque",
"Use bonus cheque": "Use bonus cheque",

View File

@@ -3,6 +3,9 @@
"/night per adult": "/yötä aikuista kohti",
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
"<b>Total price</b> (incl VAT)": "<b>Kokonaishinta</b> (sis. ALV)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/yötä aikuista kohti",
"<strong>Status</strong> Paid": "<strong>Status</strong> Maksettu",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Ei maksettu",
@@ -125,6 +128,7 @@
"Cancelled": "Peruttu",
"Change room": "Vaihda huonetta",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Muutoksia voi tehdä {time} päivänä {date}, olettaen saatavuuden olemassaolon. Huonehinnat voivat muuttua.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Sisäänkirjautuminen",
"Check in from: {checkInTime}": "Sisäänkirjautuminen alkaen: {checkInTime}",
@@ -365,6 +369,7 @@
"Lighting": "Valaistus",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Sijainti",
"Location in hotel": "Sijainti hotellissa",
"Locations": "Sijainnit",
@@ -392,6 +397,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Jäsenyysnumero",
"Member number": "Member number",
"Member price": "Jäsenhinta",
"Member price activated": "Jäsenhinta aktivoitu",
"Member price from": "Jäsenhinta alkaen",
@@ -658,6 +664,8 @@
"This room is not available": "Tämä huone ei ole käytettävissä",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Thursday",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Ajat",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "Jäsenhintaan saavat sisäänkirjautuneet tai liittyneet jäsenet.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Varmistaaksesi varauksen, pyydämme sinua antamaan meille maksukortin tiedot. Varmista, että ei veloiteta maksusi tällä hetkellä.",
@@ -680,6 +688,7 @@
"Unpaid": "Maksettaa",
"Until {time}, {date}": "Asti {time}, {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Päivitä oleskelusi",
"Use Bonus Cheque": "Käytä bonussekkiä",
"Use bonus cheque": "Käytä bonussekkiä",

View File

@@ -3,6 +3,9 @@
"/night per adult": "/natt per voksen",
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl. mva)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/natt per voksen",
"<strong>Status</strong> Paid": "<strong>Status</strong> Betalt",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Ikke betalt",
@@ -125,6 +128,7 @@
"Cancelled": "Avbrutt",
"Change room": "Endre rom",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Endringer kan gjøres til {time} på {date}, under forutsetning av tilgjengelighet. Rompriser kan variere.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Sjekk inn",
"Check in from: {checkInTime}": "Innsjekking fra: {checkInTime}",
@@ -364,6 +368,7 @@
"Lighting": "Belysning",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Beliggenhet",
"Location in hotel": "Plassering på hotellet",
"Locations": "Steder",
@@ -391,6 +396,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Medlemsnummer",
"Member number": "Member number",
"Member price": "Medlemspris",
"Member price activated": "Medlemspris aktivert",
"Member price from": "Medlemspris fra",
@@ -656,6 +662,8 @@
"This room is not available": "Dette rommet er ikke tilgjengelig",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Torsdag",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Tider",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "For å få medlemsprisen <span>{price}</span>, logg inn eller bli med når du fullfører bestillingen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For å sikre din reservasjon, ber vi om at du gir oss dine betalingskortdetaljer. Vær sikker på at ingen gebyrer vil bli belastet på dette tidspunktet.",
@@ -678,6 +686,7 @@
"Unpaid": "Ikke betalt",
"Until {time}, {date}": "Til {time}, {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Oppgrader oppholdet ditt",
"Use Bonus Cheque": "Bruk bonussjekk",
"Use bonus cheque": "Bruk bonussjekk",

View File

@@ -3,6 +3,9 @@
"/night per adult": "/natt per vuxen",
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl moms)",
"<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!": "<sasMark>SAS {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up one of your memberships for a chance of an upgrade!",
"<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.": "<sasMark>SAS {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
"<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.": "<scandicMark>Scandic {scandicLevelName}</scandicMark> has upgraded you to <sasMark>{sasLevelName}</sasMark>.",
"<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult": "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/natt per vuxen",
"<strong>Status</strong> Paid": "<strong>Status</strong> Betalat",
"<strong>Status</strong> Unpaid": "<strong>Status</strong> Ej betalat",
@@ -125,6 +128,7 @@
"Cancelled": "Avbokad",
"Change room": "Ändra rum",
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Ändringar kan göras tills {time} den {date}, under förutsättning av tillgänglighet. Priserna för rummen kan variera.",
"Changes in tier match can take up to 24 hours to be displayed.": "Changes in tier match can take up to 24 hours to be displayed.",
"Check for level upgrade": "Check for level upgrade",
"Check in": "Checka in",
"Check in from: {checkInTime}": "Incheckning från: {checkInTime}",
@@ -364,6 +368,7 @@
"Lighting": "Ljussättning",
"Link my accounts": "Link my accounts",
"Link your accounts": "Link your accounts",
"Linked account": "Linked account",
"Location": "Plats",
"Location in hotel": "Plats på hotellet",
"Locations": "Platser",
@@ -391,6 +396,7 @@
"Member Since: {value}": "Member Since: {value}",
"Member discount": "Member discount",
"Member no.": "Medlemsnummer",
"Member number": "Member number",
"Member price": "Medlemspris",
"Member price activated": "Medlemspris aktiverat",
"Member price from": "Medlemspris från",
@@ -656,6 +662,8 @@
"This room is not available": "Detta rum är inte tillgängligt",
"This verifcation is needed for additional security.": "This verifcation is needed for additional security.",
"Thursday": "Torsdag",
"Tier match status": "Tier match status",
"Tier status": "Tier status",
"Times": "Tider",
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "För att få medlemsprisen <span>{price}</span>, logga in eller bli medlem när du slutför bokningen.",
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "För att säkra din bokning ber vi om att du ger oss dina betalkortdetaljer. Välj säker på att ingen avgifter kommer att debiteras just nu.",
@@ -678,6 +686,7 @@
"Unpaid": "Ej betalt",
"Until {time}, {date}": "Tills {time} den {date}",
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
"Upgrade valid until": "Upgrade valid until",
"Upgrade your stay": "Uppgradera din vistelse",
"Use Bonus Cheque": "Använd bonuscheck",
"Use bonus cheque": "Använd bonuscheck",

View File

@@ -12,6 +12,7 @@ export enum scandicMemberships {
guestpr = "guestpr",
scandicfriends = "scandicfriend's",
sas_eb = "sas_eb",
scandic_native_tiers = "scandic_native_tiers",
}
export function getFriendsMembership(memberships: Memberships) {