Merge branch 'develop' into feat/build-initial-hotel-page-component

This commit is contained in:
Chuma McPhoy
2024-06-28 13:11:50 +02:00
35 changed files with 433 additions and 396 deletions

View File

@@ -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}

View File

@@ -30,7 +30,7 @@
padding: var(--Spacing-x5) var(--Spacing-x1);
}
.tierHeading {
.levelHeading {
color: var(--Scandic-Peach-70);
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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])

View File

@@ -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);
}

View File

@@ -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>
)
}

View File

@@ -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>
)

View File

@@ -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 {

View File

@@ -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>
)
}

View File

@@ -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);

View File

@@ -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>
)
}

View File

@@ -1,3 +0,0 @@
.highlight {
color: var(--Base-Text-Primary-Accent);
}

View File

@@ -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>
)
}

View File

@@ -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);
}
}

View File

@@ -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!",

View File

@@ -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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden 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 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": true
},
{

View File

@@ -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!",

View File

@@ -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.",

View File

@@ -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!",

View File

@@ -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!",

View File

@@ -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>
)

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 ? (

View File

@@ -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);
}

View File

@@ -8,6 +8,7 @@ const config = {
black: styles.black,
burgundy: styles.burgundy,
pale: styles.pale,
peach80: styles.peach80,
primaryLightOnSurfaceAccent: styles.plosa,
},
textAlign: {

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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
View 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
)
}