From eacca338476c170730ce1400059d17926692f8e1 Mon Sep 17 00:00:00 2001 From: Anton Gunnarsson Date: Wed, 22 Jan 2025 08:15:31 +0000 Subject: [PATCH] Merged in feature/tier-matching-component (pull request #1175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAS Tier matching comparison block (SW-921) Approved-by: Joakim Jäderberg Approved-by: Matilda Landström --- components/Blocks/index.tsx | 9 +- components/Icons/CompareArrows.tsx | 27 +++ components/Icons/index.tsx | 1 + components/SasTierComparison/index.tsx | 163 ++++++++++++++++++ .../sas-tier-comparison.module.css | 72 ++++++++ .../Blocks/SasTierComparison.graphql | 45 +++++ .../Query/ContentPage/ContentPage.graphql | 10 ++ .../contentstack/contentPage/output.ts | 9 + .../routers/contentstack/contentPage/query.ts | 12 ++ .../schemas/blocks/sasTierComparison.ts | 64 +++++++ types/enums/blocks.ts | 1 + types/enums/contentPage.ts | 1 + types/trpc/routers/contentstack/blocks.ts | 3 + 13 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 components/Icons/CompareArrows.tsx create mode 100644 components/SasTierComparison/index.tsx create mode 100644 components/SasTierComparison/sas-tier-comparison.module.css create mode 100644 lib/graphql/Fragments/Blocks/SasTierComparison.graphql create mode 100644 server/routers/contentstack/schemas/blocks/sasTierComparison.ts diff --git a/components/Blocks/index.tsx b/components/Blocks/index.tsx index 0382fbfbc..a67a3880e 100644 --- a/components/Blocks/index.tsx +++ b/components/Blocks/index.tsx @@ -4,6 +4,7 @@ import ShortcutsList from "@/components/Blocks/ShortcutsList" import TextCols from "@/components/Blocks/TextCols" import UspGrid from "@/components/Blocks/UspGrid" import JsonToHtml from "@/components/JsonToHtml" +import { SasTierComparison } from "@/components/SasTierComparison" import AccordionSection from "./Accordion" import HotelListing from "./HotelListing" @@ -90,7 +91,13 @@ export default function Blocks({ blocks }: BlocksProps) { ) case BlocksEnums.block.UspGrid: return - + case BlocksEnums.block.SasTierComparison: + return ( + + ) default: return null } diff --git a/components/Icons/CompareArrows.tsx b/components/Icons/CompareArrows.tsx new file mode 100644 index 000000000..989d5413f --- /dev/null +++ b/components/Icons/CompareArrows.tsx @@ -0,0 +1,27 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function CompareArrowsIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + ) +} diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index 1c4a1387d..5a7045654 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -51,6 +51,7 @@ export { default as CoffeeIcon } from "./Coffee" export { default as CoffeeAltIcon } from "./CoffeeAlt" export { default as CoffeeMakerIcon } from "./CoffeeMaker" export { default as CoinIcon } from "./Coin" +export { default as CompareArrowsIcon } from "./CompareArrows" export { default as ConciergeIcon } from "./Concierge" export { default as ContractIcon } from "./Contract" export { default as ConvenienceStore24hIcon } from "./ConvenienceStore24h" diff --git a/components/SasTierComparison/index.tsx b/components/SasTierComparison/index.tsx new file mode 100644 index 000000000..d08d978d3 --- /dev/null +++ b/components/SasTierComparison/index.tsx @@ -0,0 +1,163 @@ +"use client" +import { type Key, useState } from "react" + +import Link from "@/components/TempDesignSystem/Link" +import Title from "@/components/TempDesignSystem/Text/Title" + +import { ArrowRightIcon, CompareArrowsIcon } from "../Icons" +import SectionContainer from "../Section/Container" +import SectionHeader from "../Section/Header" +import Button from "../TempDesignSystem/Button" +import Select from "../TempDesignSystem/Select" + +import styles from "./sas-tier-comparison.module.css" + +import type { SasTierComparison } from "@/types/trpc/routers/contentstack/blocks" + +type SasTierComparisonContent = SasTierComparison["sas_tier_comparison"] + +type TierComparisonProps = { + content: SasTierComparisonContent + firstItem: boolean +} + +const scandicSasTierMappings: [string[], string[]][] = [ + [["L1", "L2", "L3"], ["Basic"]], + [["L4"], ["Silver"]], + [["L5"], ["Gold"]], + [ + ["L6", "L7"], + ["Diamond", "Pandion"], + ], +] + +export function SasTierComparison({ content, firstItem }: TierComparisonProps) { + const comparisonContent = content.sasTierComparison + + const scandic = comparisonContent?.scandic_friends + const sas = comparisonContent?.sas_eb + + const [activeScandicTierCode, setActiveScandicTierCode] = useState( + scandic?.tiers.at(0)?.tier_code ?? "" + ) + const [activeSasTierCode, setActiveSasTierCode] = useState( + sas?.tiers.at(0)?.tier_code ?? "" + ) + + if (!scandic || !sas) return null + + const onChangeScandic = (k: Key) => { + const key = k.toString() + setActiveScandicTierCode(k.toString()) + + const mapping = scandicSasTierMappings.find(([tierMapping]) => + tierMapping.includes(key) + ) + if (!mapping) return + const sasTierCode = mapping[1][0] + setActiveSasTierCode(sasTierCode) + } + + const onChangeSas = (k: Key) => { + const key = k.toString() + setActiveSasTierCode(key) + + const mapping = scandicSasTierMappings.find(([_, tierMapping]) => + tierMapping.includes(key) + ) + if (!mapping) return + const scandicTierCode = mapping[0][0] + setActiveScandicTierCode(scandicTierCode) + } + + return ( + +
+ + {comparisonContent.preamble && ( +

{comparisonContent.preamble}

+ )} +
+
+
+
+ + {scandic.title} + + ({ + label: tier.tier_label, + value: tier.tier_code, + }))} + value={activeSasTierCode} + onSelect={onChangeSas} + /> + {sas.read_more_link && ( + + )} +
+
+ {comparisonContent.cta && ( +
+ +
+ )} +
+
+ ) +} + +function ReadMoreLink({ href, title }: { href: string; title: string }) { + return ( + + {title} + + + ) +} diff --git a/components/SasTierComparison/sas-tier-comparison.module.css b/components/SasTierComparison/sas-tier-comparison.module.css new file mode 100644 index 000000000..8193400d4 --- /dev/null +++ b/components/SasTierComparison/sas-tier-comparison.module.css @@ -0,0 +1,72 @@ +.comparisonSection { + width: 100%; + gap: var(--Spacing-x4); +} + +.header { + display: flex; + flex-direction: column; + gap: var(--Spacing-x1); +} + +.preamble { + margin: 0; + white-space: pre-wrap; +} + +.comparisonCard { + background-color: var(--Base-Surface-Subtle-Normal); + padding: var(--Spacing-x4) var(--Spacing-x3); + border-radius: var(--Corner-radius-Large); +} + +.comparisonContainer { + display: flex; + gap: var(--Spacing-x2); +} + +.comparisonBrand { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); + width: 100%; +} + +.comparisonIcon { + background-color: var(--Base-Surface-Secondary-light-Normal); + border-radius: 50%; + width: 48px; + height: 48px; + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + margin-top: var(--Spacing-x7); +} + +@media screen and (max-width: 767px) { + .comparisonIcon { + display: none; + } + .comparisonContainer { + flex-direction: column; + gap: var(--Spacing-x4); + } +} + +.link { + display: flex; + align-items: center; + gap: var(--Spacing-x-half); +} + +.ctaContainer { + border-top: 1px solid var(--Base-Border-Subtle); + display: flex; + justify-content: center; + margin-top: var(--Spacing-x4); +} + +.ctaLink { + margin-top: var(--Spacing-x4); +} diff --git a/lib/graphql/Fragments/Blocks/SasTierComparison.graphql b/lib/graphql/Fragments/Blocks/SasTierComparison.graphql new file mode 100644 index 000000000..f96f34210 --- /dev/null +++ b/lib/graphql/Fragments/Blocks/SasTierComparison.graphql @@ -0,0 +1,45 @@ +fragment SasTierComparison_ContentPage on ContentPageBlocksSasTierComparison { + __typename + sas_tier_comparison { + comparisonConnection { + totalCount + edges { + node { + __typename + ... on SasTierComparison { + title + preamble + scandic_friends { + title + label + tiers { + tier_code + tier_label + } + read_more_link { + href + title + } + } + sas_eb { + title + label + tiers { + tier_code + tier_label + } + read_more_link { + href + title + } + } + cta { + href + title + } + } + } + } + } + } +} diff --git a/lib/graphql/Query/ContentPage/ContentPage.graphql b/lib/graphql/Query/ContentPage/ContentPage.graphql index 87b7f285f..85304010e 100644 --- a/lib/graphql/Query/ContentPage/ContentPage.graphql +++ b/lib/graphql/Query/ContentPage/ContentPage.graphql @@ -9,6 +9,7 @@ #import "../../Fragments/Blocks/Table.graphql" #import "../../Fragments/Blocks/TextCols.graphql" #import "../../Fragments/Blocks/UspGrid.graphql" +#import "../../Fragments/Blocks/SasTierComparison.graphql" #import "../../Fragments/ContentPage/NavigationLinks.graphql" #import "../../Fragments/Sidebar/Content.graphql" @@ -75,6 +76,15 @@ query GetContentPageBlocksBatch2($locale: String!, $uid: String!) { } } +query GetContentPageBlocksBatch3($locale: String!, $uid: String!) { + content_page(uid: $uid, locale: $locale) { + blocks { + __typename + ...SasTierComparison_ContentPage + } + } +} + query GetContentPageRefs($locale: String!, $uid: String!) { content_page(locale: $locale, uid: $uid) { header { diff --git a/server/routers/contentstack/contentPage/output.ts b/server/routers/contentstack/contentPage/output.ts index 163be3012..c512b971e 100644 --- a/server/routers/contentstack/contentPage/output.ts +++ b/server/routers/contentstack/contentPage/output.ts @@ -18,6 +18,7 @@ import { dynamicContentRefsSchema, dynamicContentSchema as blockDynamicContentSchema, } from "../schemas/blocks/dynamicContent" +import { sasTierComparisonSchema } from "../schemas/blocks/sasTierComparison" import { hotelListingSchema } from "../schemas/blocks/hotelListing" import { shortcutsRefsSchema, @@ -104,6 +105,13 @@ export const contentPageAccordion = z }) .merge(accordionSchema) +export const contentPageLoyaltyTierComparison = z + .object({ + __typename: z.literal( + ContentPageEnum.ContentStack.blocks.SasTierComparison + ), + }) + .merge(sasTierComparisonSchema) export const contentPageHotelListing = z .object({ __typename: z.literal(ContentPageEnum.ContentStack.blocks.HotelListing), @@ -119,6 +127,7 @@ export const blocksSchema = z.discriminatedUnion("__typename", [ contentPageTable, contentPageTextCols, contentPageUspGrid, + contentPageLoyaltyTierComparison, contentPageHotelListing, ]) diff --git a/server/routers/contentstack/contentPage/query.ts b/server/routers/contentstack/contentPage/query.ts index 02c7419d7..ea2698d39 100644 --- a/server/routers/contentstack/contentPage/query.ts +++ b/server/routers/contentstack/contentPage/query.ts @@ -3,6 +3,7 @@ import { GetContentPage, GetContentPageBlocksBatch1, GetContentPageBlocksBatch2, + GetContentPageBlocksBatch3, } from "@/lib/graphql/Query/ContentPage/ContentPage.graphql" import { contentstackExtendedProcedureUID, router } from "@/server/trpc" @@ -72,6 +73,17 @@ export const contentPageQueryRouter = router({ }, }, }, + + { + document: GetContentPageBlocksBatch3, + variables: { locale: lang, uid }, + options: { + cache: "force-cache", + next: { + tags, + }, + }, + }, ]) const contentPage = contentPageSchema.safeParse(contentPageRequest.data) diff --git a/server/routers/contentstack/schemas/blocks/sasTierComparison.ts b/server/routers/contentstack/schemas/blocks/sasTierComparison.ts new file mode 100644 index 000000000..520419503 --- /dev/null +++ b/server/routers/contentstack/schemas/blocks/sasTierComparison.ts @@ -0,0 +1,64 @@ +import { z } from "zod" + +import { MembershipLevelEnum } from "@/constants/membershipLevels" + +import { BlocksEnums } from "@/types/enums/blocks" + +const link = z.object({ + href: z.string(), + title: z.string(), +}) + +export const sasTierComparisonSchema = z.object({ + typename: z + .literal(BlocksEnums.block.SasTierComparison) + .optional() + .default(BlocksEnums.block.SasTierComparison), + sas_tier_comparison: z + .object({ + comparisonConnection: z.object({ + edges: z.array( + z.object({ + node: z.object({ + title: z.string(), + preamble: z.string().optional(), + scandic_friends: z.object({ + title: z.string(), + label: z.string(), + tiers: z.array( + z.object({ + tier_code: z.nativeEnum(MembershipLevelEnum), + tier_label: z.string(), + }) + ), + read_more_link: link.optional(), + }), + sas_eb: z.object({ + title: z.string(), + label: z.string(), + tiers: z.array( + z.object({ + tier_code: z.enum([ + "Basic", + "Silver", + "Gold", + "Diamond", + "Pandion", + ]), + tier_label: z.string(), + }) + ), + read_more_link: link.optional(), + }), + cta: link.optional(), + }), + }) + ), + }), + }) + .transform((data) => { + return { + sasTierComparison: data.comparisonConnection.edges.at(0)?.node ?? null, + } + }), +}) diff --git a/types/enums/blocks.ts b/types/enums/blocks.ts index 73e0992e8..baf9a4a9c 100644 --- a/types/enums/blocks.ts +++ b/types/enums/blocks.ts @@ -9,6 +9,7 @@ export namespace BlocksEnums { TextCols = "TextCols", TextContent = "TextContent", UspGrid = "UspGrid", + SasTierComparison = "SasTierComparison", HotelListing = "HotelListing", } } diff --git a/types/enums/contentPage.ts b/types/enums/contentPage.ts index 55fb12c35..918c68790 100644 --- a/types/enums/contentPage.ts +++ b/types/enums/contentPage.ts @@ -9,6 +9,7 @@ export namespace ContentPageEnum { TextCols = "ContentPageBlocksTextCols", UspGrid = "ContentPageBlocksUspGrid", Table = "ContentPageBlocksTable", + SasTierComparison = "ContentPageBlocksSasTierComparison", HotelListing = "ContentPageBlocksHotelListing", } diff --git a/types/trpc/routers/contentstack/blocks.ts b/types/trpc/routers/contentstack/blocks.ts index 32aedbbfc..0c8a84374 100644 --- a/types/trpc/routers/contentstack/blocks.ts +++ b/types/trpc/routers/contentstack/blocks.ts @@ -7,6 +7,7 @@ import type { import type { contentSchema } from "@/server/routers/contentstack/schemas/blocks/content" import type { dynamicContentSchema } from "@/server/routers/contentstack/schemas/blocks/dynamicContent" import type { hotelListingSchema } from "@/server/routers/contentstack/schemas/blocks/hotelListing" +import type { sasTierComparisonSchema } from "@/server/routers/contentstack/schemas/blocks/sasTierComparison" import type { shortcutsSchema } from "@/server/routers/contentstack/schemas/blocks/shortcuts" import type { tableSchema } from "@/server/routers/contentstack/schemas/blocks/table" import type { textColsSchema } from "@/server/routers/contentstack/schemas/blocks/textCols" @@ -24,3 +25,5 @@ export interface TextCols extends z.output {} export interface UspGrid extends z.output {} interface GetHotelListing extends z.output {} export type HotelListing = GetHotelListing["hotel_listing"] +export interface SasTierComparison + extends z.output {}