feat(SW-66, SW-348): search functionality and ui
This commit is contained in:
@@ -5,8 +5,8 @@ import ContentCard from "@/components/TempDesignSystem/ContentCard"
|
||||
import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
|
||||
|
||||
import type { CardsGridProps } from "@/types/components/content/blocks"
|
||||
import { CardsGridEnum } from "@/types/components/content/enums"
|
||||
import type { CardsGridProps } from "@/types/components/blocks/cardsGrid"
|
||||
import { CardsGridEnum } from "@/types/enums/cardsGrid"
|
||||
|
||||
export default function CardsGrid({
|
||||
cards_grid,
|
||||
@@ -22,36 +22,37 @@ export default function CardsGrid({
|
||||
<Grids.Stackable>
|
||||
{cards_grid.cards.map((card) => {
|
||||
switch (card.__typename) {
|
||||
case CardsGridEnum.Card:
|
||||
case CardsGridEnum.cards.Card: {
|
||||
return card.isContentCard ? (
|
||||
<ContentCard
|
||||
key={card.system.uid}
|
||||
title={card.heading || ""}
|
||||
description={card.body_text || ""}
|
||||
description={card.body_text}
|
||||
backgroundImage={card.backgroundImage}
|
||||
primaryButton={card.primaryButton}
|
||||
secondaryButton={card.secondaryButton}
|
||||
sidePeekButton={card.sidePeekButton}
|
||||
backgroundImage={card.background_image}
|
||||
style="default"
|
||||
title={card.heading}
|
||||
/>
|
||||
) : (
|
||||
<Card
|
||||
theme={cards_grid.theme || "one"}
|
||||
key={card.system.uid}
|
||||
scriptedTopTitle={card.scripted_top_title}
|
||||
heading={card.heading}
|
||||
bodyText={card.body_text}
|
||||
secondaryButton={card.secondaryButton}
|
||||
heading={card.heading}
|
||||
primaryButton={card.primaryButton}
|
||||
secondaryButton={card.secondaryButton}
|
||||
scriptedTopTitle={card.scripted_top_title}
|
||||
theme={cards_grid.theme || "one"}
|
||||
/>
|
||||
)
|
||||
case CardsGridEnum.LoyaltyCard:
|
||||
}
|
||||
case CardsGridEnum.cards.LoyaltyCard:
|
||||
return (
|
||||
<LoyaltyCard
|
||||
key={card.system.uid}
|
||||
image={card.image}
|
||||
heading={card.heading}
|
||||
bodyText={card.body_text}
|
||||
heading={card.heading}
|
||||
image={card.image}
|
||||
link={card.link}
|
||||
/>
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
@@ -8,7 +8,6 @@ import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import styles from "./current.module.css"
|
||||
|
||||
@@ -19,21 +18,16 @@ export default async function CurrentBenefitsBlock({
|
||||
subtitle,
|
||||
link,
|
||||
}: AccountPageComponentProps) {
|
||||
const user = await serverClient().user.get()
|
||||
const user = await getProfile()
|
||||
// TAKE NOTE: we need clarification on how benefits stack from different levels
|
||||
// in order to determine if a benefit is specific to a level or if it is a cumulative benefit
|
||||
// we might have to add a new boolean property "exclusive" or similar
|
||||
if (!user || "error" in user) {
|
||||
return null
|
||||
}
|
||||
const membership = getMembership(user.memberships)
|
||||
if (!membership) {
|
||||
// TODO: handle this case?
|
||||
if (!user || "error" in user || !user.membership) {
|
||||
return null
|
||||
}
|
||||
|
||||
const currentLevel = getMembershipLevelObject(
|
||||
user.memberships[0].membershipLevel as MembershipLevelEnum,
|
||||
user.membership.membershipLevel as MembershipLevelEnum,
|
||||
getLang()
|
||||
)
|
||||
if (!currentLevel) {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Lock } from "react-feather"
|
||||
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
@@ -24,12 +24,12 @@ export default async function NextLevelBenefitsBlock({
|
||||
link,
|
||||
}: AccountPageComponentProps) {
|
||||
const intl = await getIntl()
|
||||
const user = await serverClient().user.get()
|
||||
if (!user || "error" in user) {
|
||||
const user = await getProfile()
|
||||
if (!user || "error" in user || !user.membership) {
|
||||
return null
|
||||
}
|
||||
const nextLevel = getMembershipLevelObject(
|
||||
user.memberships[0].nextLevel as MembershipLevelEnum,
|
||||
user.membership.nextLevel as MembershipLevelEnum,
|
||||
getLang()
|
||||
)
|
||||
if (!nextLevel) {
|
||||
27
components/Blocks/DynamicContent/HowItWorks/index.tsx
Normal file
27
components/Blocks/DynamicContent/HowItWorks/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import SectionWrapper from "../SectionWrapper"
|
||||
|
||||
import styles from "./howItWorks.module.css"
|
||||
|
||||
import type { HowItWorksProps } from "@/types/components/blocks/dynamicContent"
|
||||
|
||||
export default async function HowItWorks({
|
||||
dynamic_content,
|
||||
firstItem,
|
||||
}: HowItWorksProps) {
|
||||
const intl = await getIntl()
|
||||
const displayHeader = !!(
|
||||
dynamic_content.link ||
|
||||
dynamic_content.subtitle ||
|
||||
dynamic_content.title
|
||||
)
|
||||
return (
|
||||
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
|
||||
<section className={styles.container}>
|
||||
<Title level="h3">{intl.formatMessage({ id: "How it works" })}</Title>
|
||||
</section>
|
||||
</SectionWrapper>
|
||||
)
|
||||
}
|
||||
@@ -20,27 +20,35 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import levelsData from "@/data/loyaltyLevels"
|
||||
|
||||
import SectionWrapper from "../SectionWrapper"
|
||||
|
||||
import styles from "./loyaltyLevels.module.css"
|
||||
|
||||
import type { Level, LevelCardProps } from "@/types/components/content/blocks"
|
||||
import type { LoyaltyLevelsProps } from "@/types/components/blocks/dynamicContent"
|
||||
import type { Level, LevelCardProps } from "@/types/components/overviewTable"
|
||||
|
||||
export default function LoyaltyLevels() {
|
||||
export default function LoyaltyLevels({
|
||||
dynamic_content,
|
||||
firstItem,
|
||||
}: LoyaltyLevelsProps) {
|
||||
const params = useParams()
|
||||
const lang = params.lang as Lang
|
||||
const { formatMessage } = useIntl()
|
||||
|
||||
const { levels } = levelsData[lang]
|
||||
return (
|
||||
<section className={styles.cardContainer}>
|
||||
{levels.map((level: Level) => (
|
||||
<LevelCard
|
||||
key={level.level}
|
||||
formatMessage={formatMessage}
|
||||
lang={lang}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
|
||||
<section className={styles.cardContainer}>
|
||||
{levels.map((level: Level) => (
|
||||
<LevelCard
|
||||
key={level.level}
|
||||
formatMessage={formatMessage}
|
||||
lang={lang}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
</SectionWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import CopyButton from "../../Buttons/CopyButton"
|
||||
import { MembershipNumberProps } from "./membershipNumber"
|
||||
import { membershipNumberVariants } from "./membershipNumberVariants"
|
||||
|
||||
import styles from "./membershipNumber.module.css"
|
||||
|
||||
import type { MembershipNumberProps } from "@/types/components/myPages/membershipNumber"
|
||||
|
||||
export default async function MembershipNumber({
|
||||
className,
|
||||
color,
|
||||
@@ -23,7 +24,7 @@ export default async function MembershipNumber({
|
||||
</Caption>
|
||||
<span className={styles.icon}>
|
||||
<Caption className={styles.icon} color="pale" asChild>
|
||||
<code>{membership.membershipNumber ?? "N/A"}</code>
|
||||
<code>{membership?.membershipNumber ?? "N/A"}</code>
|
||||
</Caption>
|
||||
{membership && (
|
||||
<CopyButton membershipNumber={membership.membershipNumber} />
|
||||
@@ -3,22 +3,20 @@ import { membershipLevels } from "@/constants/membershipLevels"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembership, isHighestMembership } from "@/utils/user"
|
||||
import { isHighestMembership } from "@/utils/user"
|
||||
|
||||
import { MembershipNumberProps } from "./MemershipNumber/membershipNumber"
|
||||
import MembershipLevel from "./MembershipLevel"
|
||||
import MembershipNumber from "./MemershipNumber"
|
||||
|
||||
import styles from "./friend.module.css"
|
||||
|
||||
import type { UserProps } from "@/types/components/myPages/user"
|
||||
import type { FriendProps } from "@/types/components/myPages/friend"
|
||||
|
||||
export default async function Friend({
|
||||
user,
|
||||
color,
|
||||
}: UserProps & Pick<MembershipNumberProps, "color">) {
|
||||
children,
|
||||
membership,
|
||||
name,
|
||||
}: FriendProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const membership = getMembership(user.memberships)
|
||||
if (!membership?.membershipLevel) {
|
||||
return null
|
||||
}
|
||||
@@ -45,9 +43,9 @@ export default async function Friend({
|
||||
</header>
|
||||
<div className={styles.membership}>
|
||||
<Title className={styles.name} color="pale" level="h3">
|
||||
{user.name}
|
||||
{name}
|
||||
</Title>
|
||||
<MembershipNumber membership={membership} color={color} />
|
||||
{children}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
@@ -1,12 +1,12 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import Hero from "./Friend/Hero"
|
||||
import MembershipNumber from "./Friend/MembershipNumber"
|
||||
import Friend from "./Friend"
|
||||
import Stats from "./Stats"
|
||||
|
||||
@@ -19,7 +19,7 @@ export default async function Overview({
|
||||
subtitle,
|
||||
title,
|
||||
}: AccountPageComponentProps) {
|
||||
const user = await serverClient().user.get()
|
||||
const user = await getProfile()
|
||||
if (!user || "error" in user) {
|
||||
return null
|
||||
}
|
||||
@@ -28,7 +28,9 @@ export default async function Overview({
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<Hero color="red">
|
||||
<Friend user={user} color="burgundy" />
|
||||
<Friend membership={user.membership} name={user.name}>
|
||||
<MembershipNumber color="burgundy" membership={user.membership} />
|
||||
</Friend>
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
<Stats user={user} />
|
||||
</Hero>
|
||||
@@ -6,7 +6,7 @@ import BenefitValue from "../BenefitValue"
|
||||
|
||||
import styles from "./benefitCard.module.css"
|
||||
|
||||
import type { BenefitCardProps } from "@/types/components/loyalty/blocks"
|
||||
import type { BenefitCardProps } from "@/types/components/overviewTable"
|
||||
|
||||
export default function BenefitCard({
|
||||
comparedValues,
|
||||
@@ -4,7 +4,7 @@ import BenefitCard from "../BenefitCard"
|
||||
|
||||
import styles from "./benefitList.module.css"
|
||||
|
||||
import type { BenefitListProps } from "@/types/components/content/blocks"
|
||||
import type { BenefitListProps } from "@/types/components/overviewTable"
|
||||
|
||||
export default function BenefitList({ levels }: BenefitListProps) {
|
||||
return getUnlockedBenefits(levels).map((benefit) => {
|
||||
@@ -4,7 +4,7 @@ import CheckCircle from "@/components/Icons/CheckCircle"
|
||||
|
||||
import styles from "./benefitValue.module.css"
|
||||
|
||||
import type { BenefitValueProps } from "@/types/components/loyalty/blocks"
|
||||
import type { BenefitValueProps } from "@/types/components/overviewTable"
|
||||
|
||||
export default function BenefitValue({ benefit }: BenefitValueProps) {
|
||||
if (!benefit.unlocked) {
|
||||
@@ -31,9 +31,9 @@ import {
|
||||
DesktopSelectColumns,
|
||||
type MobileColumnHeaderProps,
|
||||
overviewTableActionsEnum,
|
||||
type OverviewTableProps,
|
||||
type OverviewTableClientProps,
|
||||
OverviewTableReducerAction,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
} from "@/types/components/overviewTable"
|
||||
import type { User } from "@/types/user"
|
||||
|
||||
const levelsTranslations = {
|
||||
@@ -126,9 +126,9 @@ function reducer(state: any, action: OverviewTableReducerAction) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function OverviewTable({
|
||||
export default function OverviewTableClient({
|
||||
activeMembership,
|
||||
}: OverviewTableProps) {
|
||||
}: OverviewTableClientProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const levelsData = levelsTranslations[lang]
|
||||
@@ -241,6 +241,7 @@ export default function OverviewTable({
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.mobileColumns}>
|
||||
@@ -8,7 +8,7 @@ import styles from "./desktopHeader.module.css"
|
||||
import type {
|
||||
DesktopSelectColumns,
|
||||
LargeTableProps,
|
||||
} from "@/types/components/loyalty/blocks"
|
||||
} from "@/types/components/overviewTable"
|
||||
|
||||
export default function DesktopHeader({
|
||||
levels,
|
||||
@@ -11,7 +11,7 @@ import styles from "./largeTable.module.css"
|
||||
import type {
|
||||
BenefitTableHeaderProps,
|
||||
LargeTableProps,
|
||||
} from "@/types/components/content/blocks"
|
||||
} from "@/types/components/overviewTable"
|
||||
|
||||
export default function LargeTable({
|
||||
levels,
|
||||
@@ -1,6 +1,6 @@
|
||||
import styles from "./levelSummary.module.css"
|
||||
|
||||
import type { LevelSummaryProps } from "@/types/components/content/blocks"
|
||||
import type { LevelSummaryProps } from "@/types/components/overviewTable"
|
||||
|
||||
export default function LevelSummary({
|
||||
level,
|
||||
18
components/Blocks/DynamicContent/OverviewTable/index.tsx
Normal file
18
components/Blocks/DynamicContent/OverviewTable/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
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 membershipLevel = await serverClient().user.safeMembershipLevel()
|
||||
return (
|
||||
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
|
||||
<OverviewTableClient activeMembership={membershipLevel} />
|
||||
</SectionWrapper>
|
||||
)
|
||||
}
|
||||
@@ -6,14 +6,15 @@ import { useIntl } from "react-intl"
|
||||
import { webviews } from "@/constants/routes/webviews"
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import AwardPoints from "@/components/MyPages/Blocks/Points/EarnAndBurn/AwardPoints"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Table from "@/components/TempDesignSystem/Table"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import AwardPoints from "../../../AwardPoints"
|
||||
|
||||
import type { RowProps } from "@/types/components/myPages/myPage/earnAndBurn"
|
||||
import { RewardTransactionTypes } from "@/types/components/myPages/myPage/enums"
|
||||
import { Transactions } from "@/types/enums/transactions"
|
||||
|
||||
export default function Row({ transaction }: RowProps) {
|
||||
const intl = useIntl()
|
||||
@@ -29,8 +30,8 @@ export default function Row({ transaction }: RowProps) {
|
||||
: `${nightString}`
|
||||
|
||||
switch (transaction.type) {
|
||||
case RewardTransactionTypes.stay:
|
||||
case RewardTransactionTypes.stayAdj:
|
||||
case Transactions.rewardType.stay:
|
||||
case Transactions.rewardType.stayAdj:
|
||||
if (transaction.hotelId === "ORS") {
|
||||
description = intl.formatMessage({ id: "Former Scandic Hotel" })
|
||||
}
|
||||
@@ -40,19 +41,19 @@ export default function Row({ transaction }: RowProps) {
|
||||
})
|
||||
}
|
||||
break
|
||||
case RewardTransactionTypes.ancillary:
|
||||
case Transactions.rewardType.ancillary:
|
||||
description = intl.formatMessage({ id: "Extras to your booking" })
|
||||
break
|
||||
case RewardTransactionTypes.enrollment:
|
||||
case Transactions.rewardType.enrollment:
|
||||
description = intl.formatMessage({ id: "Sign up bonus" })
|
||||
break
|
||||
case RewardTransactionTypes.mastercard_points:
|
||||
case Transactions.rewardType.mastercard_points:
|
||||
description = intl.formatMessage({ id: "Scandic Friends Mastercard" })
|
||||
break
|
||||
case RewardTransactionTypes.tui_points:
|
||||
case Transactions.rewardType.tui_points:
|
||||
description = intl.formatMessage({ id: "TUI Points" })
|
||||
|
||||
case RewardTransactionTypes.pointShop:
|
||||
case Transactions.rewardType.pointShop:
|
||||
description = intl.formatMessage({ id: "Scandic Friends Point Shop" })
|
||||
break
|
||||
}
|
||||
@@ -65,8 +66,8 @@ export default function Row({ transaction }: RowProps) {
|
||||
if (
|
||||
!isWebview &&
|
||||
transaction.bookingUrl &&
|
||||
(transaction.type === RewardTransactionTypes.stay ||
|
||||
transaction.type === RewardTransactionTypes.rewardNight)
|
||||
(transaction.type === Transactions.rewardType.stay ||
|
||||
transaction.type === Transactions.rewardType.rewardNight)
|
||||
) {
|
||||
return (
|
||||
<Link variant="underscored" href={transaction.bookingUrl}>
|
||||
@@ -6,7 +6,7 @@ import JourneyTable from "./JourneyTable"
|
||||
|
||||
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
|
||||
|
||||
export default async function EarnAndBurn({
|
||||
export default function EarnAndBurn({
|
||||
link,
|
||||
subtitle,
|
||||
title,
|
||||
@@ -4,11 +4,12 @@ import { useIntl } from "react-intl"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import AwardPoints from "@/components/MyPages/Blocks/Points/EarnAndBurn/AwardPoints"
|
||||
import Table from "@/components/TempDesignSystem/Table"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import AwardPoints from "../../EarnAndBurn/AwardPoints"
|
||||
|
||||
const tableHeadings = ["Points", "Expiration Date"]
|
||||
|
||||
export default function ExpiringPointsTable({
|
||||
@@ -1,18 +1,16 @@
|
||||
import {
|
||||
MembershipLevelEnum,
|
||||
membershipLevels,
|
||||
} from "@/constants/membershipLevels"
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
|
||||
import PointsContainer from "@/components/MyPages/Blocks/Overview/Stats/Points/Container"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
import PointsContainer from "../../../Overview/Stats/Points/Container"
|
||||
import {
|
||||
NextLevelNightsColumn,
|
||||
NextLevelPointsColumn,
|
||||
StayOnLevelColumn,
|
||||
YourPointsColumn,
|
||||
} from "@/components/MyPages/Blocks/Overview/Stats/Points/PointsColumn"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
import { getMembership } from "@/utils/user"
|
||||
} from "../../../Overview/Stats/Points/PointsColumn"
|
||||
|
||||
import { UserProps } from "@/types/components/myPages/user"
|
||||
import { LangParams } from "@/types/params"
|
||||
@@ -1,4 +1,4 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
@@ -7,6 +7,7 @@ import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import Friend from "../../Overview/Friend"
|
||||
import Hero from "../../Overview/Friend/Hero"
|
||||
import MembershipNumber from "../../Overview/Friend/MembershipNumber"
|
||||
import Stats from "../../Overview/Stats"
|
||||
|
||||
import styles from "./overview.module.css"
|
||||
@@ -18,7 +19,7 @@ export default async function PointsOverview({
|
||||
subtitle,
|
||||
title,
|
||||
}: AccountPageComponentProps) {
|
||||
const user = await serverClient().user.get()
|
||||
const user = await getProfile()
|
||||
if (!user || "error" in user) {
|
||||
return null
|
||||
}
|
||||
@@ -27,7 +28,9 @@ export default async function PointsOverview({
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<Hero color="burgundy">
|
||||
<Friend user={user} color="red" />
|
||||
<Friend membership={user.membership} name={user.name}>
|
||||
<MembershipNumber color="red" membership={user.membership} />
|
||||
</Friend>
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
<Stats user={user} />
|
||||
</Hero>
|
||||
46
components/Blocks/DynamicContent/SectionWrapper/index.tsx
Normal file
46
components/Blocks/DynamicContent/SectionWrapper/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import SectionLink from "@/components/Section/Link"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import styles from "./sectionWrapper.module.css"
|
||||
|
||||
import type { DynamicContentProps } from "@/types/components/blocks/dynamicContent"
|
||||
import { DynamicContentEnum } from "@/types/enums/dynamicContent"
|
||||
|
||||
export default function SectionWrapper({
|
||||
children,
|
||||
dynamic_content,
|
||||
firstItem,
|
||||
}: React.PropsWithChildren<DynamicContentProps>) {
|
||||
const displayHeader = !!(
|
||||
dynamic_content.link ||
|
||||
dynamic_content.subtitle ||
|
||||
dynamic_content.title
|
||||
)
|
||||
const isOverviewTable =
|
||||
dynamic_content.component ===
|
||||
DynamicContentEnum.Blocks.components.overview_table
|
||||
return (
|
||||
<SectionContainer className={styles.container}>
|
||||
{isOverviewTable ? (
|
||||
<div className={styles.header}>
|
||||
<Title className={styles.tableTitle}> {dynamic_content.title}</Title>
|
||||
<Subtitle>{dynamic_content.subtitle}</Subtitle>
|
||||
</div>
|
||||
) : displayHeader ? (
|
||||
<SectionHeader
|
||||
link={dynamic_content.link}
|
||||
preamble={dynamic_content.subtitle}
|
||||
title={dynamic_content.title}
|
||||
topTitle={firstItem}
|
||||
/>
|
||||
) : null}
|
||||
{children}
|
||||
{displayHeader ? (
|
||||
<SectionLink link={dynamic_content.link} variant="mobile" />
|
||||
) : null}
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export default function ClientPreviousStays({
|
||||
// TS having a hard time with the filtered type.
|
||||
// This is only temporary as we will not return null
|
||||
// later on when we handle errors appropriately.
|
||||
const filteredStays = (data?.pages.filter((page) => page && page.data) ??
|
||||
const filteredStays = (data?.pages.filter((page) => page?.data) ??
|
||||
[]) as unknown as PreviousStaysNonNullResponseObject[]
|
||||
const stays = filteredStays.flatMap((page) => page.data)
|
||||
|
||||
@@ -15,17 +15,17 @@ export default async function SoonestStays({
|
||||
subtitle,
|
||||
link,
|
||||
}: AccountPageComponentProps) {
|
||||
const response = await serverClient().user.stays.upcoming({ limit: 3 })
|
||||
if (!response?.data) {
|
||||
const upcomingStays = await serverClient().user.stays.upcoming({ limit: 3 })
|
||||
if (!upcomingStays?.data) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} preamble={subtitle} link={link} />
|
||||
{response.data.length ? (
|
||||
{upcomingStays.data.length ? (
|
||||
<Grids.Stackable>
|
||||
{response.data.map((stay) => (
|
||||
{upcomingStays.data.map((stay) => (
|
||||
<StayCard key={stay.attributes.confirmationNumber} stay={stay} />
|
||||
))}
|
||||
</Grids.Stackable>
|
||||
61
components/Blocks/DynamicContent/index.tsx
Normal file
61
components/Blocks/DynamicContent/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import CurrentBenefitsBlock from "@/components/Blocks/DynamicContent/Benefits/CurrentLevel"
|
||||
import NextLevelBenefitsBlock from "@/components/Blocks/DynamicContent/Benefits/NextLevel"
|
||||
import HowItWorks from "@/components/Blocks/DynamicContent/HowItWorks"
|
||||
import LoyaltyLevels from "@/components/Blocks/DynamicContent/LoyaltyLevels"
|
||||
import Overview from "@/components/Blocks/DynamicContent/Overview"
|
||||
import OverviewTable from "@/components/Blocks/DynamicContent/OverviewTable"
|
||||
import EarnAndBurn from "@/components/Blocks/DynamicContent/Points/EarnAndBurn"
|
||||
import ExpiringPoints from "@/components/Blocks/DynamicContent/Points/ExpiringPoints"
|
||||
import PointsOverview from "@/components/Blocks/DynamicContent/Points/Overview"
|
||||
import PreviousStays from "@/components/Blocks/DynamicContent/Stays/Previous"
|
||||
import SoonestStays from "@/components/Blocks/DynamicContent/Stays/Soonest"
|
||||
import UpcomingStays from "@/components/Blocks/DynamicContent/Stays/Upcoming"
|
||||
|
||||
import type { DynamicContentProps } from "@/types/components/blocks/dynamicContent"
|
||||
import { DynamicContentEnum } from "@/types/enums/dynamicContent"
|
||||
|
||||
export default async function DynamicContent({
|
||||
dynamic_content,
|
||||
firstItem,
|
||||
}: DynamicContentProps) {
|
||||
switch (dynamic_content.component) {
|
||||
case DynamicContentEnum.Blocks.components.current_benefits:
|
||||
return <CurrentBenefitsBlock {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.earn_and_burn:
|
||||
return <EarnAndBurn {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.expiring_points:
|
||||
return <ExpiringPoints {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.how_it_works:
|
||||
return (
|
||||
<HowItWorks dynamic_content={dynamic_content} firstItem={firstItem} />
|
||||
)
|
||||
case DynamicContentEnum.Blocks.components.loyalty_levels:
|
||||
return (
|
||||
<LoyaltyLevels
|
||||
dynamic_content={dynamic_content}
|
||||
firstItem={firstItem}
|
||||
/>
|
||||
)
|
||||
case DynamicContentEnum.Blocks.components.membership_overview:
|
||||
return <Overview {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.next_benefits:
|
||||
return <NextLevelBenefitsBlock {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.overview_table:
|
||||
return (
|
||||
<OverviewTable
|
||||
dynamic_content={dynamic_content}
|
||||
firstItem={firstItem}
|
||||
/>
|
||||
)
|
||||
case DynamicContentEnum.Blocks.components.points_overview:
|
||||
return <PointsOverview {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.previous_stays:
|
||||
return <PreviousStays {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.soonest_stays:
|
||||
return <SoonestStays {...dynamic_content} />
|
||||
case DynamicContentEnum.Blocks.components.upcoming_stays:
|
||||
return <UpcomingStays {...dynamic_content} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import { renderOptions } from "./renderOptions"
|
||||
|
||||
import styles from "./textcols.module.css"
|
||||
|
||||
import type { TextColsProps } from "@/types/components/content/blocks"
|
||||
import type { TextColProps } from "@/types/components/blocks/textCols"
|
||||
|
||||
export default function TextCols({ textCols }: TextColsProps) {
|
||||
export default function TextCols({ text_cols }: TextColProps) {
|
||||
return (
|
||||
<div className={styles.columns}>
|
||||
{textCols.columns.map((col) => {
|
||||
{text_cols.columns.map((col) => {
|
||||
return (
|
||||
<section key={col.title} className={styles.column}>
|
||||
<Subtitle>{col.title}</Subtitle>
|
||||
@@ -2,15 +2,15 @@ import Link from "@/components/TempDesignSystem/Link"
|
||||
|
||||
import styles from "./textcols.module.css"
|
||||
|
||||
import type { EmbedByUid } from "@/types/components/jsontohtml"
|
||||
import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums"
|
||||
import type { EmbedByUid } from "@/types/transitionTypes/jsontohtml"
|
||||
import { RTEItemTypeEnum, RTETypeEnum } from "@/types/transitionTypes/rte/enums"
|
||||
import type {
|
||||
RTEDefaultNode,
|
||||
RTENext,
|
||||
RTENode,
|
||||
RTERegularNode,
|
||||
} from "@/types/rte/node"
|
||||
import type { RenderOptions } from "@/types/rte/option"
|
||||
} from "@/types/transitionTypes/rte/node"
|
||||
import type { RenderOptions } from "@/types/transitionTypes/rte/option"
|
||||
|
||||
export const renderOptions: RenderOptions = {
|
||||
[RTETypeEnum.p]: (
|
||||
64
components/Blocks/index.tsx
Normal file
64
components/Blocks/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import CardsGrid from "@/components/Blocks/CardsGrid"
|
||||
import DynamicContent from "@/components/Blocks/DynamicContent"
|
||||
import Shortcuts from "@/components/Blocks/Shortcuts"
|
||||
import TextCols from "@/components/Blocks/TextCols"
|
||||
import JsonToHtml from "@/components/JsonToHtml"
|
||||
|
||||
import type { BlocksProps } from "@/types/components/blocks"
|
||||
import { BlocksEnums } from "@/types/enums/blocks"
|
||||
|
||||
export default function Blocks({ blocks }: BlocksProps) {
|
||||
return blocks.map((block, idx) => {
|
||||
const firstItem = idx === 0
|
||||
switch (block.typename) {
|
||||
case BlocksEnums.block.CardsGrid:
|
||||
return (
|
||||
<CardsGrid
|
||||
cards_grid={block.cards_grid}
|
||||
key={`${block.cards_grid.title}-${idx}`}
|
||||
firstItem={firstItem}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.Content:
|
||||
return (
|
||||
<section key={`${block.typename}-${idx}`}>
|
||||
<JsonToHtml
|
||||
nodes={block.content.json.children}
|
||||
embeds={block.content.embedded_itemsConnection.edges}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
case BlocksEnums.block.DynamicContent:
|
||||
return (
|
||||
<DynamicContent
|
||||
dynamic_content={block.dynamic_content}
|
||||
firstItem={firstItem}
|
||||
key={`${block.dynamic_content.title}-${idx}`}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.Shortcuts:
|
||||
return (
|
||||
<Shortcuts
|
||||
firstItem={firstItem}
|
||||
key={`${block.shortcuts.title}-${idx}`}
|
||||
shortcuts={block.shortcuts.shortcuts}
|
||||
subtitle={block.shortcuts.subtitle}
|
||||
title={block.shortcuts.title}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.TextCols:
|
||||
return <TextCols text_cols={block.text_cols} />
|
||||
case BlocksEnums.block.TextContent:
|
||||
return (
|
||||
<section key={`${block.typename}-${idx}`}>
|
||||
<JsonToHtml
|
||||
embeds={block.text_content.content.embedded_itemsConnection.edges}
|
||||
nodes={block.text_content.content.json.children}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user