From af205451e65e71c3d6ed03c36aef4d2cb34c4c55 Mon Sep 17 00:00:00 2001 From: Arvid Norlin Date: Fri, 7 Jun 2024 15:54:56 +0200 Subject: [PATCH] refactor: improve level selection logic --- .../DynamicContent/OverviewTable/index.tsx | 228 +++++++++++------- .../Loyalty/Blocks/DynamicContent/index.tsx | 14 +- components/Loyalty/Blocks/index.tsx | 13 +- types/components/loyalty/blocks.ts | 6 + 4 files changed, 165 insertions(+), 96 deletions(-) diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx index 8a9a0ee5d..735490db2 100644 --- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx @@ -1,12 +1,11 @@ "use client" -import { Dispatch, SetStateAction, useEffect, useState } from "react" +import { useReducer } from "react" import { type Key } from "react-aria-components" import { useIntl } from "react-intl" import { Lang } from "@/constants/languages" import { membershipLevels } from "@/constants/membershipLevels" -import { trpc } from "@/lib/trpc/client" import Image from "@/components/Image" import Select from "@/components/TempDesignSystem/Form/Select" @@ -20,7 +19,11 @@ import OverviewTableTitle from "./Title" import styles from "./overviewTable.module.css" -import { ComparisonLevel } from "@/types/components/loyalty/blocks" +import { + ComparisonLevel, + OverviewTableProps, +} from "@/types/components/loyalty/blocks" +import { User } from "@/types/user" // These should ultimately be fetched from Contentstack const titleTranslations = { @@ -51,75 +54,102 @@ const titleTranslations = { ], } +enum actions { + SET_SELECTED_LEVEL_A_MOBILE = "SET_SELECTED_LEVEL_A_MOBILE", + SET_SELECTED_LEVEL_B_MOBILE = "SET_SELECTED_LEVEL_B_MOBILE", + SET_SELECTED_LEVEL_A_DESKTOP = "SET_SELECTED_LEVEL_A_DESKTOP", + SET_SELECTED_LEVEL_B_DESKTOP = "SET_SELECTED_LEVEL_B_DESKTOP", + SET_SELECTED_LEVEL_C_DESKTOP = "SET_SELECTED_LEVEL_C_DESKTOP", +} + function getLevelByTier(tier: number) { return levelsData.levels.find( (level) => level.tier === tier ) as ComparisonLevel } -export default function OverviewTable() { - const intl = useIntl() - // TODO: Should isLoading be used for some loader, or is it fine to just use default values and rerender? - // Also, since it's fine to not be authenticated, how should the error be handled? - const { data, isLoading } = trpc.user.get.useQuery() - - const [selectedLevelAMobile, setSelectedLevelAMobile] = useState( - getLevelByTier(1) - ) - const [selectedLevelBMobile, setSelectedLevelBMobile] = useState( - getLevelByTier(2) - ) - - const [selectedLevelADesktop, setSelectedLevelADesktop] = useState( - getLevelByTier(1) - ) - const [selectedLevelBDesktop, setSelectedLevelBDesktop] = useState( - getLevelByTier(2) - ) - const [selectedLevelCDesktop, setSelectedLevelCDesktop] = useState( - getLevelByTier(3) - ) - - useEffect(() => { - const membership = data?.memberships - ? getMembership(data.memberships) - : null - if (membership) { - const tier = membershipLevels[membership.membershipLevel] - - // The logic on which levels to display for upper levels is as requested by UI - if (tier === 6) { - setSelectedLevelAMobile(getLevelByTier(6)) - setSelectedLevelBMobile(getLevelByTier(7)) - - setSelectedLevelADesktop(getLevelByTier(5)) - setSelectedLevelBDesktop(getLevelByTier(6)) - setSelectedLevelCDesktop(getLevelByTier(7)) - } else if (tier === 7) { - setSelectedLevelAMobile(getLevelByTier(6)) - setSelectedLevelBMobile(getLevelByTier(7)) - - setSelectedLevelADesktop(getLevelByTier(6)) - setSelectedLevelBDesktop(getLevelByTier(7)) - setSelectedLevelCDesktop(getLevelByTier(1)) - } else { - setSelectedLevelAMobile(getLevelByTier(tier)) - setSelectedLevelBMobile(getLevelByTier(tier + 1)) - - setSelectedLevelADesktop(getLevelByTier(tier)) - setSelectedLevelBDesktop(getLevelByTier(tier + 1)) - setSelectedLevelCDesktop(getLevelByTier(tier + 2)) - } +function getInitialState(user?: User) { + const membership = user?.memberships ? getMembership(user.memberships) : null + if (!membership) { + return { + selectedLevelAMobile: getLevelByTier(1), + selectedLevelBMobile: getLevelByTier(2), + selectedLevelADesktop: getLevelByTier(1), + selectedLevelBDesktop: getLevelByTier(2), + selectedLevelCDesktop: getLevelByTier(3), } - }, [isLoading, data?.memberships]) + } + const tier = membershipLevels[membership.membershipLevel] - function handleSelectChange( - callback: Dispatch> - ) { + switch (tier) { + case 6: + return { + selectedLevelAMobile: getLevelByTier(6), + selectedLevelBMobile: getLevelByTier(7), + selectedLevelADesktop: getLevelByTier(5), + selectedLevelBDesktop: getLevelByTier(6), + selectedLevelCDesktop: getLevelByTier(7), + } + case 7: + return { + selectedLevelAMobile: getLevelByTier(6), + selectedLevelBMobile: getLevelByTier(7), + selectedLevelADesktop: getLevelByTier(6), + selectedLevelBDesktop: getLevelByTier(7), + selectedLevelCDesktop: getLevelByTier(1), + } + default: + return { + selectedLevelAMobile: getLevelByTier(tier), + selectedLevelBMobile: getLevelByTier(tier + 1), + selectedLevelADesktop: getLevelByTier(tier), + selectedLevelBDesktop: getLevelByTier(tier + 1), + selectedLevelCDesktop: getLevelByTier(tier + 2), + } + } +} + +function reducer(state: any, action: any) { + switch (action.type) { + case actions.SET_SELECTED_LEVEL_A_MOBILE: + return { + ...state, + selectedLevelAMobile: action.payload, + } + case actions.SET_SELECTED_LEVEL_B_MOBILE: + return { + ...state, + selectedLevelBMobile: action.payload, + } + case actions.SET_SELECTED_LEVEL_A_DESKTOP: + return { + ...state, + selectedLevelADesktop: action.payload, + } + case actions.SET_SELECTED_LEVEL_B_DESKTOP: + return { + ...state, + selectedLevelBDesktop: action.payload, + } + case actions.SET_SELECTED_LEVEL_C_DESKTOP: + return { + ...state, + selectedLevelCDesktop: action.payload, + } + default: + return state + } +} + +export default function OverviewTable({ user }: OverviewTableProps) { + const intl = useIntl() + + const [selectionState, dispatch] = useReducer(reducer, user, getInitialState) + + function handleSelectChange(actionType: string) { return (key: Key) => { if (typeof key === "number") { - const level = getLevelByTier(key) - callback(level) + dispatch({ payload: getLevelByTier(key), type: actionType }) } } } @@ -148,13 +178,13 @@ export default function OverviewTable() { name="benefitA" label={intl.formatMessage({ id: "Level" })} items={levelOptions} - value={selectedLevelAMobile.tier} - onSelect={handleSelectChange(setSelectedLevelAMobile)} + value={selectionState.selectedLevelAMobile.tier} + onSelect={handleSelectChange(actions.SET_SELECTED_LEVEL_A_MOBILE)} /> {selectedLevelAMobile.name} @@ -162,7 +192,8 @@ export default function OverviewTable() { level.tier === selectedLevelAMobile.tier + (level) => + level.tier === selectionState.selectedLevelAMobile.tier ) as ComparisonLevel } /> @@ -172,13 +203,13 @@ export default function OverviewTable() { name="benefitB" label={intl.formatMessage({ id: "Level" })} items={levelOptions} - value={selectedLevelBMobile.tier} - onSelect={handleSelectChange(setSelectedLevelBMobile)} + value={selectionState.selectedLevelBMobile.tier} + onSelect={handleSelectChange(actions.SET_SELECTED_LEVEL_B_MOBILE)} /> {selectedLevelBMobile.name} @@ -186,13 +217,19 @@ export default function OverviewTable() { level.tier === selectedLevelBMobile.tier + (level) => + level.tier === selectionState.selectedLevelBMobile.tier ) as ComparisonLevel } /> - +
@@ -201,13 +238,15 @@ export default function OverviewTable() { name="benefitA" label={intl.formatMessage({ id: "Level" })} items={levelOptions} - value={selectedLevelADesktop.tier} - onSelect={handleSelectChange(setSelectedLevelADesktop)} + value={selectionState.selectedLevelADesktop.tier} + onSelect={handleSelectChange( + actions.SET_SELECTED_LEVEL_A_DESKTOP + )} /> {selectedLevelADesktop.name} @@ -215,7 +254,8 @@ export default function OverviewTable() { level.tier === selectedLevelADesktop.tier + (level) => + level.tier === selectionState.selectedLevelADesktop.tier ) as ComparisonLevel } /> @@ -225,13 +265,15 @@ export default function OverviewTable() { name="benefitB" label={intl.formatMessage({ id: "Level" })} items={levelOptions} - value={selectedLevelBDesktop.tier} - onSelect={handleSelectChange(setSelectedLevelBDesktop)} + value={selectionState.selectedLevelBDesktop.tier} + onSelect={handleSelectChange( + actions.SET_SELECTED_LEVEL_B_DESKTOP + )} /> {selectedLevelBDesktop.name} @@ -239,7 +281,8 @@ export default function OverviewTable() { level.tier === selectedLevelBDesktop.tier + (level) => + level.tier === selectionState.selectedLevelBDesktop.tier ) as ComparisonLevel } /> @@ -249,13 +292,15 @@ export default function OverviewTable() { name="benefitC" label={intl.formatMessage({ id: "Level" })} items={levelOptions} - value={selectedLevelCDesktop.tier} - onSelect={handleSelectChange(setSelectedLevelCDesktop)} + value={selectionState.selectedLevelCDesktop.tier} + onSelect={handleSelectChange( + actions.SET_SELECTED_LEVEL_C_DESKTOP + )} /> {selectedLevelCDesktop.name} @@ -263,7 +308,8 @@ export default function OverviewTable() { level.tier === selectedLevelCDesktop.tier + (level) => + level.tier === selectionState.selectedLevelCDesktop.tier ) as ComparisonLevel } /> @@ -271,9 +317,9 @@ export default function OverviewTable() {
diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx index 3e5bb7ab0..b33734e6e 100644 --- a/components/Loyalty/Blocks/DynamicContent/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -14,14 +14,14 @@ import type { } from "@/types/components/loyalty/blocks" import { LoyaltyComponentEnum } from "@/types/components/loyalty/enums" -function DynamicComponentBlock({ component }: DynamicComponentProps) { +function DynamicComponentBlock({ component, user }: DynamicComponentProps) { switch (component) { case LoyaltyComponentEnum.how_it_works: return case LoyaltyComponentEnum.loyalty_levels: return case LoyaltyComponentEnum.overview_table: - return + return default: return null } @@ -29,6 +29,7 @@ function DynamicComponentBlock({ component }: DynamicComponentProps) { export default function DynamicContent({ dynamicContent, + user, }: DynamicContentProps) { const displayHeader = !!( dynamicContent.title || @@ -52,7 +53,14 @@ export default function DynamicContent({ ) : null} - + {dynamicContent.component === LoyaltyComponentEnum.overview_table ? ( + + ) : ( + + )} ) } diff --git a/components/Loyalty/Blocks/index.tsx b/components/Loyalty/Blocks/index.tsx index 5901f79d1..ea1a53870 100644 --- a/components/Loyalty/Blocks/index.tsx +++ b/components/Loyalty/Blocks/index.tsx @@ -1,3 +1,5 @@ +import { serverClient } from "@/lib/trpc/server" + import JsonToHtml from "@/components/JsonToHtml" import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" @@ -7,7 +9,9 @@ import CardsGrid from "./CardsGrid" import type { BlocksProps } from "@/types/components/loyalty/blocks" import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums" -export function Blocks({ blocks }: BlocksProps) { +export async function Blocks({ blocks }: BlocksProps) { + const user = await serverClient().user.get() + console.log({ user }) return blocks.map((block) => { switch (block.__typename) { case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: @@ -20,7 +24,12 @@ export function Blocks({ blocks }: BlocksProps) { ) case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: - return + return ( + + ) case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: return ( @@ -33,6 +37,8 @@ type OverviewTableTitleTranslation = { export type OverviewTableTitleProps = { texts: OverviewTableTitleTranslation[] } +export type OverviewTableProps = { user?: User } + export type Level = { tier: number name: string