Merge branch 'develop' into feat/setup-hotel-api-call
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
background-color: var(--Scandic-Brand-Warm-White);
|
||||
display: grid;
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
gap: var(--Spacing-x5);
|
||||
gap: var(--Spacing-x3);
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import Script from "next/script"
|
||||
|
||||
import TrpcProvider from "@/lib/trpc/Provider"
|
||||
|
||||
import AdobeScript from "@/components/Current/AdobeScript"
|
||||
import AdobeSDKScript from "@/components/Current/AdobeSDKScript"
|
||||
import Footer from "@/components/Current/Footer"
|
||||
import VwoScript from "@/components/Current/VwoScript"
|
||||
import { getIntl } from "@/i18n"
|
||||
@@ -33,6 +33,7 @@ export default async function RootLayout({
|
||||
return (
|
||||
<html lang={params.lang}>
|
||||
<head>
|
||||
<AdobeSDKScript />
|
||||
<Script data-cookieconsent="ignore" src="/_static/js/cookie-bot.js" />
|
||||
<Script
|
||||
strategy="beforeInteractive"
|
||||
@@ -42,10 +43,6 @@ export default async function RootLayout({
|
||||
id="Cookiebot"
|
||||
src="https://consent.cookiebot.com/uc.js"
|
||||
/>
|
||||
<Script id="ensure-datalayer">{`
|
||||
window.datalayer = window.datalayer || {}
|
||||
`}</Script>
|
||||
<AdobeScript />
|
||||
<VwoScript />
|
||||
</head>
|
||||
<body>
|
||||
@@ -56,9 +53,6 @@ export default async function RootLayout({
|
||||
<Footer lang={params.lang} />
|
||||
</TrpcProvider>
|
||||
</ServerIntlProvider>
|
||||
<Script id="page-tracking">{`
|
||||
typeof _satellite !== "undefined" && _satellite.pageBottom();
|
||||
`}</Script>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
.blocks {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x5);
|
||||
gap: var(--Spacing-x7);
|
||||
padding-left: var(--Spacing-x2);
|
||||
padding-right: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.content {
|
||||
gap: var(--Spacing-x3);
|
||||
padding-left: var(--Spacing-x3);
|
||||
padding-right: var(--Spacing-x3);
|
||||
gap: var(--Spacing-x5);
|
||||
padding-left: var(--Spacing-x5);
|
||||
padding-right: var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.content:has(> aside) {
|
||||
@@ -29,6 +29,7 @@
|
||||
}
|
||||
|
||||
.blocks {
|
||||
gap: var(--Spacing-x9);
|
||||
padding-left: var(--Spacing-x0);
|
||||
padding-right: var(--Spacing-x0);
|
||||
}
|
||||
|
||||
13
components/Current/AdobeSDKScript.tsx
Normal file
13
components/Current/AdobeSDKScript.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import Script from "next/script"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
export default function AdobeSDKScript() {
|
||||
return env.ADOBE_SDK_SCRIPT_SRC ? (
|
||||
<Script
|
||||
data-cookieconsent="statistics"
|
||||
src={env.ADOBE_SDK_SCRIPT_SRC}
|
||||
async
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import type { IconProps } from "@/types/components/icon"
|
||||
export default function ArrowRightIcon({
|
||||
className,
|
||||
color,
|
||||
height = "25",
|
||||
width = "24",
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
@@ -12,9 +14,9 @@ export default function ArrowRightIcon({
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="25"
|
||||
height={height}
|
||||
viewBox="0 0 24 25"
|
||||
width="24"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -2,15 +2,21 @@ import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function EmailIcon({ className, color, ...props }: IconProps) {
|
||||
export default function EmailIcon({
|
||||
className,
|
||||
color,
|
||||
width = "20",
|
||||
height = "20",
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="20"
|
||||
height={height}
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
|
||||
36
components/Icons/Person.tsx
Normal file
36
components/Icons/Person.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function PersonIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_1572_4293"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_1572_4293)">
|
||||
<path
|
||||
d="M10 10C9.16667 10 8.45833 9.70833 7.875 9.125C7.29167 8.54167 7 7.83333 7 7C7 6.16667 7.29167 5.45833 7.875 4.875C8.45833 4.29167 9.16667 4 10 4C10.8333 4 11.5417 4.29167 12.125 4.875C12.7083 5.45833 13 6.16667 13 7C13 7.83333 12.7083 8.54167 12.125 9.125C11.5417 9.70833 10.8333 10 10 10ZM4 14.5V14C4 13.6806 4.08681 13.3785 4.26042 13.0938C4.43403 12.809 4.67361 12.5694 4.97917 12.375C5.74306 11.9306 6.55064 11.5903 7.40192 11.3542C8.25321 11.1181 9.11779 11 9.99567 11C10.8736 11 11.7396 11.1181 12.5938 11.3542C13.4479 11.5903 14.2569 11.9306 15.0208 12.375C15.3264 12.5556 15.566 12.7917 15.7396 13.0833C15.9132 13.375 16 13.6806 16 14V14.5C16 14.9125 15.853 15.2656 15.5591 15.5594C15.2652 15.8531 14.9119 16 14.4992 16H5.4941C5.08137 16 4.72917 15.8531 4.4375 15.5594C4.14583 15.2656 4 14.9125 4 14.5ZM5.5 14.5H14.5V14C14.5 13.9281 14.479 13.8628 14.437 13.8039C14.395 13.7451 14.3396 13.6993 14.2708 13.6667C13.6319 13.2778 12.9514 12.9861 12.2292 12.7917C11.5069 12.5972 10.7639 12.5 10 12.5C9.23611 12.5 8.49306 12.5972 7.77083 12.7917C7.04861 12.9861 6.36806 13.2778 5.72917 13.6667C5.65972 13.7222 5.60417 13.7759 5.5625 13.8276C5.52083 13.8793 5.5 13.9368 5.5 14V14.5ZM10.0044 8.5C10.4181 8.5 10.7708 8.35269 11.0625 8.05808C11.3542 7.76346 11.5 7.40929 11.5 6.99558C11.5 6.58186 11.3527 6.22917 11.0581 5.9375C10.7635 5.64583 10.4093 5.5 9.99558 5.5C9.58186 5.5 9.22917 5.64731 8.9375 5.94192C8.64583 6.23654 8.5 6.59071 8.5 7.00442C8.5 7.41814 8.64731 7.77083 8.94192 8.0625C9.23654 8.35417 9.59071 8.5 10.0044 8.5Z"
|
||||
fill="#1C1B1F"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -2,15 +2,21 @@ import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function PhoneIcon({ className, color, ...props }: IconProps) {
|
||||
export default function PhoneIcon({
|
||||
className,
|
||||
color,
|
||||
height = "24",
|
||||
width = "24",
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="24"
|
||||
height={height}
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -12,5 +12,6 @@ export { default as HouseIcon } from "./House"
|
||||
export { default as InfoCircleIcon } from "./InfoCircle"
|
||||
export { default as LocationIcon } from "./Location"
|
||||
export { default as LockIcon } from "./Lock"
|
||||
export { default as PersonIcon } from "./Person"
|
||||
export { default as PhoneIcon } from "./Phone"
|
||||
export { default as PlusCircleIcon } from "./PlusCircle"
|
||||
|
||||
@@ -2,25 +2,50 @@ import SectionContainer from "@/components/Section/Container"
|
||||
import Header from "@/components/Section/Header"
|
||||
import Card from "@/components/TempDesignSystem/Card"
|
||||
import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
|
||||
|
||||
import { CardsGridProps } from "@/types/components/loyalty/blocks"
|
||||
import { LoyaltyCardsGridEnum } from "@/types/components/loyalty/enums"
|
||||
|
||||
export default function CardsGrid({ cards_grid }: CardsGridProps) {
|
||||
export default function CardsGrid({
|
||||
cards_grid,
|
||||
firstItem = false,
|
||||
}: CardsGridProps) {
|
||||
return (
|
||||
<SectionContainer>
|
||||
<Header title={cards_grid.title} subtitle={cards_grid.preamble} />
|
||||
<Header
|
||||
title={cards_grid.title}
|
||||
subtitle={cards_grid.preamble}
|
||||
topTitle={firstItem}
|
||||
/>
|
||||
<Grids.Stackable>
|
||||
{cards_grid.cards.map((card) => (
|
||||
<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}
|
||||
primaryButton={card.primaryButton}
|
||||
/>
|
||||
))}
|
||||
{cards_grid.cards.map((card) => {
|
||||
switch (card.__typename) {
|
||||
case LoyaltyCardsGridEnum.LoyaltyCard:
|
||||
return (
|
||||
<LoyaltyCard
|
||||
key={card.system.uid}
|
||||
image={card.image}
|
||||
heading={card.heading}
|
||||
bodyText={card.body_text}
|
||||
link={card.link}
|
||||
/>
|
||||
)
|
||||
case LoyaltyCardsGridEnum.Card: {
|
||||
return (
|
||||
<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}
|
||||
primaryButton={card.primaryButton}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
})}
|
||||
</Grids.Stackable>
|
||||
</SectionContainer>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useParams } from "next/navigation"
|
||||
import { notFound, useParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
@@ -15,9 +15,8 @@ import {
|
||||
NewFriend,
|
||||
TrueFriend,
|
||||
} from "@/components/Levels"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import levelsData from "./data"
|
||||
@@ -33,28 +32,21 @@ export default function LoyaltyLevels() {
|
||||
|
||||
const { levels } = levelsData[lang]
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<div className={styles.cardContainer}>
|
||||
{levels.map((level: Level) => (
|
||||
<LevelCard
|
||||
key={level.level}
|
||||
formatMessage={formatMessage}
|
||||
lang={lang}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Button asChild intent="primary">
|
||||
<Link className={styles.link} href={`/${lang}/compare-all-levels`}>
|
||||
{formatMessage({ id: "Compare all levels" })}
|
||||
</Link>
|
||||
</Button>
|
||||
<section className={styles.cardContainer}>
|
||||
{levels.map((level: Level) => (
|
||||
<LevelCard
|
||||
key={level.level}
|
||||
formatMessage={formatMessage}
|
||||
lang={lang}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
|
||||
const pointsString = `${level.requiredPoints.toLocaleString(lang)}p`
|
||||
const pointsString = `${level.requiredPoints.toLocaleString(lang)} ${formatMessage({ id: "Points" })} `
|
||||
const qualifications = level.requiredNights
|
||||
? `${pointsString} ${formatMessage({ id: "or" })} ${level.requiredNights} ${formatMessage({ id: "nights" })}`
|
||||
: pointsString
|
||||
@@ -82,20 +74,41 @@ function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
|
||||
case 7:
|
||||
Level = BestFriend
|
||||
break
|
||||
default: {
|
||||
const loyaltyLevel = level.level as never
|
||||
console.error(`Unsupported loyalty level given: ${loyaltyLevel}`)
|
||||
notFound()
|
||||
}
|
||||
}
|
||||
return (
|
||||
<article className={styles.card}>
|
||||
<Title className={styles.levelHeading} level="h4">
|
||||
{level.level}
|
||||
<header>
|
||||
<BiroScript
|
||||
type="two"
|
||||
color="primaryLightOnSurfaceAccent"
|
||||
tilted="large"
|
||||
>
|
||||
{formatMessage({ id: "Level" })} {level.level}
|
||||
</BiroScript>
|
||||
<Level color="red" />
|
||||
</header>
|
||||
<Title textAlign="center" level="h5">
|
||||
{qualifications}
|
||||
</Title>
|
||||
{Level ? <Level color="primaryLightOnSurfaceAccent" /> : null}
|
||||
<div className={styles.textContainer}>
|
||||
<Body textTransform="bold">{qualifications}</Body>
|
||||
{level.benefits.map((benefit) => (
|
||||
<Body key={benefit.title} textAlign="center">
|
||||
<CheckIcon className={styles.checkIcon} />
|
||||
<Caption
|
||||
className={styles.levelText}
|
||||
key={benefit.title}
|
||||
textAlign="center"
|
||||
color="textMediumContrast"
|
||||
>
|
||||
<CheckIcon
|
||||
className={styles.checkIcon}
|
||||
color="primaryLightOnSurfaceAccent"
|
||||
/>
|
||||
{benefit.title}
|
||||
</Body>
|
||||
</Caption>
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
.container {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.cardContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
@@ -20,37 +15,34 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
align-content: flex-start;
|
||||
background-color: var(--UI-Grey-10);
|
||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
justify-content: center;
|
||||
min-height: 280px;
|
||||
justify-items: center;
|
||||
padding: var(--Spacing-x5) var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.levelHeading {
|
||||
color: var(--Scandic-Peach-70);
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
|
||||
.textContainer {
|
||||
align-content: flex-start;
|
||||
display: grid;
|
||||
align-content: flex-end;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.levelText {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.checkIcon {
|
||||
vertical-align: text-bottom;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.container {
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.cardContainer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
|
||||
@@ -43,6 +43,7 @@ export function Blocks({ blocks }: BlocksProps) {
|
||||
<CardsGrid
|
||||
cards_grid={block.cards_grid}
|
||||
key={`${block.cards_grid.title}-${idx}`}
|
||||
firstItem={firstItem}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.icon,
|
||||
.icon * {
|
||||
fill: var(--Scandic-Brand-Burgundy);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import { EmailIcon, PhoneIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import { getValueFromContactConfig } from "@/utils/contactConfig"
|
||||
|
||||
import styles from "./contactRow.module.css"
|
||||
@@ -27,15 +29,29 @@ export default async function ContactRow({ contact }: ContactRowProps) {
|
||||
Icon = PhoneIcon
|
||||
}
|
||||
|
||||
let openableLink = val
|
||||
if (contact.contact_field.includes("email")) {
|
||||
openableLink = `mailto:${val}`
|
||||
} else if (contact.contact_field.includes("phone")) {
|
||||
openableLink = `tel:${val}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{Icon ? <Icon className={styles.icon} /> : null}
|
||||
<Body color="burgundy" textAlign="center" textTransform="bold">
|
||||
<Body color="burgundy" textTransform="bold">
|
||||
{contact.display_text}
|
||||
</Body>
|
||||
<Body color="burgundy" textAlign="center">
|
||||
<Link
|
||||
className={styles.link}
|
||||
href={openableLink}
|
||||
variant="myPage"
|
||||
color="burgundy"
|
||||
size="small"
|
||||
>
|
||||
{Icon ? <Icon width="20" height="20" color="burgundy" /> : null}
|
||||
{val}
|
||||
</Body>
|
||||
</Link>
|
||||
<Footnote color="burgundy">{contact.footnote}</Footnote>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.contactContainer {
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--UI-Grey-30);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
gap: var(--Spacing-x2);
|
||||
justify-content: center;
|
||||
padding: var(--Spacing-x4) var(--Spacing-x2) var(--Spacing-x5);
|
||||
text-align: center;
|
||||
padding-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.contact {
|
||||
|
||||
@@ -12,9 +12,7 @@ export default async function Contact({ contactBlock }: ContactProps) {
|
||||
const { formatMessage } = await getIntl()
|
||||
return (
|
||||
<article className={styles.contactContainer}>
|
||||
<Subtitle textAlign="center">
|
||||
{formatMessage({ id: "Contact us" })}
|
||||
</Subtitle>
|
||||
<Subtitle>{formatMessage({ id: "Contact us" })}</Subtitle>
|
||||
<section className={styles.contact}>
|
||||
{contactBlock.map(({ contact, __typename }, i) => {
|
||||
switch (__typename) {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { login } from "@/constants/routes/handleAuth"
|
||||
|
||||
import { auth } from "@/auth"
|
||||
import ArrowRight from "@/components/Icons/ArrowRight"
|
||||
import { ScandicFriends } from "@/components/Levels"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
@@ -20,29 +21,42 @@ export default async function JoinLoyaltyContact({
|
||||
lang,
|
||||
}: JoinLoyaltyContactProps & LangParams) {
|
||||
const { formatMessage } = await getIntl()
|
||||
const session = await auth()
|
||||
|
||||
if (session) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<section>
|
||||
<article className={styles.wrapper}>
|
||||
<Title as="h4" level="h3">
|
||||
{block.title}
|
||||
</Title>
|
||||
<ScandicFriends color="primaryLightOnSurfaceAccent" />
|
||||
{block.preamble ? (
|
||||
<Body textAlign="center">{block.preamble}</Body>
|
||||
) : null}
|
||||
<Button asChild intent="primary">
|
||||
<Body asChild fontOnly textAlign="center" textTransform="bold">
|
||||
<Link href={login[lang]}>
|
||||
{formatMessage({ id: "Join Scandic Friends" })}
|
||||
</Link>
|
||||
</Body>
|
||||
</Button>
|
||||
<Footnote asChild fontOnly textAlign="center" textTransform="bold">
|
||||
<Link color="burgundy" href={`/${lang}/login`}>
|
||||
{formatMessage({ id: "Already a friend?" })} <br />
|
||||
{formatMessage({ id: "Click here to log in" })}
|
||||
<ScandicFriends color="red" />
|
||||
{block.preamble ? <Body>{block.preamble}</Body> : null}
|
||||
<Button asChild intent="primary" theme="base" className={styles.button}>
|
||||
<Link href={login[lang]} color="white">
|
||||
{formatMessage({ id: "Join Scandic Friends" })}
|
||||
</Link>
|
||||
</Footnote>
|
||||
</Button>
|
||||
<section className={styles.loginContainer}>
|
||||
<Body>{formatMessage({ id: "Already a friend?" })}</Body>
|
||||
<Link
|
||||
className={styles.link}
|
||||
color="burgundy"
|
||||
href={`/${lang}/login`}
|
||||
variant="icon"
|
||||
size="small"
|
||||
>
|
||||
<ArrowRight
|
||||
color="burgundy"
|
||||
className={styles.icon}
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
{formatMessage({ id: "Log in here" })}
|
||||
</Link>
|
||||
</section>
|
||||
</article>
|
||||
{block.contact ? <Contact contactBlock={block.contact} /> : null}
|
||||
</section>
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
.container {
|
||||
background-color: var(--Main-Grey-White);
|
||||
.wrapper {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
padding-bottom: var(--Spacing-x5);
|
||||
padding-top: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
.loginContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.button {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
padding: var(--Spacing-x4) var(--Spacing-x2) var(--Spacing-x5);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
14
components/Loyalty/Sidebar/MyPagesNavigation/index.tsx
Normal file
14
components/Loyalty/Sidebar/MyPagesNavigation/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { auth } from "@/auth"
|
||||
import MyPagesSidebar from "@/components/MyPages/Sidebar"
|
||||
|
||||
import { LangParams } from "@/types/params"
|
||||
|
||||
export default async function MyPagesNavigation({ lang }: LangParams) {
|
||||
const session = await auth()
|
||||
|
||||
if (!session) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <MyPagesSidebar lang={lang} />
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import JsonToHtml from "@/components/JsonToHtml"
|
||||
|
||||
import JoinLoyaltyContact from "./JoinLoyalty"
|
||||
import MyPagesNavigation from "./MyPagesNavigation"
|
||||
|
||||
import styles from "./sidebar.module.css"
|
||||
|
||||
import { SidebarTypenameEnum } from "@/types/components/loyalty/enums"
|
||||
import {
|
||||
LoyaltySidebarDynamicComponentEnum,
|
||||
SidebarTypenameEnum,
|
||||
} from "@/types/components/loyalty/enums"
|
||||
import { SidebarProps } from "@/types/components/loyalty/sidebar"
|
||||
import { LangParams } from "@/types/params"
|
||||
|
||||
@@ -36,6 +40,19 @@ export default function SidebarLoyalty({
|
||||
lang={lang}
|
||||
/>
|
||||
)
|
||||
case SidebarTypenameEnum.LoyaltyPageSidebarDynamicContent:
|
||||
switch (block.dynamic_content.component) {
|
||||
case LoyaltySidebarDynamicComponentEnum.my_pages_navigation:
|
||||
return (
|
||||
<MyPagesNavigation
|
||||
key={`${block.__typename}-${idx}`}
|
||||
lang={lang}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
.aside {
|
||||
align-content: flex-start;
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.content {
|
||||
padding: var(--Spacing-x0) var(--Spacing-x2);
|
||||
.content {
|
||||
padding: var(--Spacing-x0) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1366px) {
|
||||
.aside {
|
||||
align-content: flex-start;
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ChevronDownIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
import styles from "./button.module.css"
|
||||
@@ -17,11 +18,14 @@ export default function ShowMoreButton({
|
||||
<div className={styles.container}>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
intent="primary"
|
||||
onClick={loadMoreData}
|
||||
theme="secondaryDark"
|
||||
variant="icon"
|
||||
type="button"
|
||||
className={styles.button}
|
||||
theme="base"
|
||||
intent="text"
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
{formatMessage({ id: "Show more" })}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr min(50px);
|
||||
background-color: var(--Base-Surface-Primary-Normal);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
min-height: 250px;
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x3);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
min-height: 250px;
|
||||
padding: var(--Spacing-x0) var(--Spacing-x3);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.burgundyTitle {
|
||||
color: var(--Scandic-Brand-Burgundy);
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,35 @@
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { homeHrefs } from "@/constants/homeHrefs"
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import { ArrowRightIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./emptyUpcomingStays.module.css"
|
||||
|
||||
export default async function EmptyUpcomingStaysBlock() {
|
||||
import { LangParams } from "@/types/params"
|
||||
|
||||
export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
|
||||
const { formatMessage } = await getIntl()
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<Title as="h5" level="h3" color="red">
|
||||
{formatMessage({ id: "You have no upcoming stays." })}
|
||||
<span className={styles.burgundyTitle}>
|
||||
{" "}
|
||||
{formatMessage({ id: "Where should you go next?" })}
|
||||
</span>
|
||||
</Title>
|
||||
<Button asChild intent="primary" type="button">
|
||||
<Link href="#">{formatMessage({ id: "Get inspired" })}</Link>
|
||||
</Button>
|
||||
<div className={styles.titleContainer}>
|
||||
<Title as="h5" level="h3" color="red" className={styles.title}>
|
||||
{formatMessage({ id: "You have no upcoming stays." })}
|
||||
<span className={styles.burgundyTitle}>
|
||||
{formatMessage({ id: "Where should you go next?" })}
|
||||
</span>
|
||||
</Title>
|
||||
</div>
|
||||
<Link
|
||||
href={homeHrefs[env.NODE_ENV][lang]}
|
||||
className={styles.link}
|
||||
color="peach80"
|
||||
>
|
||||
{formatMessage({ id: "Get inspired" })}
|
||||
<ArrowRightIcon color="peach80" />
|
||||
</Link>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default async function SoonestStays({
|
||||
))}
|
||||
</Grids.Stackable>
|
||||
) : (
|
||||
<EmptyUpcomingStaysBlock />
|
||||
<EmptyUpcomingStaysBlock lang={lang} />
|
||||
)}
|
||||
</SectionContainer>
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ import type { StayCardProps } from "@/types/components/myPages/stays/stayCard"
|
||||
|
||||
export default function StayCard({ stay, lang }: StayCardProps) {
|
||||
const { checkinDate, checkoutDate, hotelInformation } = stay.attributes
|
||||
|
||||
const arrival = dt(checkinDate).locale(lang)
|
||||
const arrivalDate = arrival.format("DD MMM")
|
||||
const arrivalDateTime = arrival.format("YYYY-MM-DD")
|
||||
@@ -33,7 +32,7 @@ export default function StayCard({ stay, lang }: StayCardProps) {
|
||||
{hotelInformation.hotelName}
|
||||
</Title>
|
||||
<div className={styles.date}>
|
||||
<CalendarIcon color="burgundy" />
|
||||
<CalendarIcon color="burgundy" height={24} width={24} />
|
||||
<Caption asChild>
|
||||
<time dateTime={arrivalDateTime}>{arrivalDate}</time>
|
||||
</Caption>
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr min(50px);
|
||||
background-color: var(--Base-Surface-Primary-Normal);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
min-height: 250px;
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x3);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
min-height: 250px;
|
||||
padding: var(--Spacing-x0) var(--Spacing-x3);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.burgundyTitle {
|
||||
color: var(--Scandic-Brand-Burgundy);
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { homeHrefs } from "@/constants/homeHrefs"
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { ArrowRightIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
@@ -14,18 +14,22 @@ export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
|
||||
const { formatMessage } = await getIntl()
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<Title as="h5" level="h3" color="red">
|
||||
{formatMessage({ id: "You have no upcoming stays." })}
|
||||
<span className={styles.burgundyTitle}>
|
||||
{" "}
|
||||
{formatMessage({ id: "Where will you go next?" })}
|
||||
</span>
|
||||
</Title>
|
||||
<Button asChild intent="primary" type="button">
|
||||
<Link href={homeHrefs[env.NODE_ENV][lang]}>
|
||||
{formatMessage({ id: "Get inspired" })}
|
||||
</Link>
|
||||
</Button>
|
||||
<div className={styles.titleContainer}>
|
||||
<Title as="h5" level="h3" color="red" className={styles.title}>
|
||||
{formatMessage({ id: "You have no upcoming stays." })}
|
||||
<span className={styles.burgundyTitle}>
|
||||
{formatMessage({ id: "Where will you go next?" })}
|
||||
</span>
|
||||
</Title>
|
||||
</div>
|
||||
<Link
|
||||
href={homeHrefs[env.NODE_ENV][lang]}
|
||||
className={styles.link}
|
||||
color="peach80"
|
||||
>
|
||||
{formatMessage({ id: "Get inspired" })}
|
||||
<ArrowRightIcon color="peach80" />
|
||||
</Link>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function SectionHeader({
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<Title
|
||||
as={topTitle ? "h2" : "h3"}
|
||||
as={topTitle ? "h3" : "h4"}
|
||||
className={styles.title}
|
||||
level={topTitle ? "h1" : "h2"}
|
||||
>
|
||||
|
||||
@@ -41,6 +41,12 @@ a.inverted {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.text,
|
||||
a.text {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* VARIANTS */
|
||||
.default,
|
||||
a.default {
|
||||
@@ -50,7 +56,9 @@ a.default {
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
/* SIZES */
|
||||
@@ -97,6 +105,25 @@ a.default {
|
||||
color: var(--Base-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.basePrimary svg,
|
||||
.icon.basePrimary svg * {
|
||||
fill: var(--Base-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.basePrimary:active svg,
|
||||
.icon.basePrimary:focus svg,
|
||||
.icon.basePrimary:hover svg,
|
||||
.icon.basePrimary:active svg *,
|
||||
.icon.basePrimary:focus svg *,
|
||||
.icon.basePrimary:hover svg * {
|
||||
fill: var(--Base-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.basePrimary:disabled *,
|
||||
.icon.basePrimary:disabled svg * {
|
||||
fill: var(--Base-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.baseSecondary {
|
||||
background-color: var(--Base-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Base-Button-Secondary-Border-Normal);
|
||||
@@ -117,6 +144,25 @@ a.default {
|
||||
color: var(--Base-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.baseSecondary svg,
|
||||
.icon.baseSecondary svg * {
|
||||
fill: var(--Base-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.baseSecondary:active svg,
|
||||
.icon.baseSecondary:focus svg,
|
||||
.icon.baseSecondary:hover svg,
|
||||
.icon.baseSecondary:active svg *,
|
||||
.icon.baseSecondary:focus svg *,
|
||||
.icon.baseSecondary:hover svg * {
|
||||
fill: var(--Base-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.baseSecondary:disabled svg,
|
||||
.icon.baseSecondary:disabled svg * {
|
||||
fill: var(--Base-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.baseTertiary {
|
||||
background-color: var(--Base-Button-Tertiary-Fill-Normal);
|
||||
color: var(--Base-Button-Tertiary-On-Fill-Normal);
|
||||
@@ -134,6 +180,25 @@ a.default {
|
||||
color: var(--Base-Button-Tertiary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.baseTertiary svg,
|
||||
.icon.baseTertiary svg * {
|
||||
fill: var(--Base-Button-Tertiary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.baseTertiary:active svg,
|
||||
.icon.baseTertiary:focus svg,
|
||||
.icon.baseTertiary:hover svg,
|
||||
.icon.baseTertiary:active svg *,
|
||||
.icon.baseTertiary:focus svg *,
|
||||
.icon.baseTertiary:hover svg * {
|
||||
fill: var(--Base-Button-Tertiary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.baseTertiary:disabled svg,
|
||||
.icon.baseTertiary:disabled svg * {
|
||||
fill: var(--Base-Button-Tertiary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.baseInverted {
|
||||
background-color: var(--Base-Button-Inverted-Fill-Normal);
|
||||
color: var(--Base-Button-Inverted-On-Fill-Normal);
|
||||
@@ -151,6 +216,60 @@ a.default {
|
||||
color: var(--Base-Button-Inverted-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.baseInverted svg,
|
||||
.icon.baseInverted svg * {
|
||||
fill: var(--Base-Button-Inverted-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.baseInverted:active svg,
|
||||
.icon.baseInverted:focus svg,
|
||||
.icon.baseInverted:hover svg,
|
||||
.icon.baseInverted:active svg *,
|
||||
.icon.baseInverted:focus svg *,
|
||||
.icon.baseInverted:hover svg * {
|
||||
fill: var(--Base-Button-Inverted-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.baseInverted:disabled svg,
|
||||
.icon.baseInverted:disabled svg * {
|
||||
fill: var(--Base-Button-Inverted-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.baseText {
|
||||
color: var(--Base-Button-Text-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.baseText:active,
|
||||
.baseText:focus,
|
||||
.baseText:hover {
|
||||
color: var(--Base-Button-Text-On-Fill-Hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.baseText:disabled {
|
||||
color: var(--Base-Button-Text-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.baseText svg,
|
||||
.icon.baseText svg * {
|
||||
fill: var(--Base-Button-Text-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.baseText:active svg,
|
||||
.icon.baseText:focus svg,
|
||||
.icon.baseText:hover svg,
|
||||
.icon.baseText:active svg *,
|
||||
.icon.baseText:focus svg *,
|
||||
.icon.baseText:hover svg * {
|
||||
fill: var(--Base-Button-Text-On-Fill-Hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.icon.baseText:disabled svg,
|
||||
.icon.baseText:disabled svg * {
|
||||
fill: var(--Base-Button-Text-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryStrongPrimary {
|
||||
background-color: var(--Theme-Primary-Strong-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Normal);
|
||||
@@ -168,6 +287,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryStrongPrimary svg,
|
||||
.icon.primaryStrongPrimary svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryStrongPrimary:active svg,
|
||||
.icon.primaryStrongPrimary:focus svg,
|
||||
.icon.primaryStrongPrimary:hover svg,
|
||||
.icon.primaryStrongPrimary:active svg *,
|
||||
.icon.primaryStrongPrimary:focus svg *,
|
||||
.icon.primaryStrongPrimary:hover svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryStrongPrimary:disabled svg,
|
||||
.icon.primaryStrongPrimary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryStrongSecondary {
|
||||
background-color: var(--Theme-Primary-Strong-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Primary-Strong-Button-Secondary-Border-Normal);
|
||||
@@ -188,6 +326,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryStrongSecondary svg,
|
||||
.icon.primaryStrongSecondary svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryStrongSecondary:active svg,
|
||||
.icon.primaryStrongSecondary:focus svg,
|
||||
.icon.primaryStrongSecondary:hover svg,
|
||||
.icon.primaryStrongSecondary:active svg *,
|
||||
.icon.primaryStrongSecondary:focus svg *,
|
||||
.icon.primaryStrongSecondary:hover svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryStrongSecondary:disabled svg,
|
||||
.icon.primaryStrongSecondary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Strong-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryDarkPrimary {
|
||||
background-color: var(--Theme-Primary-Dark-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Normal);
|
||||
@@ -205,6 +362,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryDarkPrimary svg,
|
||||
.icon.primaryDarkPrimary svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryDarkPrimary:active svg,
|
||||
.icon.primaryDarkPrimary:focus svg,
|
||||
.icon.primaryDarkPrimary:hover svg,
|
||||
.icon.primaryDarkPrimary:active svg *,
|
||||
.icon.primaryDarkPrimary:focus svg *,
|
||||
.icon.primaryDarkPrimary:hover svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryDarkPrimary:disabled svg,
|
||||
.icon.primaryDarkPrimary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryDarkSecondary {
|
||||
background-color: var(--Theme-Primary-Dark-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Primary-Dark-Button-Secondary-Border-Normal);
|
||||
@@ -225,6 +401,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryDarkSecondary svg,
|
||||
.icon.primaryDarkSecondary svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryDarkSecondary:active svg,
|
||||
.icon.primaryDarkSecondary:focus svg,
|
||||
.icon.primaryDarkSecondary:hover svg,
|
||||
.icon.primaryDarkSecondary:active svg *,
|
||||
.icon.primaryDarkSecondary:focus svg *,
|
||||
.icon.primaryDarkSecondary:hover svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryDarkSecondary:disabled svg,
|
||||
.icon.primaryDarkSecondary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryLightPrimary {
|
||||
background-color: var(--Theme-Primary-Light-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Normal);
|
||||
@@ -242,6 +437,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryLightPrimary svg,
|
||||
.icon.primaryLightPrimary svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryLightPrimary:active svg,
|
||||
.icon.primaryLightPrimary:focus svg,
|
||||
.icon.primaryLightPrimary:hover svg,
|
||||
.icon.primaryLightPrimary:active svg *,
|
||||
.icon.primaryLightPrimary:focus svg *,
|
||||
.icon.primaryLightPrimary:hover svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryLightPrimary:disabled svg,
|
||||
.icon.primaryLightPrimary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.primaryLightSecondary {
|
||||
background-color: var(--Theme-Primary-Light-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Primary-Light-Button-Secondary-Border-Normal);
|
||||
@@ -262,6 +476,25 @@ a.default {
|
||||
color: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.primaryLightSecondary svg,
|
||||
.icon.primaryLightSecondary svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.primaryLightSecondary:active svg,
|
||||
.icon.primaryLightSecondary:focus svg,
|
||||
.icon.primaryLightSecondary:hover svg,
|
||||
.icon.primaryLightSecondary:active svg *,
|
||||
.icon.primaryLightSecondary:focus svg *,
|
||||
.icon.primaryLightSecondary:hover svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.primaryLightSecondary:disabled svg,
|
||||
.icon.primaryLightSecondary:disabled svg * {
|
||||
fill: var(--Theme-Primary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.secondaryDarkPrimary {
|
||||
background-color: var(--Theme-Secondary-Dark-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Normal);
|
||||
@@ -279,6 +512,25 @@ a.default {
|
||||
color: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkPrimary svg,
|
||||
.icon.secondaryDarkPrimary svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkPrimary:active svg,
|
||||
.icon.secondaryDarkPrimary:focus svg,
|
||||
.icon.secondaryDarkPrimary:hover svg,
|
||||
.icon.secondaryDarkPrimary:active svg *,
|
||||
.icon.secondaryDarkPrimary:focus svg *,
|
||||
.icon.secondaryDarkPrimary:hover svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkPrimary:disabled svg,
|
||||
.icon.secondaryDarkPrimary:disabled svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.secondaryDarkSecondary {
|
||||
background-color: var(--Theme-Secondary-Dark-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Secondary-Dark-Button-Secondary-Border-Normal);
|
||||
@@ -299,6 +551,25 @@ a.default {
|
||||
color: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkSecondary svg,
|
||||
.icon.secondaryDarkSecondary svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkSecondary:active svg,
|
||||
.icon.secondaryDarkSecondary:focus svg,
|
||||
.icon.secondaryDarkSecondary:hover svg,
|
||||
.icon.secondaryDarkSecondary:active svg *,
|
||||
.icon.secondaryDarkSecondary:focus svg *,
|
||||
.icon.secondaryDarkSecondary:hover svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.secondaryDarkSecondary:disabled svg,
|
||||
.icon.secondaryDarkSecondary:disabled svg * {
|
||||
fill: var(--Theme-Secondary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.secondaryLightPrimary {
|
||||
background-color: var(--Theme-Secondary-Light-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Normal);
|
||||
@@ -316,6 +587,25 @@ a.default {
|
||||
color: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.secondaryLightPrimary svg,
|
||||
.icon.secondaryLightPrimary svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.secondaryLightPrimary:active svg,
|
||||
.icon.secondaryLightPrimary:focus svg,
|
||||
.icon.secondaryLightPrimary:hover svg,
|
||||
.icon.secondaryLightPrimary:active svg *,
|
||||
.icon.secondaryLightPrimary:focus svg *,
|
||||
.icon.secondaryLightPrimary:hover svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.secondaryLightPrimary:disabled svg,
|
||||
.icon.secondaryLightPrimary:disabled svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.secondaryLightSecondary {
|
||||
background-color: var(--Theme-Secondary-Light-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Secondary-Light-Button-Secondary-Border-Normal);
|
||||
@@ -336,6 +626,25 @@ a.default {
|
||||
color: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.secondaryLightSecondary svg,
|
||||
.icon.secondaryLightSecondary svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.secondaryLightSecondary:active svg,
|
||||
.icon.secondaryLightSecondary:focus svg,
|
||||
.icon.secondaryLightSecondary:hover svg,
|
||||
.icon.secondaryLightSecondary:active svg *,
|
||||
.icon.secondaryLightSecondary:focus svg *,
|
||||
.icon.secondaryLightSecondary:hover svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.secondaryLightSecondary:disabled svg,
|
||||
.icon.secondaryLightSecondary:disabled svg * {
|
||||
fill: var(--Theme-Secondary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.tertiaryDarkPrimary {
|
||||
background-color: var(--Theme-Tertiary-Dark-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Normal);
|
||||
@@ -353,6 +662,25 @@ a.default {
|
||||
color: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkPrimary svg,
|
||||
.icon.tertiaryDarkPrimary svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkPrimary:active svg,
|
||||
.icon.tertiaryDarkPrimary:focus svg,
|
||||
.icon.tertiaryDarkPrimary:hover svg,
|
||||
.icon.tertiaryDarkPrimary:active svg *,
|
||||
.icon.tertiaryDarkPrimary:focus svg *,
|
||||
.icon.tertiaryDarkPrimary:hover svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkPrimary:disabled svg,
|
||||
.icon.tertiaryDarkPrimary:disabled svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.tertiaryDarkSecondary {
|
||||
background-color: var(--Theme-Tertiary-Dark-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Theme-Tertiary-Dark-Button-Secondary-Border-Normal);
|
||||
@@ -373,6 +701,25 @@ a.default {
|
||||
color: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkSecondary svg,
|
||||
.icon.tertiaryDarkSecondary svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkSecondary:active svg,
|
||||
.icon.tertiaryDarkSecondary:focus svg,
|
||||
.icon.tertiaryDarkSecondary:hover svg,
|
||||
.icon.tertiaryDarkSecondary:active svg *,
|
||||
.icon.tertiaryDarkSecondary:focus svg *,
|
||||
.icon.tertiaryDarkSecondary:hover svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.tertiaryDarkSecondary:disabled svg,
|
||||
.icon.tertiaryDarkSecondary:disabled svg * {
|
||||
fill: var(--Theme-Tertiary-Dark-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.tertiaryLightPrimary {
|
||||
background-color: var(--Theme-Tertiary-Light-Button-Primary-Fill-Normal);
|
||||
color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Normal);
|
||||
@@ -390,6 +737,25 @@ a.default {
|
||||
color: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightPrimary svg,
|
||||
.icon.tertiaryLightPrimary svg * {
|
||||
fill: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightPrimary:active svg,
|
||||
.icon.tertiaryLightPrimary:focus svg,
|
||||
.icon.tertiaryLightPrimary:hover svg,
|
||||
.icon.tertiaryLightPrimary:active svg *,
|
||||
.icon.tertiaryLightPrimary:focus svg *,
|
||||
.icon.tertiaryLightPrimary:hover svg * {
|
||||
fill: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightPrimary:disabled svg,
|
||||
.icon.tertiaryLightPrimary:disabled svg * {
|
||||
fill: var(--Theme-Tertiary-Light-Button-Primary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.tertiaryLightSecondary {
|
||||
background-color: var(--Tertiary-Light-Button-Secondary-Fill-Normal);
|
||||
border-color: var(--Tertiary-Light-Button-Secondary-Border-Normal);
|
||||
@@ -409,3 +775,22 @@ a.default {
|
||||
border-color: var(--Tertiary-Light-Button-Secondary-Border-Disabled);
|
||||
color: var(--Tertiary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightSecondary svg,
|
||||
.icon.tertiaryLightSecondary svg * {
|
||||
fill: var(--Tertiary-Light-Button-Secondary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightSecondary:active svg,
|
||||
.icon.tertiaryLightSecondary:focus svg,
|
||||
.icon.tertiaryLightSecondary:hover svg,
|
||||
.icon.tertiaryLightSecondary:active svg *,
|
||||
.icon.tertiaryLightSecondary:focus svg *,
|
||||
.icon.tertiaryLightSecondary:hover svg * {
|
||||
fill: var(--Tertiary-Light-Button-Secondary-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.icon.tertiaryLightSecondary:disabled svg,
|
||||
.icon.tertiaryLightSecondary:disabled svg * {
|
||||
fill: var(--Tertiary-Light-Button-Secondary-On-Fill-Disabled);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export const buttonVariants = cva(styles.btn, {
|
||||
primary: styles.primary,
|
||||
secondary: styles.secondary,
|
||||
tertiary: styles.tertiary,
|
||||
text: styles.text,
|
||||
},
|
||||
size: {
|
||||
small: styles.small,
|
||||
@@ -128,5 +129,10 @@ export const buttonVariants = cva(styles.btn, {
|
||||
intent: "secondary",
|
||||
theme: "tertiaryLight",
|
||||
},
|
||||
{
|
||||
className: styles.baseText,
|
||||
intent: "text",
|
||||
theme: "base",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
@@ -47,7 +46,6 @@ export default function Card({
|
||||
<BiroScript className={styles.scriptedTitle} type="two">
|
||||
{scriptedTopTitle}
|
||||
</BiroScript>
|
||||
<Divider />
|
||||
</section>
|
||||
) : null}
|
||||
<Title as="h5" className={styles.heading} level="h3">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
.icon {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.myPage {
|
||||
@@ -102,6 +102,10 @@
|
||||
color: var(--Scandic-Peach-80);
|
||||
}
|
||||
|
||||
.white {
|
||||
color: var(--Base-Button-Primary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
.regular {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
font-size: var(--typography-Body-Regular-fontSize);
|
||||
|
||||
@@ -13,6 +13,7 @@ export const linkVariants = cva(styles.link, {
|
||||
none: "",
|
||||
pale: styles.pale,
|
||||
peach80: styles.peach80,
|
||||
white: styles.white,
|
||||
},
|
||||
size: {
|
||||
small: styles.small,
|
||||
|
||||
66
components/TempDesignSystem/LoyaltyCard/index.tsx
Normal file
66
components/TempDesignSystem/LoyaltyCard/index.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import ArrowRight from "@/components/Icons/ArrowRight"
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import { loyaltyCardVariants } from "./variants"
|
||||
|
||||
import styles from "./loyaltyCard.module.css"
|
||||
|
||||
import type { LoyaltyCardProps } from "./loyaltyCard"
|
||||
|
||||
export default function LoyaltyCard({
|
||||
link,
|
||||
image,
|
||||
heading,
|
||||
bodyText,
|
||||
theme = "white",
|
||||
className,
|
||||
}: LoyaltyCardProps) {
|
||||
return (
|
||||
<article
|
||||
className={loyaltyCardVariants({
|
||||
className,
|
||||
theme,
|
||||
})}
|
||||
>
|
||||
{image ? (
|
||||
<Image
|
||||
src={image.url}
|
||||
width={180}
|
||||
height={160}
|
||||
className={styles.image}
|
||||
alt={image.meta.alt || image.title}
|
||||
/>
|
||||
) : null}
|
||||
<Title as="h5" level="h3" textAlign="center">
|
||||
{heading}
|
||||
</Title>
|
||||
{bodyText ? (
|
||||
<Body textAlign="center" color="red">
|
||||
{bodyText}
|
||||
</Body>
|
||||
) : null}
|
||||
<div className={styles.buttonContainer}>
|
||||
{link ? (
|
||||
<Link
|
||||
className={styles.link}
|
||||
color="burgundy"
|
||||
href={link.href}
|
||||
target={link.openInNewTab ? "_blank" : undefined}
|
||||
variant="myPage"
|
||||
>
|
||||
<ArrowRight
|
||||
color="burgundy"
|
||||
className={styles.icon}
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
{link.title}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
.container {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
gap: var(--Spacing-x2);
|
||||
height: 480px;
|
||||
justify-content: space-between;
|
||||
margin-right: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x4) var(--Spacing-x3);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: contain;
|
||||
height: 160px;
|
||||
width: auto;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.white {
|
||||
background-color: var(--Main-Grey-White);
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-self: center;
|
||||
}
|
||||
20
components/TempDesignSystem/LoyaltyCard/loyaltyCard.ts
Normal file
20
components/TempDesignSystem/LoyaltyCard/loyaltyCard.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { loyaltyCardVariants } from "./variants"
|
||||
|
||||
import type { VariantProps } from "class-variance-authority"
|
||||
|
||||
import { ImageVaultAsset } from "@/types/components/imageVaultImage"
|
||||
|
||||
export interface LoyaltyCardProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof loyaltyCardVariants> {
|
||||
link?: {
|
||||
href: string
|
||||
title: string
|
||||
openInNewTab?: boolean
|
||||
isExternal: boolean
|
||||
}
|
||||
image?: ImageVaultAsset
|
||||
heading?: string | null
|
||||
bodyText?: string | null
|
||||
backgroundImage?: { url: string }
|
||||
}
|
||||
14
components/TempDesignSystem/LoyaltyCard/variants.ts
Normal file
14
components/TempDesignSystem/LoyaltyCard/variants.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./loyaltyCard.module.css"
|
||||
|
||||
export const loyaltyCardVariants = cva(styles.container, {
|
||||
variants: {
|
||||
theme: {
|
||||
white: styles.white,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
theme: "white",
|
||||
},
|
||||
})
|
||||
@@ -26,6 +26,14 @@
|
||||
line-height: var(--typography-Script-2-lineHeight);
|
||||
}
|
||||
|
||||
.tiltedSmall {
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
|
||||
.tiltedLarge {
|
||||
transform: rotate(-13deg) translate(0px, -15px);
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export default function BiroScript({
|
||||
color,
|
||||
textAlign,
|
||||
type,
|
||||
tilted,
|
||||
...props
|
||||
}: BiroScriptProps) {
|
||||
const Comp = asChild ? Slot : "span"
|
||||
@@ -18,6 +19,7 @@ export default function BiroScript({
|
||||
color,
|
||||
textAlign,
|
||||
type,
|
||||
tilted,
|
||||
})
|
||||
return <Comp className={classNames} {...props} />
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ const config = {
|
||||
one: styles.one,
|
||||
two: styles.two,
|
||||
},
|
||||
tilted: {
|
||||
small: styles.tiltedSmall,
|
||||
large: styles.tiltedLarge,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
type: "one",
|
||||
|
||||
@@ -36,3 +36,15 @@
|
||||
.pale {
|
||||
color: var(--Scandic-Brand-Pale-Peach);
|
||||
}
|
||||
|
||||
.textMediumContrast {
|
||||
color: var(--Base-Text-UI-Medium-contrast);
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -9,19 +9,21 @@ export default function Caption({
|
||||
className = "",
|
||||
color,
|
||||
fontOnly = false,
|
||||
textAlign,
|
||||
textTransform,
|
||||
...props
|
||||
}: CaptionProps) {
|
||||
const Comp = asChild ? Slot : "p"
|
||||
const classNames = fontOnly
|
||||
? fontOnlycaptionVariants({
|
||||
className,
|
||||
textTransform,
|
||||
})
|
||||
className,
|
||||
textTransform,
|
||||
})
|
||||
: captionVariants({
|
||||
className,
|
||||
color,
|
||||
textTransform,
|
||||
})
|
||||
className,
|
||||
color,
|
||||
textTransform,
|
||||
textAlign,
|
||||
})
|
||||
return <Comp className={classNames} {...props} />
|
||||
}
|
||||
|
||||
@@ -8,11 +8,16 @@ const config = {
|
||||
black: styles.black,
|
||||
burgundy: styles.burgundy,
|
||||
pale: styles.pale,
|
||||
textMediumContrast: styles.textMediumContrast,
|
||||
},
|
||||
textTransform: {
|
||||
bold: styles.bold,
|
||||
regular: styles.regular,
|
||||
},
|
||||
textAlign: {
|
||||
center: styles.center,
|
||||
left: styles.left,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "black",
|
||||
|
||||
@@ -43,4 +43,8 @@
|
||||
|
||||
.pale {
|
||||
color: var(--Scandic-Brand-Pale-Peach);
|
||||
}
|
||||
}
|
||||
|
||||
.textMediumContrast {
|
||||
color: var(--Base-Text-UI-Medium-contrast);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const config = {
|
||||
black: styles.black,
|
||||
burgundy: styles.burgundy,
|
||||
pale: styles.pale,
|
||||
textMediumContrast: styles.textMediumContrast,
|
||||
},
|
||||
textAlign: {
|
||||
center: styles.center,
|
||||
|
||||
5
env/server.ts
vendored
5
env/server.ts
vendored
@@ -9,7 +9,9 @@ export const env = createEnv({
|
||||
*/
|
||||
isServer: typeof window === "undefined" || "Deno" in window,
|
||||
server: {
|
||||
APPLICATION_INSIGHTS_CONNECTION_STRING: z.string().optional(),
|
||||
ADOBE_SCRIPT_SRC: z.string().optional(),
|
||||
ADOBE_SDK_SCRIPT_SRC: z.string().optional(),
|
||||
API_BASEURL: z.string(),
|
||||
BUILD_ID: z.string().default("64rYXBu8o2eHp0Jf"),
|
||||
CMS_ACCESS_TOKEN: z.string(),
|
||||
@@ -58,7 +60,10 @@ export const env = createEnv({
|
||||
},
|
||||
emptyStringAsUndefined: true,
|
||||
runtimeEnv: {
|
||||
APPLICATION_INSIGHTS_CONNECTION_STRING:
|
||||
process.env.APPLICATION_INSIGHTS_CONNECTION_STRING,
|
||||
ADOBE_SCRIPT_SRC: process.env.ADOBE_SCRIPT_SRC,
|
||||
ADOBE_SDK_SCRIPT_SRC: process.env.ADOBE_SDK_SCRIPT_SRC,
|
||||
API_BASEURL: process.env.API_BASEURL,
|
||||
BUILD_ID: process.env.BUILD_ID,
|
||||
CMS_ACCESS_TOKEN: process.env.CMS_ACCESS_TOKEN,
|
||||
|
||||
21
instrumentation.ts
Normal file
21
instrumentation.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { env } from "./env/server"
|
||||
|
||||
export async function register() {
|
||||
if (
|
||||
process.env.NEXT_RUNTIME === "nodejs" &&
|
||||
env.APPLICATION_INSIGHTS_CONNECTION_STRING
|
||||
) {
|
||||
const { AzureMonitorTraceExporter } = await import(
|
||||
"@azure/monitor-opentelemetry-exporter"
|
||||
)
|
||||
const { registerOTel } = await import("@vercel/otel")
|
||||
|
||||
const connectionString = env.APPLICATION_INSIGHTS_CONNECTION_STRING
|
||||
const traceExporter = new AzureMonitorTraceExporter({ connectionString })
|
||||
|
||||
registerOTel({
|
||||
serviceName: "scandic-web",
|
||||
traceExporter,
|
||||
})
|
||||
}
|
||||
}
|
||||
28
lib/graphql/Fragments/Blocks/LoyaltyCard.graphql
Normal file
28
lib/graphql/Fragments/Blocks/LoyaltyCard.graphql
Normal file
@@ -0,0 +1,28 @@
|
||||
fragment LoyaltyCardBlock on LoyaltyCard {
|
||||
heading
|
||||
body_text
|
||||
image
|
||||
title
|
||||
link {
|
||||
cta_text
|
||||
open_in_new_tab
|
||||
external_link {
|
||||
title
|
||||
href
|
||||
}
|
||||
linkConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...LoyaltyPageLink
|
||||
...ContentPageLink
|
||||
...AccountPageLink
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system {
|
||||
locale
|
||||
uid
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
fragment CardBlockRef on Card {
|
||||
__typename
|
||||
secondary_button {
|
||||
linkConnection {
|
||||
edges {
|
||||
|
||||
18
lib/graphql/Fragments/Blocks/Refs/LoyaltyCard.graphql
Normal file
18
lib/graphql/Fragments/Blocks/Refs/LoyaltyCard.graphql
Normal file
@@ -0,0 +1,18 @@
|
||||
fragment LoyaltyCardBlockRef on LoyaltyCard {
|
||||
__typename
|
||||
link {
|
||||
linkConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...LoyaltyPageRef
|
||||
...ContentPageRef
|
||||
...AccountPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
#import "../Fragments/Image.graphql"
|
||||
#import "../Fragments/Blocks/Card.graphql"
|
||||
#import "../Fragments/Blocks/LoyaltyCard.graphql"
|
||||
|
||||
#import "../Fragments/Blocks/Refs/Card.graphql"
|
||||
#import "../Fragments/Blocks/Refs/LoyaltyCard.graphql"
|
||||
|
||||
#import "../Fragments/LoyaltyPage/Breadcrumbs.graphql"
|
||||
#import "../Fragments/PageLink/AccountPageLink.graphql"
|
||||
#import "../Fragments/PageLink/ContentPageLink.graphql"
|
||||
@@ -83,7 +87,9 @@ query GetLoyaltyPage($locale: String!, $uid: String!) {
|
||||
cardConnection(limit: 10) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...CardBlock
|
||||
...LoyaltyCardBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,6 +100,11 @@ query GetLoyaltyPage($locale: String!, $uid: String!) {
|
||||
heading
|
||||
sidebar {
|
||||
__typename
|
||||
... on LoyaltyPageSidebarDynamicContent {
|
||||
dynamic_content {
|
||||
component
|
||||
}
|
||||
}
|
||||
... on LoyaltyPageSidebarJoinLoyaltyContact {
|
||||
join_loyalty_contact {
|
||||
title
|
||||
@@ -104,6 +115,7 @@ query GetLoyaltyPage($locale: String!, $uid: String!) {
|
||||
contact {
|
||||
display_text
|
||||
contact_field
|
||||
footnote
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,6 +218,7 @@ query GetLoyaltyPageRefs($locale: String!, $uid: String!) {
|
||||
edges {
|
||||
node {
|
||||
...CardBlockRef
|
||||
...LoyaltyCardBlockRef
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const nextConfig = {
|
||||
eslint: { ignoreDuringBuilds: true },
|
||||
trailingSlash: false,
|
||||
experimental: {
|
||||
instrumentationHook: true,
|
||||
serverActions: {
|
||||
allowedOrigins: [
|
||||
"*--web-scandic-hotels.netlify.app",
|
||||
|
||||
370
package-lock.json
generated
370
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.1.0",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.24",
|
||||
"@contentstack/live-preview-utils": "^1.4.0",
|
||||
"@hookform/error-message": "^2.0.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
@@ -20,6 +21,7 @@
|
||||
"@trpc/client": "^11.0.0-next-beta.318",
|
||||
"@trpc/react-query": "^11.0.0-next-beta.318",
|
||||
"@trpc/server": "^11.0.0-next-beta.318",
|
||||
"@vercel/otel": "^1.9.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clean-deep": "^3.4.0",
|
||||
"dayjs": "^1.11.10",
|
||||
@@ -130,6 +132,156 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-auth": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz",
|
||||
"integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-client": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz",
|
||||
"integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-rest-pipeline": "^1.9.1",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"@azure/core-util": "^1.6.1",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.1.tgz",
|
||||
"integrity": "sha512-ExPSbgjwCoht6kB7B4MeZoBAxcQSIl29r/bPeazZJx50ej4JJCByimLOrZoIsurISNyJQQHf30b3JfqC3Hb88A==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.9.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/agent-base": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
|
||||
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.0.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-tracing": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz",
|
||||
"integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz",
|
||||
"integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/logger": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.2.tgz",
|
||||
"integrity": "sha512-l170uE7bsKpIU6B/giRc9i4NI0Mj+tANMMMxf7Zi/5cKzEqPayP7+X1WPrG7e+91JgY8N+7K7nF2WOi7iVhXvg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/monitor-opentelemetry-exporter": {
|
||||
"version": "1.0.0-beta.24",
|
||||
"resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry-exporter/-/monitor-opentelemetry-exporter-1.0.0-beta.24.tgz",
|
||||
"integrity": "sha512-oEYmQxfa40Rcqh358GeVd9MPQd9dHOxQJBsH1BwXW1aZ4cd9QwylEzAMrLvwJiHmaq0g4CzPnbtlpwxfb758Qg==",
|
||||
"dependencies": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-client": "^1.0.0",
|
||||
"@azure/core-rest-pipeline": "^1.1.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.52.0",
|
||||
"@opentelemetry/core": "^1.25.0",
|
||||
"@opentelemetry/resources": "^1.25.0",
|
||||
"@opentelemetry/sdk-logs": "^0.52.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.25.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.25.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.25.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
|
||||
@@ -3396,6 +3548,130 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api-logs": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz",
|
||||
"integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/core": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz",
|
||||
"integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz",
|
||||
"integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@types/shimmer": "^1.0.2",
|
||||
"import-in-the-middle": "^1.8.1",
|
||||
"require-in-the-middle": "^7.1.1",
|
||||
"semver": "^7.5.2",
|
||||
"shimmer": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/resources": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz",
|
||||
"integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-logs": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz",
|
||||
"integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.4.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-metrics": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz",
|
||||
"integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"lodash.merge": "^4.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-trace-base": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz",
|
||||
"integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz",
|
||||
"integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@panva/hkdf": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
|
||||
@@ -5868,6 +6144,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/shimmer": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz",
|
||||
"integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/sinonjs__fake-timers": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
|
||||
@@ -6050,6 +6332,23 @@
|
||||
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vercel/otel": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/otel/-/otel-1.9.1.tgz",
|
||||
"integrity": "sha512-ZSTqgvd+w/lcB1nxEW8EHSBBqd4ZdeJ1t5op1CFo/nKFdG/EshwMon0qKc2ZxVEZXOZJI/x+LKf8Y5/Y/VHqEA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"@opentelemetry/api-logs": ">=0.46.0 && <1.0.0",
|
||||
"@opentelemetry/instrumentation": ">=0.46.0 && <1.0.0",
|
||||
"@opentelemetry/resources": "^1.19.0",
|
||||
"@opentelemetry/sdk-logs": ">=0.46.0 && <1.0.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.19.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
@@ -6074,7 +6373,6 @@
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -6092,6 +6390,15 @@
|
||||
"acorn-walk": "^8.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-attributes": {
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
|
||||
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
@@ -7236,8 +7543,7 @@
|
||||
"node_modules/cjs-module-lexer": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
|
||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q=="
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.0",
|
||||
@@ -8104,7 +8410,6 @@
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@@ -9661,7 +9966,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -10108,7 +10412,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@@ -10340,6 +10643,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/import-in-the-middle": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.8.1.tgz",
|
||||
"integrity": "sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.2",
|
||||
"acorn-import-attributes": "^1.9.5",
|
||||
"cjs-module-lexer": "^1.2.2",
|
||||
"module-details-from-path": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/import-local": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
|
||||
@@ -10543,7 +10858,6 @@
|
||||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
@@ -13262,8 +13576,7 @@
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
@@ -13820,6 +14133,12 @@
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/module-details-from-path": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
|
||||
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
@@ -13871,8 +14190,7 @@
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/mustache": {
|
||||
"version": "4.2.0",
|
||||
@@ -14594,8 +14912,7 @@
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.2",
|
||||
@@ -15635,6 +15952,20 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-in-the-middle": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz",
|
||||
"integrity": "sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"module-details-from-path": "^1.0.3",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -15651,7 +15982,6 @@
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.13.0",
|
||||
"path-parse": "^1.0.7",
|
||||
@@ -15978,7 +16308,6 @@
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@@ -15993,7 +16322,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -16134,6 +16462,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shimmer": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
|
||||
"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
@@ -16759,7 +17093,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -17981,8 +18314,7 @@
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.3.4",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"test:unit:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.24",
|
||||
"@contentstack/live-preview-utils": "^1.4.0",
|
||||
"@hookform/error-message": "^2.0.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
@@ -36,6 +37,7 @@
|
||||
"@trpc/client": "^11.0.0-next-beta.318",
|
||||
"@trpc/react-query": "^11.0.0-next-beta.318",
|
||||
"@trpc/server": "^11.0.0-next-beta.318",
|
||||
"@vercel/otel": "^1.9.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clean-deep": "^3.4.0",
|
||||
"dayjs": "^1.11.10",
|
||||
|
||||
@@ -59,6 +59,7 @@ export type ContactConfig = ContactConfigData["all_contact_config"]["items"][0]
|
||||
export type ContactFields = {
|
||||
display_text: string | null
|
||||
contact_field: string
|
||||
footnote: string | null
|
||||
}
|
||||
|
||||
export const validateHeaderConfigSchema = z.object({
|
||||
|
||||
@@ -2,10 +2,13 @@ import { z } from "zod"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
|
||||
import { ImageVaultAsset } from "@/types/components/imageVaultImage"
|
||||
import {
|
||||
JoinLoyaltyContactTypenameEnum,
|
||||
LoyaltyBlocksTypenameEnum,
|
||||
LoyaltyCardsGridEnum,
|
||||
LoyaltyComponentEnum,
|
||||
LoyaltySidebarDynamicComponentEnum,
|
||||
SidebarTypenameEnum,
|
||||
} from "@/types/components/loyalty/enums"
|
||||
import { Embeds } from "@/types/requests/embeds"
|
||||
@@ -47,6 +50,7 @@ const loyaltyPageShortcuts = z.object({
|
||||
})
|
||||
|
||||
const cardBlock = z.object({
|
||||
__typename: z.literal(LoyaltyCardsGridEnum.Card),
|
||||
heading: z.string().nullable(),
|
||||
body_text: z.string().nullable(),
|
||||
background_image: z.any(),
|
||||
@@ -73,6 +77,30 @@ const cardBlock = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyCardBlock = z.object({
|
||||
__typename: z.literal(LoyaltyCardsGridEnum.LoyaltyCard),
|
||||
heading: z.string().nullable(),
|
||||
body_text: z.string().nullable(),
|
||||
image: z.any(),
|
||||
link: z
|
||||
.object({
|
||||
openInNewTab: z.boolean(),
|
||||
title: z.string(),
|
||||
href: z.string(),
|
||||
isExternal: z.boolean(),
|
||||
})
|
||||
.optional(),
|
||||
system: z.object({
|
||||
locale: z.nativeEnum(Lang),
|
||||
uid: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageCardsItems = z.discriminatedUnion("__typename", [
|
||||
loyaltyCardBlock,
|
||||
cardBlock,
|
||||
])
|
||||
|
||||
const loyaltyPageCards = z.object({
|
||||
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
|
||||
cards_grid: z.object({
|
||||
@@ -80,7 +108,7 @@ const loyaltyPageCards = z.object({
|
||||
preamble: z.string().nullable(),
|
||||
layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]),
|
||||
theme: z.enum(["one", "two", "three"]).nullable(),
|
||||
cards: z.array(cardBlock),
|
||||
cards: z.array(loyaltyPageCardsItems),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -132,14 +160,23 @@ const loyaltyPageJoinLoyaltyContact = z.object({
|
||||
contact: z.object({
|
||||
display_text: z.string().nullable(),
|
||||
contact_field: z.string(),
|
||||
footnote: z.string().nullable(),
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageSidebarDynamicContent = z.object({
|
||||
__typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarDynamicContent),
|
||||
dynamic_content: z.object({
|
||||
component: z.nativeEnum(LoyaltySidebarDynamicComponentEnum),
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyPageSidebarItem = z.discriminatedUnion("__typename", [
|
||||
loyaltyPageSidebarTextContent,
|
||||
loyaltyPageSidebarDynamicContent,
|
||||
loyaltyPageJoinLoyaltyContact,
|
||||
])
|
||||
|
||||
@@ -151,7 +188,6 @@ export const validateLoyaltyPageSchema = z.object({
|
||||
})
|
||||
|
||||
// Block types
|
||||
|
||||
export type DynamicContent = z.infer<typeof loyaltyPageDynamicContent>
|
||||
|
||||
type BlockContentRaw = z.infer<typeof loyaltyPageBlockTextContent>
|
||||
@@ -164,11 +200,25 @@ export interface RteBlockContent extends BlockContentRaw {
|
||||
}
|
||||
}
|
||||
|
||||
type LoyaltyCardRaw = z.infer<typeof loyaltyCardBlock>
|
||||
|
||||
type LoyaltyCard = Omit<LoyaltyCardRaw, "image"> & {
|
||||
image?: ImageVaultAsset
|
||||
}
|
||||
|
||||
type CardRaw = z.infer<typeof cardBlock>
|
||||
|
||||
type Card = Omit<CardRaw, "background_image"> & {
|
||||
backgroundImage?: ImageVaultAsset
|
||||
}
|
||||
|
||||
type CardsGridRaw = z.infer<typeof loyaltyPageCards>
|
||||
|
||||
export type CardsRaw = CardsGridRaw["cards_grid"]["cards"][number]
|
||||
export type CardsGrid = Omit<CardsGridRaw, "cards"> & {
|
||||
cards: (LoyaltyCard | Card)[]
|
||||
}
|
||||
|
||||
export type CardsGrid = z.infer<typeof loyaltyPageCards>
|
||||
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
|
||||
|
||||
export type Shortcuts = z.infer<typeof loyaltyPageShortcuts>
|
||||
|
||||
@@ -185,8 +235,13 @@ export type RteSidebarContent = Omit<SidebarContentRaw, "content"> & {
|
||||
}
|
||||
}
|
||||
}
|
||||
type SideBarDynamicContent = z.infer<typeof loyaltyPageSidebarDynamicContent>
|
||||
|
||||
export type JoinLoyaltyContact = z.infer<typeof loyaltyPageJoinLoyaltyContact>
|
||||
export type Sidebar = JoinLoyaltyContact | RteSidebarContent
|
||||
export type Sidebar =
|
||||
| JoinLoyaltyContact
|
||||
| RteSidebarContent
|
||||
| SideBarDynamicContent
|
||||
type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema>
|
||||
|
||||
export type LoyaltyPage = Omit<LoyaltyPageDataRaw, "blocks" | "sidebar"> & {
|
||||
@@ -210,6 +265,7 @@ const pageConnectionRefs = z.object({
|
||||
})
|
||||
|
||||
const cardBlockRefs = z.object({
|
||||
__typename: z.literal(LoyaltyCardsGridEnum.Card),
|
||||
primary_button: z
|
||||
.object({
|
||||
linkConnection: pageConnectionRefs,
|
||||
@@ -227,13 +283,32 @@ const cardBlockRefs = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
const loyaltyCardBlockRefs = z.object({
|
||||
__typename: z.literal(LoyaltyCardsGridEnum.LoyaltyCard),
|
||||
link: z
|
||||
.object({
|
||||
linkConnection: pageConnectionRefs,
|
||||
})
|
||||
.nullable(),
|
||||
|
||||
system: z.object({
|
||||
content_type_uid: z.string(),
|
||||
uid: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
const cardGridCardsRef = z.discriminatedUnion("__typename", [
|
||||
loyaltyCardBlockRefs,
|
||||
cardBlockRefs,
|
||||
])
|
||||
|
||||
const loyaltyPageCardsRefs = z.object({
|
||||
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
|
||||
cards_grid: z.object({
|
||||
cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: cardBlockRefs,
|
||||
node: cardGridCardsRef,
|
||||
})
|
||||
),
|
||||
}),
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
generateTag,
|
||||
generateTags,
|
||||
} from "@/utils/generateTag"
|
||||
import { insertResponseToImageVaultAsset } from "@/utils/imageVault"
|
||||
import { removeMultipleSlashes } from "@/utils/url"
|
||||
|
||||
import { removeEmptyObjects } from "../../utils"
|
||||
@@ -22,7 +23,17 @@ import {
|
||||
} from "./output"
|
||||
import { getConnections } from "./utils"
|
||||
|
||||
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
||||
import { InsertResponse } from "@/types/components/imageVaultImage"
|
||||
import {
|
||||
LoyaltyBlocksTypenameEnum,
|
||||
LoyaltyCardsGridEnum,
|
||||
} from "@/types/components/loyalty/enums"
|
||||
|
||||
function makeImageVaultImage(image: any) {
|
||||
return image && !!Object.keys(image).length
|
||||
? insertResponseToImageVaultAsset(image as InsertResponse)
|
||||
: undefined
|
||||
}
|
||||
|
||||
function makeButtonObject(button: any) {
|
||||
return {
|
||||
@@ -35,9 +46,9 @@ function makeButtonObject(button: any) {
|
||||
href:
|
||||
button.is_contentstack_link && button.linkConnection.edges.length
|
||||
? button.linkConnection.edges[0].node.web?.original_url ||
|
||||
removeMultipleSlashes(
|
||||
`/${button.linkConnection.edges[0].node.system.locale}/${button.linkConnection.edges[0].node.url}`
|
||||
)
|
||||
removeMultipleSlashes(
|
||||
`/${button.linkConnection.edges[0].node.system.locale}/${button.linkConnection.edges[0].node.url}`
|
||||
)
|
||||
: button.external_link.href,
|
||||
isExternal: !button.is_contentstack_link,
|
||||
}
|
||||
@@ -96,66 +107,82 @@ export const loyaltyPageQueryRouter = router({
|
||||
|
||||
const blocks = response.data.loyalty_page.blocks
|
||||
? response.data.loyalty_page.blocks.map((block: any) => {
|
||||
switch (block.__typename) {
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
|
||||
return {
|
||||
...block,
|
||||
dynamic_content: {
|
||||
...block.dynamic_content,
|
||||
link: block.dynamic_content.link.pageConnection.edges.length
|
||||
? {
|
||||
text: block.dynamic_content.link.text,
|
||||
href: removeMultipleSlashes(
|
||||
`/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}/${block.dynamic_content.link.pageConnection.edges[0].node.url}`
|
||||
),
|
||||
title:
|
||||
block.dynamic_content.link.pageConnection.edges[0]
|
||||
.node.title,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
||||
return {
|
||||
...block,
|
||||
shortcuts: {
|
||||
...block.shortcuts,
|
||||
shortcuts: block.shortcuts.shortcuts.map((shortcut: any) => ({
|
||||
text: shortcut.text,
|
||||
openInNewTab: shortcut.open_in_new_tab,
|
||||
...shortcut.linkConnection.edges[0].node,
|
||||
url:
|
||||
shortcut.linkConnection.edges[0].node.web?.original_url ||
|
||||
removeMultipleSlashes(
|
||||
`/${shortcut.linkConnection.edges[0].node.system.locale}/${shortcut.linkConnection.edges[0].node.url}`
|
||||
),
|
||||
})),
|
||||
},
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
||||
return {
|
||||
...block,
|
||||
cards_grid: {
|
||||
...block.cards_grid,
|
||||
cards: block.cards_grid.cardConnection.edges.map(
|
||||
({ node: card }: { node: any }) => {
|
||||
return {
|
||||
...card,
|
||||
primaryButton: card.has_primary_button
|
||||
? makeButtonObject(card.primary_button)
|
||||
: undefined,
|
||||
secondaryButton: card.has_secondary_button
|
||||
? makeButtonObject(card.secondary_button)
|
||||
: undefined,
|
||||
switch (block.__typename) {
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
|
||||
return {
|
||||
...block,
|
||||
dynamic_content: {
|
||||
...block.dynamic_content,
|
||||
link: block.dynamic_content.link.pageConnection.edges.length
|
||||
? {
|
||||
text: block.dynamic_content.link.text,
|
||||
href: removeMultipleSlashes(
|
||||
`/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}/${block.dynamic_content.link.pageConnection.edges[0].node.url}`
|
||||
),
|
||||
title:
|
||||
block.dynamic_content.link.pageConnection.edges[0]
|
||||
.node.title,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
||||
return {
|
||||
...block,
|
||||
shortcuts: {
|
||||
...block.shortcuts,
|
||||
shortcuts: block.shortcuts.shortcuts.map((shortcut: any) => ({
|
||||
text: shortcut.text,
|
||||
openInNewTab: shortcut.open_in_new_tab,
|
||||
...shortcut.linkConnection.edges[0].node,
|
||||
url:
|
||||
shortcut.linkConnection.edges[0].node.web?.original_url ||
|
||||
removeMultipleSlashes(
|
||||
`/${shortcut.linkConnection.edges[0].node.system.locale}/${shortcut.linkConnection.edges[0].node.url}`
|
||||
),
|
||||
})),
|
||||
},
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
||||
return {
|
||||
...block,
|
||||
cards_grid: {
|
||||
...block.cards_grid,
|
||||
cards: block.cards_grid.cardConnection.edges.map(
|
||||
({ node: card }: { node: any }) => {
|
||||
switch (card.__typename) {
|
||||
case LoyaltyCardsGridEnum.LoyaltyCard:
|
||||
return {
|
||||
...card,
|
||||
image: makeImageVaultImage(card.image),
|
||||
link: makeButtonObject({
|
||||
...card.link,
|
||||
is_contentstack_link:
|
||||
!!card.link.linkConnection.edges.length,
|
||||
}),
|
||||
}
|
||||
case LoyaltyCardsGridEnum.Card:
|
||||
return {
|
||||
...card,
|
||||
backgroundImage: makeImageVaultImage(
|
||||
card.background_image
|
||||
),
|
||||
primaryButton: card.has_primary_button
|
||||
? makeButtonObject(card.primary_button)
|
||||
: undefined,
|
||||
secondaryButton: card.has_secondary_button
|
||||
? makeButtonObject(card.secondary_button)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
}
|
||||
default:
|
||||
return block
|
||||
}
|
||||
})
|
||||
),
|
||||
},
|
||||
}
|
||||
default:
|
||||
return block
|
||||
}
|
||||
})
|
||||
: null
|
||||
|
||||
const loyaltyPage = {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { LoyaltyPageRefsDataRaw } from "./output"
|
||||
|
||||
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
||||
import {
|
||||
LoyaltyBlocksTypenameEnum,
|
||||
LoyaltyCardsGridEnum,
|
||||
} from "@/types/components/loyalty/enums"
|
||||
import type { Edges } from "@/types/requests/utils/edges"
|
||||
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||
|
||||
@@ -18,13 +21,23 @@ export function getConnections(refs: LoyaltyPageRefsDataRaw) {
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid: {
|
||||
connections.push(item.cards_grid.cardConnection)
|
||||
item.cards_grid.cardConnection.edges.forEach((card) => {
|
||||
if (card.node.primary_button) {
|
||||
connections.push(card.node.primary_button?.linkConnection)
|
||||
} else if (card.node.secondary_button) {
|
||||
connections.push(card.node.secondary_button?.linkConnection)
|
||||
switch (card.node.__typename) {
|
||||
case LoyaltyCardsGridEnum.LoyaltyCard: {
|
||||
if (card.node.link) {
|
||||
connections.push(card.node.link?.linkConnection)
|
||||
}
|
||||
break
|
||||
}
|
||||
case LoyaltyCardsGridEnum.Card: {
|
||||
if (card.node.primary_button) {
|
||||
connections.push(card.node.primary_button?.linkConnection)
|
||||
} else if (card.node.secondary_button) {
|
||||
connections.push(card.node.secondary_button?.linkConnection)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {
|
||||
|
||||
@@ -24,7 +24,9 @@ export type DynamicComponentProps = {
|
||||
component: DynamicContent["dynamic_content"]["component"]
|
||||
}
|
||||
|
||||
export type CardsGridProps = Pick<CardsGrid, "cards_grid">
|
||||
export type CardsGridProps = Pick<CardsGrid, "cards_grid"> & {
|
||||
firstItem?: boolean
|
||||
}
|
||||
|
||||
export type Content = { content: RteBlockContent["content"]["content"] }
|
||||
|
||||
|
||||
@@ -13,10 +13,15 @@ export type JoinLoyaltyContactContact = Typename<
|
||||
export enum SidebarTypenameEnum {
|
||||
LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact",
|
||||
LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent",
|
||||
LoyaltyPageSidebarDynamicContent = "LoyaltyPageSidebarDynamicContent",
|
||||
}
|
||||
|
||||
export type SidebarTypename = keyof typeof SidebarTypenameEnum
|
||||
|
||||
export enum LoyaltySidebarDynamicComponentEnum {
|
||||
my_pages_navigation = "my_pages_navigation",
|
||||
}
|
||||
|
||||
export enum LoyaltyComponentEnum {
|
||||
loyalty_levels = "loyalty_levels",
|
||||
how_it_works = "how_it_works",
|
||||
@@ -31,3 +36,8 @@ export enum LoyaltyBlocksTypenameEnum {
|
||||
LoyaltyPageBlocksShortcuts = "LoyaltyPageBlocksShortcuts",
|
||||
LoyaltyPageBlocksCardsGrid = "LoyaltyPageBlocksCardsGrid",
|
||||
}
|
||||
|
||||
export enum LoyaltyCardsGridEnum {
|
||||
LoyaltyCard = "LoyaltyCard",
|
||||
Card = "Card",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user