Merged in monorepo-step-1 (pull request #1080)

Migrate to a monorepo setup - step 1

* Move web to subfolder /apps/scandic-web

* Yarn + transitive deps

- Move to yarn
- design-system package removed for now since yarn doesn't
support the parameter for token (ie project currently broken)
- Add missing transitive dependencies as Yarn otherwise
prevents these imports
- VS Code doesn't pick up TS path aliases unless you open
/apps/scandic-web instead of root (will be fixed with monorepo)

* Pin framer-motion to temporarily fix typing issue

https://github.com/adobe/react-spectrum/issues/7494

* Pin zod to avoid typ error

There seems to have been a breaking change in the types
returned by zod where error is now returned as undefined
instead of missing in the type. We should just handle this
but to avoid merge conflicts just pin the dependency for
now.

* Pin react-intl version

Pin version of react-intl to avoid tiny type issue where formatMessage
does not accept a generic any more. This will be fixed in a future
commit, but to avoid merge conflicts just pin for now.

* Pin typescript version

Temporarily pin version as newer versions as stricter and results in
a type error. Will be fixed in future commit after merge.

* Setup workspaces

* Add design-system as a monorepo package

* Remove unused env var DESIGN_SYSTEM_ACCESS_TOKEN

* Fix husky for monorepo setup

* Update netlify.toml

* Add lint script to root package.json

* Add stub readme

* Fix react-intl formatMessage types

* Test netlify.toml in root

* Remove root toml

* Update netlify.toml publish path

* Remove package-lock.json

* Update build for branch/preview builds


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-02-26 10:36:17 +00:00
committed by Linus Flood
parent 667cab6fb6
commit 80100e7631
2731 changed files with 30986 additions and 23708 deletions

View File

@@ -0,0 +1,176 @@
"use client"
import { useReducer } from "react"
import { useIntl } from "react-intl"
import {
type MembershipLevel,
membershipLevels,
} from "@/constants/membershipLevels"
import MembershipLevelIcon from "@/components/Levels/Icon"
import Select from "@/components/TempDesignSystem/Select"
import LargeTable from "./LargeTable"
import LevelSummary from "./LevelSummary"
import { getInitialState, getLevel, reducer } from "./reducer"
import RewardList from "./RewardList"
import YourLevel from "./YourLevelScript"
import styles from "./overviewTable.module.css"
import type { Key } from "react-aria-components"
import {
type ComparisonLevel,
type DesktopSelectColumns,
type MobileColumnHeaderProps,
OverviewTableActionsEnum,
type OverviewTableClientProps,
} from "@/types/components/overviewTable"
function getLevelNamesForSelect(level: MembershipLevel, levelName: string) {
const levelToNumber = membershipLevels[level]
return [levelToNumber, levelName].join(" ")
}
export default function OverviewTableClient({
activeMembership,
levels,
}: OverviewTableClientProps) {
const intl = useIntl()
const [selectionState, dispatch] = useReducer(
reducer,
{ activeMembership, levels },
getInitialState
)
function handleSelectChange(actionType: OverviewTableActionsEnum) {
return (key: Key) => {
dispatch({
payload: getLevel(key as MembershipLevel, levels),
type: actionType,
})
}
}
const levelOptions = levels.map((level) => ({
label: getLevelNamesForSelect(level.level_id, level.name),
value: level.level_id,
}))
const activeMembershipLevel = activeMembership ?? null
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_id ? (
<YourLevel />
) : null}
<MembershipLevelIcon
level={selectedLevelMobile.level_id}
color="red"
height="50"
width="100"
/>
</div>
<LevelSummary
level={
levels.find(
(level) => level.level_id === selectedLevelMobile.level_id
)!
}
showDescription={false}
/>
<Select
aria-label={intl.formatMessage({ id: "Level" })}
name={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
items={levelOptions}
value={selectedLevelMobile.level_id}
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={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
items={levelOptions}
value={selectedLevelDesktop.level_id}
onSelect={handleSelectChange(actionEnumDesktop)}
/>
)
}
return (
<div>
<div className={styles.mobileColumns}>
<div className={styles.columnHeaderContainer}>
<MobileColumnHeader column={"A"} />
<MobileColumnHeader column={"B"} />
</div>
<RewardList
levels={[
selectionState.selectedLevelAMobile,
selectionState.selectedLevelBMobile,
]}
/>
</div>
<div className={styles.columns}>
<LargeTable
levels={[
selectionState.selectedLevelADesktop,
selectionState.selectedLevelBDesktop,
selectionState.selectedLevelCDesktop,
]}
activeLevel={activeMembershipLevel}
Select={SelectDesktop}
/>
</div>
<div className={styles.largeTableContainer}>
<LargeTable levels={levels} activeLevel={activeMembershipLevel} />
</div>
</div>
)
}

View File

@@ -0,0 +1,33 @@
.header {
background-color: inherit;
}
.iconRow {
border-bottom: none;
position: sticky;
top: 0;
z-index: 1;
background-color: inherit;
}
.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 MembershipLevelIcon from "@/components/Levels/Icon"
import LevelSummary from "../../LevelSummary"
import YourLevel from "../../YourLevelScript"
import styles from "./desktopHeader.module.css"
import type {
DesktopSelectColumns,
LargeTableProps,
} from "@/types/components/overviewTable"
export default function DesktopHeader({
levels,
activeLevel,
Select,
}: LargeTableProps) {
return (
<thead className={styles.header}>
<tr className={styles.iconRow}>
<th className={styles.verticalTableHeader} />
{levels.map((level, idx) => {
return (
<th key={"image" + level.level_id + idx} className={styles.iconTh}>
{activeLevel === level.level_id ? <YourLevel /> : null}
<MembershipLevelIcon
color="red"
level={level.level_id}
height="50"
width="100"
/>
</th>
)
})}
</tr>
<tr>
<th />
{levels.map((level, idx) => {
return (
<th
key={"summary" + level.level_id + 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

@@ -0,0 +1,87 @@
import { ChevronDown } from "react-feather"
import Title from "@/components/TempDesignSystem/Text/Title"
import {
findAvailableRewards,
getGroupedLabelAndDescription,
getGroupedRewards,
} from "@/utils/loyaltyTable"
import RewardValue from "../RewardValue"
import DesktopHeader from "./DesktopHeader"
import styles from "./largeTable.module.css"
import type {
LargeTableProps,
RewardTableHeaderProps,
} from "@/types/components/overviewTable"
export default function LargeTable({
levels,
activeLevel,
Select,
}: LargeTableProps) {
const keyedGroupedRewards = getGroupedRewards(levels)
return (
<table className={styles.table}>
<DesktopHeader
levels={levels}
activeLevel={activeLevel}
Select={Select}
/>
<tbody className={styles.tbody}>
{Object.entries(keyedGroupedRewards).map(
([key, groupedRewards], idx) => {
const { label, description } =
getGroupedLabelAndDescription(groupedRewards)
return (
<tr key={key + idx} className={styles.tr}>
<th scope={"row"} className={styles.rewardTh}>
<RewardTableHeader name={label} description={description} />
</th>
{levels.map((level, idx) => {
const rewardIdsInGroup = groupedRewards.map(
(b) => b.reward_id
)
const reward = findAvailableRewards(rewardIdsInGroup, level)
return (
<td
key={`${reward?.reward_id}-${idx}`}
className={styles.td}
>
<RewardValue reward={reward} />
</td>
)
})}
</tr>
)
}
)}
</tbody>
</table>
)
}
function RewardTableHeader({ name, description }: RewardTableHeaderProps) {
return (
<details className={styles.details}>
<summary className={styles.summary}>
<hgroup className={styles.rewardHeader}>
<Title as="h4" level="h2" textTransform={"regular"}>
{name}
</Title>
<span className={styles.chevron}>
<ChevronDown />
</span>
</hgroup>
</summary>
<p
className={styles.rewardDescription}
dangerouslySetInnerHTML={{ __html: description }}
/>
</details>
)
}

View File

@@ -0,0 +1,58 @@
.table {
border: none;
border-collapse: collapse;
background-color: var(--UI-Opacity-White-100);
border-radius: var(--Corner-radius-Medium);
color: var(--UI-Grey-100);
}
.tr {
border-bottom: 1px solid var(--Base-Border-Subtle);
}
.tr:last-child {
border: none;
}
.td {
font-size: var(--typography-Footnote-Regular-fontSize);
text-align: center;
}
.rewardTh {
padding: var(--Spacing-x3) var(--Spacing-x2);
font-size: var(--typography-Caption-Regular-fontSize);
font-weight: var(--typography-Caption-Regular-fontWeight);
}
.details[open] .chevron {
transform: rotate(180deg);
}
.rewardHeader {
display: grid;
gap: var(--Spacing-x1);
grid-template-columns: 1fr auto;
text-align: start;
}
.rewardDescription {
margin: 0;
padding-top: var(--Spacing-x1);
text-align: start;
padding-right: calc(var(--Spacing-x3) + var(--Spacing-x1));
}
.chevron {
display: flex;
align-self: start;
color: var(--UI-Grey-80);
}
.summary::-webkit-details-marker {
display: none;
}
.summary {
list-style: none;
}

View File

@@ -0,0 +1,37 @@
import { useIntl } from "react-intl"
import styles from "./levelSummary.module.css"
import type { LevelSummaryProps } from "@/types/components/overviewTable"
export default function LevelSummary({
level,
showDescription = true,
}: LevelSummaryProps) {
const intl = useIntl()
const pointsMsg: React.ReactNode = level.required_nights
? intl.formatMessage(
{
id: "{pointsAmount, number} points or {nightsAmount, number} nights",
},
{
pointsAmount: level.required_points,
nightsAmount: level.required_nights,
highlight: (str) => <span className={styles.redText}>{str}</span>,
}
)
: intl.formatMessage(
{ id: "{pointsAmount, number} points" },
{ pointsAmount: level.required_points }
)
return (
<div className={styles.levelSummary}>
<span className={styles.levelRequirements}>{pointsMsg}</span>
{showDescription && (
<p className={styles.levelSummaryText}>{level.description}</p>
)}
</div>
)
}

View File

@@ -0,0 +1,37 @@
.levelSummary {
display: flex;
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);
text-align: center;
width: 100%;
}
.levelSummaryText {
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);
}
.levelSummaryText {
font-size: var(--typography-Caption-Regular-fontSize);
}
}

View File

@@ -0,0 +1,48 @@
import { ChevronDown } from "react-feather"
import Title from "@/components/TempDesignSystem/Text/Title"
import RewardValue from "../../RewardValue"
import styles from "./rewardCard.module.css"
import type { RewardCardProps } from "@/types/components/overviewTable"
export default function RewardCard({
comparedValues,
title,
description,
}: RewardCardProps) {
return (
<div className={styles.rewardCard}>
<div className={styles.rewardInfo}>
<details className={styles.details}>
<summary className={styles.summary}>
<hgroup className={styles.rewardCardHeader}>
<Title as="h4" level="h2" textTransform={"regular"}>
{title}
</Title>
<span className={styles.chevron}>
<ChevronDown />
</span>
</hgroup>
</summary>
<p
className={styles.rewardCardDescription}
dangerouslySetInnerHTML={{ __html: description }}
/>
</details>
</div>
<div className={styles.rewardComparison}>
{comparedValues.map((reward, idx) => (
<div
key={`${reward?.reward_id}-${idx}`}
className={styles.comparisonItem}
>
<RewardValue reward={reward} />
</div>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,55 @@
.rewardCard {
padding-bottom: var(--Spacing-x-one-and-half);
grid-column: 1/3;
}
.rewardCardHeader {
display: grid;
grid-template-columns: 1fr auto;
}
.rewardCardDescription {
font-size: var(--typography-Caption-Regular-fontSize);
line-height: 150%;
padding-right: var(--Spacing-x4);
}
.rewardInfo {
padding-bottom: var(--Spacing-x-one-and-half);
}
.rewardComparison {
display: grid;
grid-template-columns: 1fr 1fr;
}
.comparisonItem {
display: flex;
justify-content: center;
align-items: center;
padding-top: var(--Spacing-x-one-and-half);
}
.details[open] .chevron {
transform: rotate(180deg);
}
.chevron {
display: flex;
align-items: center;
color: var(--UI-Grey-80);
}
.summary::-webkit-details-marker {
display: none;
}
.summary {
list-style: none;
}
@media screen and (min-width: 950px) {
.rewardComparison {
grid-template-columns: 1fr 1fr 1fr;
}
}

View File

@@ -0,0 +1,38 @@
import {
findAvailableRewards,
getGroupedLabelAndDescription,
getGroupedRewards,
} from "@/utils/loyaltyTable"
import RewardCard from "./Card"
import styles from "./rewardList.module.css"
import type { RewardListProps } from "@/types/components/overviewTable"
export default function RewardList({ levels }: RewardListProps) {
const keyedGroupedRewards = getGroupedRewards(levels)
return Object.values(keyedGroupedRewards).map((groupedRewards) => {
const rewardIdsInGroup = groupedRewards.map((b) => b.reward_id)
const { label, description } = getGroupedLabelAndDescription(groupedRewards)
const levelRewards = levels.map((level) => {
return findAvailableRewards(rewardIdsInGroup, level)
})
return (
<div
key={levelRewards[0]?.reward_id ?? ""}
className={styles.rewardCardWrapper}
>
<RewardCard
title={label}
description={description}
comparedValues={levelRewards}
/>
</div>
)
})
}

View File

@@ -0,0 +1,19 @@
.rewardCardWrapper {
border-bottom: 1px solid var(--Base-Border-Subtle);
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
grid-column: 1/3;
padding-top: 0;
margin: var(--Spacing-x1) var(--Spacing-x2);
}
.rewardCardWrapper:last-child {
border: none;
}
@media screen and (min-width: 950px) {
.rewardCardWrapper {
grid-column: 1/4;
}
}

View File

@@ -0,0 +1,21 @@
import { Minus } from "react-feather"
import CheckCircle from "@/components/Icons/CheckCircle"
import styles from "./rewardValue.module.css"
import type { RewardValueProps } from "@/types/components/overviewTable"
export default function RewardValue({ reward }: RewardValueProps) {
if (!reward) {
return <Minus color="var(--UI-Grey-40)" />
}
if (!reward.value) {
return <CheckCircle height={32} width={32} color="green" />
}
return (
<div className={styles.rewardValueContainer}>
<span className={styles.rewardValue}>{reward.value}</span>
</div>
)
}

View File

@@ -0,0 +1,19 @@
.rewardValueContainer {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--Spacing-x-half);
padding: 0 var(--Spacing-x4) 0 var(--Spacing-x4);
text-wrap: balance;
}
.rewardValue {
font-size: var(--typography-Body-Bold-fontSize);
font-weight: var(--typography-Body-Bold-fontWeight);
}
.rewardValueDetails {
font-size: var(--typography-Footnote-Regular-fontSize);
text-align: center;
color: var(--UI-Grey-80);
}

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

@@ -0,0 +1,26 @@
import { getMembershipLevelSafely } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"
import SectionWrapper from "../SectionWrapper"
import OverviewTableClient from "./Client"
import type { OverviewTableProps } from "@/types/components/blocks/dynamicContent"
export default async function OverviewTable({
dynamic_content,
firstItem,
}: OverviewTableProps) {
const [levels, membershipLevel] = await Promise.all([
serverClient().contentstack.rewards.all(),
getMembershipLevelSafely(),
])
return (
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
<OverviewTableClient
levels={levels}
activeMembership={membershipLevel?.membershipLevel ?? null}
/>
</SectionWrapper>
)
}

View File

@@ -0,0 +1,100 @@
.intro {
display: grid;
gap: var(--Spacing-x3);
}
.largeTableContainer {
display: none;
}
.columns {
display: none;
position: relative;
background-color: var(--UI-Opacity-White-100);
border-radius: var(--Corner-radius-Medium);
}
.mobileColumns {
background-color: var(--UI-Opacity-White-100);
display: grid;
grid-template-columns: 1fr 1fr;
margin: 0 calc(0px - var(--Spacing-x2)) calc(0px - var(--Spacing-x9))
calc(0px - var(--Spacing-x2));
padding-bottom: var(--Spacing-x9);
position: relative;
}
.columnHeaderContainer {
display: contents;
grid-template-columns: 1fr 1fr;
gap: var(--Spacing-x2);
}
.columnHeader {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
padding: var(--Spacing-x4) var(--Spacing-x2);
justify-content: flex-end;
}
.icon {
align-self: center;
}
.columnHeader:nth-child(1) {
padding-right: var(--Spacing-x1);
}
.columnHeader:nth-child(2) {
padding-left: var(--Spacing-x1);
border-top-left-radius: var(--Corner-radius-Medium);
}
.columnHeader:nth-child(2):has(+ .columnHeader) {
padding-left: var(--Spacing-x1);
padding-right: var(--Spacing-x1);
}
.columnHeader:nth-child(3) {
padding-left: var(--Spacing-x1);
}
@media screen and (min-width: 768px) {
.mobileColumns {
padding-bottom: 0;
margin-bottom: 0;
}
}
@media screen and (min-width: 950px) {
.mobileColumns {
display: none;
}
.columnHeaderContainer {
grid-template-columns: 1fr 1fr 1fr;
}
.columnHeader:nth-child(2) {
border-top-right-radius: var(--Corner-radius-Medium);
}
.columns {
display: block;
}
}
@media screen and (min-width: 1367px) {
.columns {
display: none;
}
.intro {
margin: auto;
}
.largeTableContainer {
display: block;
margin: auto;
}
}

View File

@@ -0,0 +1,95 @@
import {
type MembershipLevel,
MembershipLevelEnum,
} from "@/constants/membershipLevels"
import { getSteppedUpLevel } from "@/utils/user"
import {
type LevelWithRewards,
OverviewTableActionsEnum,
type OverviewTableClientProps,
type OverviewTableReducerAction,
} from "@/types/components/overviewTable"
export function getLevel(
membershipLevel: MembershipLevel,
levels: LevelWithRewards[]
) {
return levels.find((level) => level.level_id === membershipLevel)!
}
export function getInitialState({
activeMembership,
levels,
}: OverviewTableClientProps) {
if (!activeMembership) {
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L1, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L2, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L1, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L2, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L3, levels),
}
}
const level = MembershipLevelEnum[activeMembership]
switch (level) {
case MembershipLevelEnum.L6:
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L5, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L7, levels),
}
case MembershipLevelEnum.L7:
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L1, levels),
}
default:
return {
selectedLevelAMobile: getLevel(level, levels),
selectedLevelBMobile: getLevel(getSteppedUpLevel(level, 1), levels),
selectedLevelADesktop: getLevel(level, levels),
selectedLevelBDesktop: getLevel(getSteppedUpLevel(level, 1), levels),
selectedLevelCDesktop: getLevel(getSteppedUpLevel(level, 2), levels),
}
}
}
export function reducer(state: any, action: OverviewTableReducerAction) {
switch (action.type) {
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE:
return {
...state,
selectedLevelAMobile: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE:
return {
...state,
selectedLevelBMobile: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP:
return {
...state,
selectedLevelADesktop: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP:
return {
...state,
selectedLevelBDesktop: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP:
return {
...state,
selectedLevelCDesktop: action.payload,
}
default:
return state
}
}