Merge branch 'develop' into feat/build-initial-hotel-page-component
This commit is contained in:
@@ -85,7 +85,7 @@ function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
|
||||
}
|
||||
return (
|
||||
<article className={styles.card}>
|
||||
<Title className={styles.tierHeading} level="h4">
|
||||
<Title className={styles.levelHeading} level="h4">
|
||||
{level.level}
|
||||
</Title>
|
||||
{Level ? <Level color="primaryLightOnSurfaceAccent" /> : null}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
padding: var(--Spacing-x5) var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.tierHeading {
|
||||
.levelHeading {
|
||||
color: var(--Scandic-Peach-70);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
.benefitCard {
|
||||
background-color: var(--Scandic-Opacity-White-100);
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
color: var(--Main-Brand-Burgundy);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
padding-bottom: var(--Spacing-x-one-and-half);
|
||||
z-index: 2;
|
||||
grid-column: 1/3;
|
||||
}
|
||||
@@ -16,11 +12,11 @@
|
||||
.benefitCardDescription {
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
line-height: 150%;
|
||||
padding-right: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.benefitInfo {
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
padding: var(--Spacing-x-one-and-half) 0;
|
||||
padding-bottom: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.benefitComparison {
|
||||
@@ -32,7 +28,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: var(--Spacing-x-one-and-half);
|
||||
padding-top: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.details[open] .chevron {
|
||||
@@ -42,6 +38,7 @@
|
||||
.chevron {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--UI-Grey-80);
|
||||
}
|
||||
|
||||
.summary::-webkit-details-marker {
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
.benefitCardWrapper {
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-column: 1/3;
|
||||
padding: var(--Spacing-x2);
|
||||
padding-top: 0;
|
||||
margin: var(--Spacing-x1) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.benefitCardWrapper:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import { getHighestLevel } from "@/utils/loyaltyTable"
|
||||
|
||||
import BenefitCard from "../BenefitCard"
|
||||
|
||||
import styles from "./benefitList.module.css"
|
||||
|
||||
import {
|
||||
BenefitListProps,
|
||||
ComparisonLevel,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
import { BenefitListProps } from "@/types/components/loyalty/blocks"
|
||||
|
||||
export default function BenefitList({ levels }: BenefitListProps) {
|
||||
const highestTier = levels.reduce(
|
||||
(acc: ComparisonLevel | null, level: ComparisonLevel) => {
|
||||
if (!acc) {
|
||||
return level
|
||||
}
|
||||
return level.tier > acc.tier ? level : acc
|
||||
},
|
||||
null
|
||||
)
|
||||
|
||||
return highestTier?.benefits
|
||||
const highestLevel = getHighestLevel(levels)
|
||||
return highestLevel?.benefits
|
||||
.filter((benefit) => benefit.unlocked)
|
||||
.map((benefit, idx) => {
|
||||
const levelBenefits = levels.map((level) => level.benefits[idx])
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.iconRow {
|
||||
border-bottom: none;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.verticalTableHeader {
|
||||
min-width: 242px;
|
||||
}
|
||||
|
||||
.iconTh {
|
||||
padding: var(--Spacing-x5) var(--Spacing-x2) var(--Spacing-x2);
|
||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.summaryTh {
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
||||
padding: 0 var(--Spacing-x2) var(--Spacing-x2);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.select {
|
||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
||||
padding: 0 var(--Spacing-x2) var(--Spacing-x2);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import Image from "@/components/Image"
|
||||
|
||||
import LevelSummary from "../../LevelSummary"
|
||||
import YourLevel from "../../YourLevelScript"
|
||||
|
||||
import styles from "./desktopHeader.module.css"
|
||||
|
||||
import {
|
||||
DesktopSelectColumns,
|
||||
LargeTableProps,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
|
||||
export default function DesktopHeader({
|
||||
levels,
|
||||
activeLevel,
|
||||
Select,
|
||||
}: LargeTableProps) {
|
||||
return (
|
||||
<thead>
|
||||
<tr className={styles.iconRow}>
|
||||
<th className={styles.verticalTableHeader} />
|
||||
{levels.map((level, idx) => {
|
||||
return (
|
||||
<th key={"image" + level.level + idx} className={styles.iconTh}>
|
||||
{activeLevel === level.level ? <YourLevel /> : null}
|
||||
<Image
|
||||
height={50}
|
||||
width={100}
|
||||
alt={level.name}
|
||||
src={level.icon}
|
||||
/>
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
<tr>
|
||||
<th />
|
||||
{levels.map((level, idx) => {
|
||||
return (
|
||||
<th
|
||||
key={"summary" + level.level + idx}
|
||||
className={styles.summaryTh}
|
||||
>
|
||||
<LevelSummary level={level} />
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{Select && (
|
||||
<tr>
|
||||
<th />
|
||||
{["A", "B", "C"].map((column, idx) => {
|
||||
return (
|
||||
<th key={column + idx} className={styles.select}>
|
||||
<Select column={column as DesktopSelectColumns["column"]} />
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
</thead>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChevronDown } from "react-feather"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getHighestLevel } from "@/utils/loyaltyTable"
|
||||
|
||||
import BenefitValue from "../BenefitValue"
|
||||
import LevelSummary from "../LevelSummary"
|
||||
import DesktopHeader from "./DesktopHeader"
|
||||
|
||||
import styles from "./largeTable.module.css"
|
||||
|
||||
@@ -13,57 +13,41 @@ import {
|
||||
LargeTableProps,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
|
||||
export default function LargeTable({ levels }: LargeTableProps) {
|
||||
export default function LargeTable({
|
||||
levels,
|
||||
activeLevel,
|
||||
Select,
|
||||
}: LargeTableProps) {
|
||||
const highestLevel = getHighestLevel(levels)
|
||||
return (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr className={styles.iconRow}>
|
||||
<th className={styles.verticalTableHeader} />
|
||||
|
||||
{levels.map((level) => {
|
||||
<DesktopHeader
|
||||
levels={levels}
|
||||
activeLevel={activeLevel}
|
||||
Select={Select}
|
||||
/>
|
||||
<tbody className={styles.tbody}>
|
||||
{highestLevel?.benefits
|
||||
.filter((benefit) => benefit.unlocked)
|
||||
.map((benefit, index) => {
|
||||
return (
|
||||
<th key={level.tier} className={styles.iconTh}>
|
||||
<Image
|
||||
height={50}
|
||||
width={100}
|
||||
alt={level.name}
|
||||
src={level.icon}
|
||||
/>
|
||||
</th>
|
||||
<tr key={benefit.name} className={styles.tr}>
|
||||
<th scope={"row"} className={styles.benefitTh}>
|
||||
<BenefitTableHeader
|
||||
name={benefit.name}
|
||||
description={benefit.description}
|
||||
/>
|
||||
</th>
|
||||
{levels.map((level, idx) => {
|
||||
return (
|
||||
<td key={"icon" + level.level + idx} className={styles.td}>
|
||||
<BenefitValue benefit={level.benefits[index]} />
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
<tr>
|
||||
<th />
|
||||
{levels.map((level) => {
|
||||
return (
|
||||
<th key={level.tier} className={styles.summaryTh}>
|
||||
<LevelSummary level={level} />
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{levels[0].benefits.map((benefit, index) => {
|
||||
return (
|
||||
<tr key={benefit.name} className={styles.tr}>
|
||||
<th scope={"row"} className={styles.benefitTh}>
|
||||
<BenefitTableHeader
|
||||
name={benefit.name}
|
||||
description={benefit.description}
|
||||
/>
|
||||
</th>
|
||||
{levels.map((level) => {
|
||||
return (
|
||||
<td key={level.tier} className={styles.td}>
|
||||
<BenefitValue benefit={level.benefits[index]} />
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
@@ -3,44 +3,26 @@
|
||||
border-collapse: collapse;
|
||||
background-color: var(--Scandic-Opacity-White-100);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
color: var(--UI-Grey-100);
|
||||
}
|
||||
|
||||
.iconRow {
|
||||
border-bottom: none;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
.tr {
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.verticalTableHeader {
|
||||
width: 242px;
|
||||
}
|
||||
|
||||
.iconTh {
|
||||
padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x2);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.summaryTh {
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
font-weight: 400;
|
||||
padding: 0 var(--Spacing-x3) var(--Spacing-x2);
|
||||
vertical-align: top;
|
||||
.tr:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.td {
|
||||
border-top: 1px solid var(--Scandic-Beige-20);
|
||||
border-bottom: 1px solid var(--Scandic-Beige-20);
|
||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.benefitTh {
|
||||
border-top: 1px solid var(--Scandic-Beige-20);
|
||||
border-bottom: 1px solid var(--Scandic-Beige-20);
|
||||
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
font-weight: 400;
|
||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
||||
}
|
||||
|
||||
.details[open] .chevron {
|
||||
@@ -58,12 +40,13 @@
|
||||
margin: 0;
|
||||
padding-top: var(--Spacing-x1);
|
||||
text-align: start;
|
||||
padding-right: calc(24px + var(--Spacing-x1));
|
||||
padding-right: calc(var(--Spacing-x3) + var(--Spacing-x1));
|
||||
}
|
||||
|
||||
.chevron {
|
||||
display: flex;
|
||||
align-self: start;
|
||||
color: var(--UI-Grey-80);
|
||||
}
|
||||
|
||||
.summary::-webkit-details-marker {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import Image from "@/components/Image"
|
||||
|
||||
import styles from "./levelSummary.module.css"
|
||||
|
||||
import { LevelSummaryProps } from "@/types/components/loyalty/blocks"
|
||||
|
||||
export default function LevelSummary({ level }: LevelSummaryProps) {
|
||||
export default function LevelSummary({
|
||||
level,
|
||||
showDescription = true,
|
||||
}: LevelSummaryProps) {
|
||||
return (
|
||||
<div className={styles.levelSummary}>
|
||||
<span className={styles.levelRequirements}>{level.requirement}</span>
|
||||
<p className={styles.levelSummaryText}>{level.description}</p>
|
||||
{showDescription && (
|
||||
<p className={styles.levelSummaryText}>{level.description}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,22 +3,27 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x3);
|
||||
padding-bottom: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.levelRequirements {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||
color: var(--Scandic-Peach-80);
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.levelSummaryText {
|
||||
color: var(--Main-Brand-Burgundy);
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
line-height: var(--typography-Body-Regular-lineHeight);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
.levelRequirements {
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1367px) {
|
||||
.levelRequirements {
|
||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Fragment } from "react"
|
||||
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import styles from "./overviewTableTitle.module.css"
|
||||
|
||||
import { OverviewTableTitleProps } from "@/types/components/loyalty/blocks"
|
||||
|
||||
export default function OverviewTableTitle({ texts }: OverviewTableTitleProps) {
|
||||
return (
|
||||
<Title>
|
||||
{texts.map(({ text, highlight }, idx) => (
|
||||
<Fragment key={idx}>
|
||||
<span className={highlight ? styles.highlight : ""}>{text}</span>
|
||||
{idx < texts.length - 1 && " "}
|
||||
</Fragment>
|
||||
))}
|
||||
</Title>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.highlight {
|
||||
color: var(--Base-Text-Primary-Accent);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
|
||||
import styles from "./yourLevel.module.css"
|
||||
|
||||
export default function YourLevel() {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<BiroScript
|
||||
className={styles.script}
|
||||
color="peach80"
|
||||
type="two"
|
||||
textAlign={"center"}
|
||||
>
|
||||
{intl.formatMessage({ id: "Your level" })}
|
||||
</BiroScript>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.script {
|
||||
transform: rotate(-4deg);
|
||||
padding-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 950px) {
|
||||
.script {
|
||||
padding-bottom: var(--Spacing-x1);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p",
|
||||
"description": "Det her er starten på noget smukt. Som New Friend kan du godt glæde dig til at opdage alt det skønne ved Scandic.",
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "Du har været her meget på det sidste! Lad os tage venskabet videre ét behageligt ophold ad gangen.",
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "Vi har lært hinanden bedre at kende, og det gør din oplevelse hos Scandic meget mere personlig.",
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Her er vist grobund for et livslangt venskab, og det betyder, at du får adgang til meget mere med Scandic.",
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "Du har været hos os på mange ophold, så derfor vil vi gerne give dig nogle af vores bedste bonusser.",
|
||||
@@ -381,7 +381,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "Vi er glade for, at du besøger os, uanset om det er høj- eller lavsæson. Derfor får du endnu flere skræddersyede fordele.",
|
||||
@@ -458,7 +458,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "Det bliver simpelthen ikke bedre end det her, når det kommer til de helt eksklusive oplevelser!",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p or 0 nights",
|
||||
"description": "Dies ist der Beginn von etwas Wunderbarem: Als New Friend können Sie sich auf eine Reise voller herrlicher Scandic-Entdeckungen freuen.",
|
||||
@@ -55,7 +55,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": false
|
||||
},
|
||||
{
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "Sie waren in letzter Zeit viel bei uns! Und ehrlich gesagt haben wir das Gefühl, dass wir auf einer Wellenlänge sind – die vielen angenehmen Aufenthalte und lustigen Überraschungen sprechen für sich.",
|
||||
@@ -130,7 +130,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": false
|
||||
},
|
||||
{
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "Jetzt wird es ernst: Wir lernen uns wirklich besser kennen, was bedeutet, dass Ihre Zeit mit Scandic noch viel persönlicher wird.",
|
||||
@@ -206,7 +206,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": false
|
||||
},
|
||||
{
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Ein Hoch auf uns! Unser Verhältnis scheint sich in Richtung Freunde fürs Leben zu entwickeln – was auch bedeutet, dass Sie Zugang zu einer ganzen Menge mehr Scandic bekommen.",
|
||||
@@ -283,7 +283,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": false
|
||||
},
|
||||
{
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "Sie haben uns während zahlreicher Aufenthalte, Happy Hours und Workouts im Fitnessstudio die Treue gehalten – deshalb wollen wir uns mit einigen unserer großartigsten Belohnungen bei Ihnen revanchieren.",
|
||||
@@ -360,7 +360,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": false
|
||||
},
|
||||
{
|
||||
@@ -381,7 +381,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "s spielt keine Rolle, ob Haupt- oder Nebensaison: Sie sind immer für uns da. Genießen Sie noch mehr individuelle Vorteile – genau nach Ihrem Geschmack.",
|
||||
@@ -437,7 +437,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": true
|
||||
},
|
||||
{
|
||||
@@ -458,7 +458,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "Für eine Freundschaft wie diese gibt es im Grunde keine passenden Worte, aber wir versuchen es trotzdem: Denn es könnte gar nichts Besseres geben, wenn es um sehr, sehr exklusive Erlebnisse geht!",
|
||||
@@ -514,7 +514,7 @@
|
||||
},
|
||||
{
|
||||
"name": "48-Stunden-Zimmergarantie",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garanlevelt ein Zimmer, wenn Sie 48 Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
|
||||
"unlocked": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p",
|
||||
"description": "This is the start of something beautiful: as a New Friend, get ready for a journey of delightful Scandic discoveries.",
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "You've been around a lot lately! And honestly, we feel like we're vibing - one enjoyable stay at a time.",
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "It's serious now: we're really getting to know each other which makes your Scandic experiences a lot more personal.",
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Cheers to us! This seems to be going in the direction of friends for life - and that comes with access to a-whole-lotta-more of Scandic.",
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "You've stuck with us through stays, after works and gym sessions - so we'll stick with you through some of our very best rewards.",
|
||||
@@ -381,7 +381,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "It doesn't matter if it's peak or off season, you're always there for us. Enjoy even more tailored perks - just the way you like them.",
|
||||
@@ -458,7 +458,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "There are no words for a bond like this, but here's a few anyway: It simply doesn't get any better when it comes to very, very exclusive experiences!",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p",
|
||||
"description": "Olemme uuden ja upean kynnyksellä: New Friend -ystävänä pääset nauttimaan kaikesta ihanasta, mitä Scandic tarjoaa.",
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "Kiva, että olet vieraillut meillä, ja tuntuu, että ystävyytemme on hyvässä nosteessa. Tästä on hyvä jatkaa, yksi yöpyminen ja iloinen yllätys kerrallaan!",
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "Onpa kiva, että olet vieraillut meillä näin usein! Nyt etusi vain paranevat, sillä olemmehan jo enemmän kuin hyvän päivän tuttuja.",
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Kippis syventyvälle ystävyydellemme. Nyt pääset nauttimaan liudasta uusia etuja.",
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "Kiva, että olemme saaneet jakaa paljon yhteisiä hetkiä. Olet tosiaan nimesi arvoinen Loyal Friend! Haluamme panostaa ystävyyteemme myös jatkossa ja annammekin sinulle kasan uusia, ihania etuja.",
|
||||
@@ -381,7 +381,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "Onpa ollut ihana nähdä sinua näin paljon viime aikoina. Tosiystävän tapaan haluamme palkita sinua entistä yksilöllisemmillä eduilla.",
|
||||
@@ -458,7 +458,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "Ystävyytemme on vailla vertaa. Koska sanat eivät riitä kiittämään ystävyydestämme, pääset nyt käsiksi kaikkein eksklusiivisimpiin elämyksiin.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p",
|
||||
"description": "Dette er starten på noe vakkert: Som en New Friend, gjør deg klar for en reise fylt av fine Scandic-opplevelser.",
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "Du har vært her mye i det siste! Og ærlig talt føler vi at vi er på bølgelengde – ett behagelig opphold og én morsom overraskelse om gangen.",
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "Nå er det seriøst: Vi begynner virkelig å bli kjent med hverandre, noe som gjør Scandic-opplevelsen din mye mer personlig.",
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Hurra for oss! Dette ser ut til å gå i retning av venner for livet – og det kommer med tilgang til mye mer av Scandic.",
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "Du har vært lojal mot oss gjennom opphold, happy hours og treningsøkter – så vi er der for deg med noen av de aller beste fordelene våre.",
|
||||
@@ -381,7 +381,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "Det spiller ingen rolle om det er høysesong eller lavsesong, du er alltid der for oss. Nyt enda flere skreddersydde fordeler – akkurat slik du liker dem.",
|
||||
@@ -458,7 +458,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "Det finnes ikke ord for et bånd som dette, men vi gjør et forsøk allikevel: Det blir bare ikke bedre når det gjelder svært eksklusive opplevelser!",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"levels": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": 1,
|
||||
"name": "New Friend",
|
||||
"requirement": "0p or 0 nights",
|
||||
"description": "Detta är början på något speciellt, vi kan känna det. Som New Friend bjuder dina vistelser på nya upptäckter.",
|
||||
@@ -76,7 +76,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 2,
|
||||
"level": 2,
|
||||
"name": "Good Friend",
|
||||
"requirement": "5 000p",
|
||||
"description": "Vi har setts mycket på senaste tiden och det känns typ som att vi har nåt speciellt på gång – en vistelse och rolig överraskning i taget.",
|
||||
@@ -151,7 +151,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 3,
|
||||
"level": 3,
|
||||
"name": "Close Friend",
|
||||
"requirement": "10 000p",
|
||||
"description": "Det börjar bli seriöst: nu när vi lärt känna varann bättre blir också din upplevelse lite personligare.",
|
||||
@@ -227,7 +227,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 4,
|
||||
"level": 4,
|
||||
"name": "Dear Friend",
|
||||
"requirement": "25 000p",
|
||||
"description": "Är det bara en känsla eller ligger det vänner för livet-vibbar i luften? Skål för oss och för att du nu får ännu mer av Scandic.",
|
||||
@@ -304,7 +304,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 5,
|
||||
"level": 5,
|
||||
"name": "Loyal Friend",
|
||||
"requirement": "50 000p",
|
||||
"description": "Du har varit hos oss under otaliga resor och fler nätter än vi kan räkna, så därför vill vi ge dig några av våra allra bästa förmåner.",
|
||||
@@ -380,7 +380,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 6,
|
||||
"level": 6,
|
||||
"name": "True Friend",
|
||||
"requirement": "100 000p",
|
||||
"description": "Du är alltid där för oss, oavsett om det är hög- eller lågsäsong. Därför vill vi ge dig fler förmåner – precis som du gillar.",
|
||||
@@ -457,7 +457,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": 7,
|
||||
"level": 7,
|
||||
"name": "Best Friend",
|
||||
"requirement": "400 000p",
|
||||
"description": "Det är svårt att sätta ord på ett så här starkt band, men här kommer några ändå: det går inte att få bättre upplevelser än så här!",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useParams } from "next/navigation"
|
||||
import { useReducer } from "react"
|
||||
import { type Key } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
@@ -21,16 +19,21 @@ import SV from "./data/SV.json"
|
||||
import BenefitList from "./BenefitList"
|
||||
import LargeTable from "./LargeTable"
|
||||
import LevelSummary from "./LevelSummary"
|
||||
import YourLevel from "./YourLevelScript"
|
||||
|
||||
import styles from "./overviewTable.module.css"
|
||||
|
||||
import type { Key } from "react-aria-components"
|
||||
|
||||
import {
|
||||
ComparisonLevel,
|
||||
DesktopSelectColumns,
|
||||
MobileColumnHeaderProps,
|
||||
overviewTableActionsEnum,
|
||||
OverviewTableProps,
|
||||
OverviewTableReducerAction,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
import { User } from "@/types/user"
|
||||
import type { User } from "@/types/user"
|
||||
|
||||
const levelsTranslations = {
|
||||
[Lang.en]: EN,
|
||||
@@ -40,9 +43,10 @@ const levelsTranslations = {
|
||||
[Lang.fi]: FI,
|
||||
[Lang.de]: DE,
|
||||
}
|
||||
function getTranslatedLevelByTier(tier: membershipLevels, lang: Lang) {
|
||||
|
||||
function getTranslatedLevel(membershipLevel: membershipLevels, lang: Lang) {
|
||||
return levelsTranslations[lang].levels.find(
|
||||
(level) => level.tier === tier
|
||||
(level) => level.level === membershipLevel
|
||||
) as ComparisonLevel
|
||||
}
|
||||
|
||||
@@ -50,40 +54,40 @@ function getInitialState({ user, lang }: { user?: User; lang: Lang }) {
|
||||
const membership = user?.memberships ? getMembership(user.memberships) : null
|
||||
if (!membership?.membershipLevel) {
|
||||
return {
|
||||
selectedLevelAMobile: getTranslatedLevelByTier(1, lang),
|
||||
selectedLevelBMobile: getTranslatedLevelByTier(2, lang),
|
||||
selectedLevelADesktop: getTranslatedLevelByTier(1, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevelByTier(2, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevelByTier(3, lang),
|
||||
selectedLevelAMobile: getTranslatedLevel(1, lang),
|
||||
selectedLevelBMobile: getTranslatedLevel(2, lang),
|
||||
selectedLevelADesktop: getTranslatedLevel(1, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevel(2, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevel(3, lang),
|
||||
}
|
||||
}
|
||||
if (!membership.membershipLevel) return null
|
||||
const tier = membershipLevels[membership.membershipLevel]
|
||||
const level = membershipLevels[membership.membershipLevel]
|
||||
|
||||
switch (tier) {
|
||||
switch (level) {
|
||||
case 6:
|
||||
return {
|
||||
selectedLevelAMobile: getTranslatedLevelByTier(6, lang),
|
||||
selectedLevelBMobile: getTranslatedLevelByTier(7, lang),
|
||||
selectedLevelADesktop: getTranslatedLevelByTier(5, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevelByTier(6, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevelByTier(7, lang),
|
||||
selectedLevelAMobile: getTranslatedLevel(6, lang),
|
||||
selectedLevelBMobile: getTranslatedLevel(7, lang),
|
||||
selectedLevelADesktop: getTranslatedLevel(5, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevel(6, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevel(7, lang),
|
||||
}
|
||||
case 7:
|
||||
return {
|
||||
selectedLevelAMobile: getTranslatedLevelByTier(6, lang),
|
||||
selectedLevelBMobile: getTranslatedLevelByTier(7, lang),
|
||||
selectedLevelADesktop: getTranslatedLevelByTier(6, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevelByTier(7, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevelByTier(1, lang),
|
||||
selectedLevelAMobile: getTranslatedLevel(6, lang),
|
||||
selectedLevelBMobile: getTranslatedLevel(7, lang),
|
||||
selectedLevelADesktop: getTranslatedLevel(6, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevel(7, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevel(1, lang),
|
||||
}
|
||||
default:
|
||||
return {
|
||||
selectedLevelAMobile: getTranslatedLevelByTier(tier, lang),
|
||||
selectedLevelBMobile: getTranslatedLevelByTier(tier + 1, lang),
|
||||
selectedLevelADesktop: getTranslatedLevelByTier(tier, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevelByTier(tier + 1, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevelByTier(tier + 2, lang),
|
||||
selectedLevelAMobile: getTranslatedLevel(level, lang),
|
||||
selectedLevelBMobile: getTranslatedLevel(level + 1, lang),
|
||||
selectedLevelADesktop: getTranslatedLevel(level, lang),
|
||||
selectedLevelBDesktop: getTranslatedLevel(level + 1, lang),
|
||||
selectedLevelCDesktop: getTranslatedLevel(level + 2, lang),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +138,7 @@ export default function OverviewTable({ user }: OverviewTableProps) {
|
||||
return (key: Key) => {
|
||||
if (typeof key === "number") {
|
||||
dispatch({
|
||||
payload: getTranslatedLevelByTier(key, lang),
|
||||
payload: getTranslatedLevel(key, lang),
|
||||
type: actionType,
|
||||
})
|
||||
}
|
||||
@@ -142,68 +146,106 @@ export default function OverviewTable({ user }: OverviewTableProps) {
|
||||
}
|
||||
|
||||
const levelOptions = levelsData.levels.map((level) => ({
|
||||
label: level.name,
|
||||
value: level.tier,
|
||||
label: [level.level, level.name].join(" "),
|
||||
value: level.level,
|
||||
}))
|
||||
|
||||
const activeMembership = user?.memberships
|
||||
? getMembership(user.memberships)
|
||||
: null
|
||||
|
||||
let activeMembershipLevel: membershipLevels | null = null
|
||||
if (activeMembership?.membershipLevel) {
|
||||
activeMembershipLevel = membershipLevels[activeMembership?.membershipLevel]
|
||||
}
|
||||
|
||||
function MobileColumnHeader({ column }: MobileColumnHeaderProps) {
|
||||
let selectedLevelMobile: ComparisonLevel
|
||||
let actionEnumMobile: overviewTableActionsEnum
|
||||
switch (column) {
|
||||
case "A":
|
||||
selectedLevelMobile = selectionState.selectedLevelAMobile
|
||||
actionEnumMobile = overviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE
|
||||
break
|
||||
case "B":
|
||||
selectedLevelMobile = selectionState.selectedLevelBMobile
|
||||
actionEnumMobile = overviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE
|
||||
break
|
||||
default:
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className={styles.columnHeader}>
|
||||
<div className={styles.icon}>
|
||||
{activeMembershipLevel === selectedLevelMobile.level ? (
|
||||
<YourLevel />
|
||||
) : null}
|
||||
<Image
|
||||
src={selectedLevelMobile.icon}
|
||||
alt={selectedLevelMobile.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
</div>
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) => level.level === selectedLevelMobile.level
|
||||
) as ComparisonLevel
|
||||
}
|
||||
showDescription={false}
|
||||
/>
|
||||
<Select
|
||||
aria-label={intl.formatMessage({ id: "Level" })}
|
||||
name={`benefit` + column}
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectedLevelMobile.level}
|
||||
onSelect={handleSelectChange(actionEnumMobile)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SelectDesktop({ column }: DesktopSelectColumns) {
|
||||
let selectedLevelDesktop: ComparisonLevel
|
||||
let actionEnumDesktop: overviewTableActionsEnum
|
||||
switch (column) {
|
||||
case "A":
|
||||
selectedLevelDesktop = selectionState.selectedLevelADesktop
|
||||
actionEnumDesktop =
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP
|
||||
break
|
||||
case "B":
|
||||
selectedLevelDesktop = selectionState.selectedLevelBDesktop
|
||||
actionEnumDesktop =
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP
|
||||
break
|
||||
case "C":
|
||||
selectedLevelDesktop = selectionState.selectedLevelCDesktop
|
||||
actionEnumDesktop =
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP
|
||||
break
|
||||
default:
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
aria-label={intl.formatMessage({ id: "Level" })}
|
||||
name={`benefit` + column}
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectedLevelDesktop.level}
|
||||
onSelect={handleSelectChange(actionEnumDesktop)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.mobileColumns}>
|
||||
<div className={styles.columnHeaderContainer}>
|
||||
<div className={styles.columnHeader}>
|
||||
<Select
|
||||
name="benefitA"
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectionState.selectedLevelAMobile.tier}
|
||||
onSelect={handleSelectChange(
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE
|
||||
)}
|
||||
/>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
src={selectionState.selectedLevelAMobile.icon}
|
||||
alt={selectionState.selectedLevelAMobile.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) =>
|
||||
level.tier === selectionState.selectedLevelAMobile.tier
|
||||
) as ComparisonLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.columnHeader}>
|
||||
<Select
|
||||
name="benefitB"
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectionState.selectedLevelBMobile.tier}
|
||||
onSelect={handleSelectChange(
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE
|
||||
)}
|
||||
/>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
src={selectionState.selectedLevelBMobile.icon}
|
||||
alt={selectionState.selectedLevelBMobile.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) =>
|
||||
level.tier === selectionState.selectedLevelBMobile.tier
|
||||
) as ComparisonLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<MobileColumnHeader column={"A"} />
|
||||
<MobileColumnHeader column={"B"} />
|
||||
</div>
|
||||
<BenefitList
|
||||
levels={[
|
||||
@@ -213,100 +255,22 @@ export default function OverviewTable({ user }: OverviewTableProps) {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.columns}>
|
||||
<div className={styles.columnHeaderContainer}>
|
||||
<div className={styles.columnHeader}>
|
||||
<Select
|
||||
name="benefitA"
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectionState.selectedLevelADesktop.tier}
|
||||
onSelect={handleSelectChange(
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP
|
||||
)}
|
||||
/>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
src={selectionState.selectedLevelADesktop.icon}
|
||||
alt={selectionState.selectedLevelADesktop.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) =>
|
||||
level.tier === selectionState.selectedLevelADesktop.tier
|
||||
) as ComparisonLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.columnHeader}>
|
||||
<Select
|
||||
name="benefitB"
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectionState.selectedLevelBDesktop.tier}
|
||||
onSelect={handleSelectChange(
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP
|
||||
)}
|
||||
/>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
src={selectionState.selectedLevelBDesktop.icon}
|
||||
alt={selectionState.selectedLevelBDesktop.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) =>
|
||||
level.tier === selectionState.selectedLevelBDesktop.tier
|
||||
) as ComparisonLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.columnHeader}>
|
||||
<Select
|
||||
name="benefitC"
|
||||
label={intl.formatMessage({ id: "Level" })}
|
||||
items={levelOptions}
|
||||
value={selectionState.selectedLevelCDesktop.tier}
|
||||
onSelect={handleSelectChange(
|
||||
overviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP
|
||||
)}
|
||||
/>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
src={selectionState.selectedLevelCDesktop.icon}
|
||||
alt={selectionState.selectedLevelCDesktop.name}
|
||||
height={50}
|
||||
width={100}
|
||||
/>
|
||||
|
||||
<LevelSummary
|
||||
level={
|
||||
levelsData.levels.find(
|
||||
(level) =>
|
||||
level.tier === selectionState.selectedLevelCDesktop.tier
|
||||
) as ComparisonLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<BenefitList
|
||||
<LargeTable
|
||||
levels={[
|
||||
selectionState.selectedLevelADesktop,
|
||||
selectionState.selectedLevelBDesktop,
|
||||
selectionState.selectedLevelCDesktop,
|
||||
]}
|
||||
activeLevel={activeMembershipLevel}
|
||||
Select={SelectDesktop}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.largeTableContainer}>
|
||||
{/* Remove `as` once we have real data */}
|
||||
<LargeTable levels={levelsData.levels as ComparisonLevel[]} />
|
||||
<LargeTable
|
||||
levels={levelsData.levels as ComparisonLevel[]}
|
||||
activeLevel={activeMembershipLevel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,15 +11,17 @@
|
||||
display: none;
|
||||
position: relative;
|
||||
background-color: var(--Scandic-Opacity-White-100);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.mobileColumns {
|
||||
background-color: var(--Scandic-Opacity-White-100);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
margin: 0 calc(var(--Spacing-x2) * -1);
|
||||
padding-top: var(--Spacing-x2);
|
||||
margin: 0 calc(var(--Spacing-x2) * -1) calc(var(--Spacing-x9) * -1)
|
||||
calc(var(--Spacing-x2) * -1);
|
||||
padding-bottom: var(--Spacing-x9);
|
||||
position: relative;
|
||||
background-color: var(--Scandic-Opacity-White-100);
|
||||
}
|
||||
|
||||
.columnHeaderContainer {
|
||||
@@ -33,7 +35,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x4) var(--Spacing-x2);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@@ -71,9 +74,7 @@
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
margin: 0 calc(var(--Spacing-x2) * -1);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
.container {
|
||||
/* These negative margins are needed for horizontally scrollable lists of cards */
|
||||
margin-right: calc(0px - var(--Spacing-x2));
|
||||
/* These negative margins are needed for getting the right background color for mobile loyalty table overview */
|
||||
margin: 0 calc(0px - var(--Spacing-x2)) calc(0px - var(--Spacing-x9))
|
||||
calc(0px - var(--Spacing-x2));
|
||||
overflow-x: hidden;
|
||||
padding-right: var(--Spacing-x2);
|
||||
padding: 0 var(--Spacing-x2) var(--Spacing-x9) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
padding-bottom: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.tableTitle {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.preamble {
|
||||
@@ -22,4 +28,8 @@
|
||||
margin-right: var(--Spacing-x0);
|
||||
margin-left: var(--Spacing-x0);
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { auth } from "@/auth"
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import Header from "@/components/Section/Header"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import OverviewTableTitle from "./OverviewTable/Title"
|
||||
import HowItWorks from "./HowItWorks"
|
||||
import LoyaltyLevels from "./LoyaltyLevels"
|
||||
import OverviewTable from "./OverviewTable"
|
||||
@@ -36,35 +36,6 @@ async function DynamicComponentBlock({ component }: DynamicComponentProps) {
|
||||
}
|
||||
}
|
||||
|
||||
// These should ultimately be fetched from Contentstack
|
||||
const titleTranslations = {
|
||||
[Lang.en]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
// TODO: Add translations for the following languages
|
||||
[Lang.da]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
[Lang.no]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
[Lang.sv]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
[Lang.fi]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
[Lang.de]: [
|
||||
{ text: "7 delightful levels", highlight: true },
|
||||
{ text: "of friendship", highlight: false },
|
||||
],
|
||||
}
|
||||
|
||||
export default function DynamicContent({
|
||||
dynamicContent,
|
||||
firstItem,
|
||||
@@ -77,12 +48,11 @@ export default function DynamicContent({
|
||||
|
||||
const isOverviewTable =
|
||||
dynamicContent.component === LoyaltyComponentEnum.overview_table
|
||||
|
||||
return (
|
||||
<SectionContainer className={styles.container}>
|
||||
{isOverviewTable ? (
|
||||
<div className={styles.header}>
|
||||
<OverviewTableTitle texts={titleTranslations[Lang.en]} />
|
||||
<Title className={styles.tableTitle}> {dynamicContent.title}</Title>
|
||||
<Subtitle>{dynamicContent.subtitle}</Subtitle>
|
||||
</div>
|
||||
) : displayHeader ? (
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
color: var(--Scandic-Brand-Pale-Peach);
|
||||
}
|
||||
|
||||
.peach80 {
|
||||
color: var(--Scandic-Peach-80);
|
||||
}
|
||||
|
||||
.plosa {
|
||||
color: var(--Theme-Primary-Light-On-Surface-Accent);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const config = {
|
||||
black: styles.black,
|
||||
burgundy: styles.burgundy,
|
||||
pale: styles.pale,
|
||||
peach80: styles.peach80,
|
||||
primaryLightOnSurfaceAccent: styles.plosa,
|
||||
},
|
||||
textAlign: {
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "Du har ingen tidligere ophold.",
|
||||
"You have no upcoming stays.": "Du har ingen kommende ophold.",
|
||||
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
||||
"Your level": "Dit niveau",
|
||||
"Zip code": "Postnummer"
|
||||
}
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "Sie haben keine vorherigen Aufenthalte.",
|
||||
"You have no upcoming stays.": "Sie haben keine bevorstehenden Aufenthalte.",
|
||||
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
||||
"Your level": "Dein ebene",
|
||||
"Zip code": "PLZ"
|
||||
}
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "You have no previous stays.",
|
||||
"You have no upcoming stays.": "You have no upcoming stays.",
|
||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||
"Your level": "Your level",
|
||||
"Zip code": "Zip code"
|
||||
}
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "Sinulla ei ole aiempaa oleskelua.",
|
||||
"You have no upcoming stays.": "Sinulla ei ole tulevia oleskeluja.",
|
||||
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
|
||||
"Your level": "Tasosi",
|
||||
"Zip code": "Postinumero"
|
||||
}
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "Du har ingen tidligere opphold.",
|
||||
"You have no upcoming stays.": "Du har ingen kommende opphold.",
|
||||
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
||||
"Your level": "Ditt nivå",
|
||||
"Zip code": "Post kode"
|
||||
}
|
||||
|
||||
@@ -89,5 +89,6 @@
|
||||
"You have no previous stays.": "Du har inga tidigare vistelser.",
|
||||
"You have no upcoming stays.": "Du har inga kommande vistelser.",
|
||||
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
||||
"Your level": "Din nivå",
|
||||
"Zip code": "Postnummer"
|
||||
}
|
||||
|
||||
@@ -30,13 +30,6 @@ export type Content = { content: RteBlockContent["content"]["content"] }
|
||||
|
||||
type Benefit = { title: string }
|
||||
|
||||
type OverviewTableTitleTranslation = {
|
||||
text: string
|
||||
highlight: boolean
|
||||
}
|
||||
|
||||
export type OverviewTableTitleProps = { texts: OverviewTableTitleTranslation[] }
|
||||
|
||||
export type OverviewTableProps = { user: User | null }
|
||||
|
||||
export type Level = {
|
||||
@@ -54,7 +47,7 @@ export type LevelCardProps = {
|
||||
}
|
||||
|
||||
export type ComparisonLevel = {
|
||||
tier: membershipLevels
|
||||
level: membershipLevels
|
||||
name: string
|
||||
description: string
|
||||
requirement: string
|
||||
@@ -72,6 +65,7 @@ export type ComparisonLevel = {
|
||||
|
||||
export type LevelSummaryProps = {
|
||||
level: ComparisonLevel
|
||||
showDescription?: boolean
|
||||
}
|
||||
|
||||
export type BenefitCardProps = {
|
||||
@@ -94,8 +88,18 @@ export type BenefitListProps = {
|
||||
levels: ComparisonLevel[]
|
||||
}
|
||||
|
||||
export type MobileColumnHeaderProps = {
|
||||
column: "A" | "B"
|
||||
}
|
||||
|
||||
export type DesktopSelectColumns = {
|
||||
column: MobileColumnHeaderProps["column"] | "C"
|
||||
}
|
||||
|
||||
export type LargeTableProps = {
|
||||
levels: ComparisonLevel[]
|
||||
activeLevel: membershipLevels | null
|
||||
Select?: (column: DesktopSelectColumns) => JSX.Element | null
|
||||
}
|
||||
|
||||
export type BenefitTableHeaderProps = {
|
||||
|
||||
13
utils/loyaltyTable.ts
Normal file
13
utils/loyaltyTable.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ComparisonLevel } from "@/types/components/loyalty/blocks"
|
||||
|
||||
export function getHighestLevel(levels: ComparisonLevel[]) {
|
||||
return levels.reduce(
|
||||
(acc: ComparisonLevel | null, level: ComparisonLevel) => {
|
||||
if (!acc) {
|
||||
return level
|
||||
}
|
||||
return level.level > acc.level ? level : acc
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user