From 118651103867cb9fb9d57b0712cf7ebe3a8d9722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20Landstr=C3=B6m?= Date: Fri, 28 Jun 2024 10:13:32 +0200 Subject: [PATCH] feat: update loyalty overview table ui --- .../DynamicContent/LoyaltyLevels/index.tsx | 2 +- .../LoyaltyLevels/loyaltyLevels.module.css | 2 +- .../BenefitCard/benefitCard.module.css | 13 +- .../BenefitList/benefitList.module.css | 7 +- .../OverviewTable/BenefitList/index.tsx | 20 +- .../DesktopHeader/desktopHeader.module.css | 28 ++ .../LargeTable/DesktopHeader/index.tsx | 63 ++++ .../OverviewTable/LargeTable/index.tsx | 80 ++--- .../LargeTable/largeTable.module.css | 33 +- .../OverviewTable/LevelSummary/index.tsx | 11 +- .../LevelSummary/levelSummary.module.css | 9 +- .../OverviewTable/Title/index.tsx | 20 -- .../Title/overviewTableTitle.module.css | 3 - .../OverviewTable/YourLevelScript/index.tsx | 19 ++ .../YourLevelScript/yourLevel.module.css | 10 + .../DynamicContent/OverviewTable/data/DA.json | 14 +- .../DynamicContent/OverviewTable/data/DE.json | 28 +- .../DynamicContent/OverviewTable/data/EN.json | 14 +- .../DynamicContent/OverviewTable/data/FI.json | 14 +- .../DynamicContent/OverviewTable/data/NO.json | 14 +- .../DynamicContent/OverviewTable/data/SV.json | 14 +- .../DynamicContent/OverviewTable/index.tsx | 302 ++++++++---------- .../OverviewTable/overviewTable.module.css | 15 +- .../DynamicContent/dynamicContent.module.css | 16 +- .../Loyalty/Blocks/DynamicContent/index.tsx | 34 +- .../Text/BiroScript/biroScript.module.css | 4 + .../Text/BiroScript/variants.ts | 1 + i18n/dictionaries/da.json | 1 + i18n/dictionaries/de.json | 1 + i18n/dictionaries/en.json | 1 + i18n/dictionaries/fi.json | 1 + i18n/dictionaries/no.json | 1 + i18n/dictionaries/sv.json | 1 + types/components/loyalty/blocks.ts | 20 +- utils/loyaltyTable.ts | 13 + 35 files changed, 433 insertions(+), 396 deletions(-) create mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/desktopHeader.module.css create mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx delete mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/Title/index.tsx delete mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/Title/overviewTableTitle.module.css create mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/YourLevelScript/index.tsx create mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/YourLevelScript/yourLevel.module.css create mode 100644 utils/loyaltyTable.ts diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx index 4d4292bb6..d5be0f1e9 100644 --- a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx @@ -85,7 +85,7 @@ function LevelCard({ formatMessage, lang, level }: LevelCardProps) { } return (
- + <Title className={styles.levelHeading} level="h4"> {level.level} {Level ? : null} diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css index 1d23f6418..9cb5c7cc8 100644 --- a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css @@ -30,7 +30,7 @@ padding: var(--Spacing-x5) var(--Spacing-x1); } -.tierHeading { +.levelHeading { color: var(--Scandic-Peach-70); } diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitCard/benefitCard.module.css b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitCard/benefitCard.module.css index 2079571a5..ae643de29 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitCard/benefitCard.module.css +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitCard/benefitCard.module.css @@ -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 { diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/benefitList.module.css b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/benefitList.module.css index 1b008ba50..dba0dd085 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/benefitList.module.css +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/benefitList.module.css @@ -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) { diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx index d60dfcf9b..a1a8ec7be 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/BenefitList/index.tsx @@ -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]) diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/desktopHeader.module.css b/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/desktopHeader.module.css new file mode 100644 index 000000000..32ed2be4d --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/desktopHeader.module.css @@ -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); +} diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx new file mode 100644 index 000000000..e4eb196fb --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/LargeTable/DesktopHeader/index.tsx @@ -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 ( + + + + {levels.map((level, idx) => { + return ( + + {activeLevel === level.level ? : null} + {level.name} + + ) + })} + + + + {levels.map((level, idx) => { + return ( + + + + ) + })} + + {Select && ( + + + {["A", "B", "C"].map((column, idx) => { + return ( + + + + ) + } + + 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 ( + - {selectionState.selectedLevelAMobile.name} - - - level.tier === selectionState.selectedLevelAMobile.tier - ) as ComparisonLevel - } - /> - -
- - {selectionState.selectedLevelADesktop.name} - - - level.tier === selectionState.selectedLevelADesktop.tier - ) as ComparisonLevel - } - /> -
-
- - {selectionState.selectedLevelCDesktop.name} - - - level.tier === selectionState.selectedLevelCDesktop.tier - ) as ComparisonLevel - } - /> -
- -
{/* Remove `as` once we have real data */} - +
) diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/overviewTable.module.css b/components/Loyalty/Blocks/DynamicContent/OverviewTable/overviewTable.module.css index 159c32da0..3262b4d07 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/overviewTable.module.css +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/overviewTable.module.css @@ -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; } } diff --git a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css index 4eb79cdcd..0232da65d 100644 --- a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css +++ b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css @@ -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; + } } diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx index 4f94c59b9..e5566519a 100644 --- a/components/Loyalty/Blocks/DynamicContent/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -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 ( {isOverviewTable ? (
- + {dynamicContent.title} {dynamicContent.subtitle}
) : displayHeader ? ( diff --git a/components/TempDesignSystem/Text/BiroScript/biroScript.module.css b/components/TempDesignSystem/Text/BiroScript/biroScript.module.css index abb5f3e67..1c7cc7774 100644 --- a/components/TempDesignSystem/Text/BiroScript/biroScript.module.css +++ b/components/TempDesignSystem/Text/BiroScript/biroScript.module.css @@ -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); } diff --git a/components/TempDesignSystem/Text/BiroScript/variants.ts b/components/TempDesignSystem/Text/BiroScript/variants.ts index 2385e09c7..df1bec379 100644 --- a/components/TempDesignSystem/Text/BiroScript/variants.ts +++ b/components/TempDesignSystem/Text/BiroScript/variants.ts @@ -8,6 +8,7 @@ const config = { black: styles.black, burgundy: styles.burgundy, pale: styles.pale, + peach80: styles.peach80, primaryLightOnSurfaceAccent: styles.plosa, }, textAlign: { diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index c31dd8aea..76ead89a8 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -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" } diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index 6044b47a6..218a4c630 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -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" } diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index e0cfd7b2b..dd7e4e95e 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -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" } diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index 77d843365..a54a7c7c1 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -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" } diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index ce38e1561..bc54fb5f1 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -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" } diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 4a7aa614e..e9d5d9eec 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -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" } diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts index 90a329da3..c06475cb1 100644 --- a/types/components/loyalty/blocks.ts +++ b/types/components/loyalty/blocks.ts @@ -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 = { diff --git a/utils/loyaltyTable.ts b/utils/loyaltyTable.ts new file mode 100644 index 000000000..f8c4fed04 --- /dev/null +++ b/utils/loyaltyTable.ts @@ -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 + ) +}