feat: add card grid component

This commit is contained in:
Christel Westerberg
2024-04-26 08:19:19 +02:00
parent 2ddabf1e50
commit 00f30811cf
33 changed files with 575 additions and 121 deletions

View File

@@ -0,0 +1,28 @@
.container {
display: grid;
gap: 2.4rem;
}
.subtitle {
margin: 0;
}
.titleContainer {
display: grid;
gap: 0.8rem;
}
.cardContainer {
display: grid;
gap: 1.6rem;
}
@media screen and (min-width: 950px) {
.cardContainer {
grid-template-columns: 1fr 1fr;
}
.cardWrapper:last-child {
grid-column: span 2;
}
}

View File

@@ -0,0 +1,50 @@
import { _ } from "@/lib/translation"
import Card from "@/components/TempDesignSystem/Card"
import Title from "@/components/Title"
import styles from "./cardGrid.module.css"
import { CardGridProps, CardProps } from "@/types/components/loyalty/blocks"
export default function CardGrid({ card_grid }: CardGridProps) {
return (
<section className={styles.container}>
<header className={styles.titleContainer}>
<Title as="h3" level="h2" weight="semiBold" uppercase>
{card_grid.title}
</Title>
{card_grid.subtitle ? (
<Title
as="h5"
level="h3"
weight="regular"
className={styles.subtitle}
>
{card_grid.subtitle}
</Title>
) : null}
</header>
<div className={styles.cardContainer}>
{card_grid.cards.map((card, i) => (
<CardWrapper key={`${card.title}+${i}`} card={card} />
))}
</div>
</section>
)
}
function CardWrapper({ card }: CardProps) {
const link = card.referenceConnection.edges.length
? {
href: card.referenceConnection.edges[0].node.url,
title: _("Read more"),
}
: undefined
return (
<div className={styles.cardWrapper}>
<Card subtitle={card.subtitle} title={card.title} link={link} />
</div>
)
}

View File

@@ -0,0 +1,11 @@
.container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 37rem;
border-radius: 1.6rem;
background-color: var(--Base-Fill-Normal);
text-align: center;
margin-right: 1.6rem;
}

View File

@@ -1,3 +1,13 @@
import Title from "@/components/Title"
import styles from "./howItWorks.module.css"
export default function HowItWorks() {
return <div></div>
return (
<div className={styles.container}>
<Title level="h3" uppercase>
How it works Placeholder
</Title>
</div>
)
}

View File

@@ -1,26 +1,50 @@
import { Check } from "react-feather"
import { _ } from "@/lib/translation"
import { serverClient } from "@/lib/trpc/server"
import Image from "@/components/Image"
import Button from "@/components/TempDesignSystem/Button"
import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/Title"
import styles from "./loyaltyLevels.module.css"
import { LevelCardProps } from "@/types/components/loyalty/blocks"
export default async function LoyaltyLevels() {
const data = await serverClient().loyalty.levels.all()
return (
<div>
{data.map((level) => (
<LevelCard key={level.tier} level={level} />
<section className={styles.container}>
<div className={styles.cardContainer}>
{data.map((level) => (
<LevelCard key={level.tier} level={level} />
))}
</div>
<div className={styles.buttonContainer}>
<Button intent="primary" asChild>
<Link href={"/compare-all-levels"}>{_("Compare all levels")}</Link>
</Button>
</div>
</section>
)
}
function LevelCard({ level }: LevelCardProps) {
return (
<div className={styles.card}>
<Title level="h4">{level.tier}</Title>
<Image src={level.logo} alt={level.name} width={140} height={54} />
<p className={styles.qualifications}>
{level.requiredPoints} {_("or")} {level.requiredNights} {_("nights")}
</p>
{level.topBenefits.map((benefit) => (
<p key={benefit} className={styles.benefits}>
<Check className={styles.icon} />
{benefit}
</p>
))}
</div>
)
}
type LevelCardProps = {
level: {
tier: number
name: string
requiredPoints: number
requiredNights: string
topBenefits: string[]
}
}
function LevelCard({ level }: LevelCardProps) {
return <div></div>
}

View File

@@ -0,0 +1,83 @@
.container {
display: grid;
gap: 2.4rem;
}
.buttonContainer {
display: flex;
justify-content: center;
}
.cardContainer {
display: flex;
gap: 0.8rem;
overflow-x: auto;
padding-right: 1.6rem;
margin-right: -1.6rem;
/* Hide scrollbar IE and Edge */
-ms-overflow-style: none;
/* Hide Scrollbar Firefox */
scrollbar-width: none;
}
.card {
display: flex;
flex-direction: column;
align-items: center;
height: 37rem;
min-width: 32rem;
padding: 4rem 1rem;
background-color: var(--Base-Fill-Normal);
border-radius: 1.6rem;
gap: 1.8rem;
}
.qualifications {
margin: 0;
font-size: var(--typography-Body-Bold-fontSize);
line-height: var(--typography-Body-Bold-lineHeight);
/* font-weight: var(--typography-Body-Bold-fontWeight); -- Tokens not parsable*/
font-weight: 600;
}
.benefits {
font-family: var(--fira-sans);
font-size: var(--typography-Body-Regular-fontSize);
line-height: var(--typography-Body-Regular-lineHeight);
margin: 0;
text-align: center;
}
.icon {
font-family: var(--fira-sans);
position: relative;
top: 0.3rem;
height: 1.4rem;
}
@media screen and (min-width: 950px) {
.container {
gap: 3.2rem;
}
.cardContainer {
display: grid;
grid-template-columns: repeat(
12,
auto
); /* Three columns in the first row */
padding-right: 0;
margin-right: 0rem;
}
.card {
min-width: auto;
}
.card:nth-child(-n + 3) {
grid-column: span 4;
}
.card:nth-last-child(-n + 4) {
grid-column: span 3;
}
}

View File

@@ -0,0 +1,28 @@
.container {
display: grid;
gap: 2.4rem;
overflow: hidden;
margin-right: -1.6rem;
padding-right: 1.6rem;
}
.titleContainer {
display: grid;
grid-template-areas: "title link";
grid-template-columns: 1fr max-content;
padding-bottom: 0.8rem;
}
.title {
grid-area: title;
}
.link {
grid-area: link;
font-size: var(--typography-Body-Underlined-fontSize);
color: var(--some-black-color, #000);
}
.subtitle {
margin: 0;
}

View File

@@ -1,18 +1,27 @@
import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/Title"
import HowItWorks from "./HowItWorks"
import LoyaltyLevels from "./LoyaltyLevels"
import OverviewTable from "./OverviewTable"
import styles from "./dynamicContent.module.css"
import { DynamicContentProps } from "@/types/components/loyalty/blocks"
import {
DynamicContentProps,
LoyaltyComponent,
LoyaltyComponentEnum,
} from "@/types/components/loyalty/blocks"
} from "@/types/requests/loyaltyPage"
function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) {
switch (component) {
case LoyaltyComponentEnum.how_it_works:
return <p>How it works</p>
return <HowItWorks />
case LoyaltyComponentEnum.loyalty_levels:
return <p>loyalty_levels</p>
return <LoyaltyLevels />
case LoyaltyComponentEnum.overview_table:
return <p>overview_table</p>
// TODO: IMPLEMENT OVERVIEW TABLE!
return <OverviewTable />
default:
return null
}
@@ -21,14 +30,44 @@ function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) {
export default function DynamicContent({
dynamicContent,
}: DynamicContentProps) {
const link = dynamicContent.link.pageConnection.edges.length
? dynamicContent.link.pageConnection.edges[0].node.url
: null
return (
<section>
<section className={styles.container}>
<header>
<Title level="h3">{dynamicContent.title}</Title>
{dynamicContent.preamble ? <p>{dynamicContent.preamble}</p> : null}
{dynamicContent.link ? <></> : null}
<div className={styles.titleContainer}>
{dynamicContent.title && (
<Title
as="h3"
level="h2"
className={styles.title}
weight="semiBold"
uppercase
>
{dynamicContent.title}
</Title>
)}
{link && (
<Link className={styles.link} href={link}>
{dynamicContent.link.text}
</Link>
)}
</div>
{dynamicContent.subtitle && (
<Title
as="h5"
level="h3"
weight="regular"
className={styles.subtitle}
>
{dynamicContent.subtitle}
</Title>
)}
</header>
<DynamicComponentBlock component={dynamicContent.component} />
<div>
<DynamicComponentBlock component={dynamicContent.component} />
</div>
</section>
)
}

View File

@@ -1,6 +1,8 @@
import JsonToHtml from "@/components/JsonToHtml"
import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent"
import CardGrid from "./CardGrid"
import {
Blocks as BlocksType,
LoyaltyBlocksTypenameEnum,
@@ -10,7 +12,7 @@ export function Blocks({ blocks }: { blocks: BlocksType[] }) {
return blocks.map((block) => {
switch (block.__typename) {
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid:
return <p>Cards</p>
return <CardGrid card_grid={block.card_grid} />
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
return (
<JsonToHtml