From c00f7b78ebe22b3fdbc447918e7dd19ea7bdb26b Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Wed, 17 Apr 2024 16:20:55 +0200 Subject: [PATCH 01/11] feat(WEB-131): add loyalty page --- .../(protected)/my-pages/benefits/page.tsx | 2 +- .../(public)/loyalty-page/layout.module.css | 9 ++ .../(live)/(public)/loyalty-page/layout.tsx | 21 +++ .../(public)/loyalty-page/page.module.css | 18 +++ .../(live)/(public)/loyalty-page/page.tsx | 40 +++++ .../Blocks/Benefits/CurrentLevel/index.tsx | 2 +- .../Blocks/Benefits/NextLevel/index.tsx | 2 +- .../MyPages/Blocks/Challenges/index.tsx | 2 +- components/MyPages/Blocks/Overview/index.tsx | 2 +- components/MyPages/Blocks/Shortcuts/index.tsx | 2 +- .../MyPages/Blocks/Stays/StayCard/index.tsx | 2 +- components/MyPages/Sidebar/index.tsx | 3 +- components/{MyPages => }/Title/index.tsx | 0 .../{MyPages => }/Title/title.module.css | 2 +- components/{MyPages => }/Title/variants.ts | 0 lib/graphql/Query/LoyaltyPage.graphql | 140 ++++++++++++++++++ server/routers/contentstack/index.ts | 2 + .../contentstack/loyaltyPage/index.tsx | 5 + .../routers/contentstack/loyaltyPage/query.ts | 33 +++++ types/components/myPages/title.ts | 6 +- types/requests/loyaltyPage.ts | 104 +++++++++++++ 21 files changed, 386 insertions(+), 11 deletions(-) create mode 100644 app/[lang]/(live)/(public)/loyalty-page/layout.module.css create mode 100644 app/[lang]/(live)/(public)/loyalty-page/layout.tsx create mode 100644 app/[lang]/(live)/(public)/loyalty-page/page.module.css create mode 100644 app/[lang]/(live)/(public)/loyalty-page/page.tsx rename components/{MyPages => }/Title/index.tsx (100%) rename components/{MyPages => }/Title/title.module.css (99%) rename components/{MyPages => }/Title/variants.ts (100%) create mode 100644 lib/graphql/Query/LoyaltyPage.graphql create mode 100644 server/routers/contentstack/loyaltyPage/index.tsx create mode 100644 server/routers/contentstack/loyaltyPage/query.ts create mode 100644 types/requests/loyaltyPage.ts diff --git a/app/[lang]/(live)/(protected)/my-pages/benefits/page.tsx b/app/[lang]/(live)/(protected)/my-pages/benefits/page.tsx index c3aba6b90..ada9f3e70 100644 --- a/app/[lang]/(live)/(protected)/my-pages/benefits/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/benefits/page.tsx @@ -1,7 +1,7 @@ import CurrentBenefitsBlock from "@/components/MyPages/Blocks/Benefits/CurrentLevel" import NextLevelBenefitsBlock from "@/components/MyPages/Blocks/Benefits/NextLevel" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import { shortcuts } from "./_constants" diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css new file mode 100644 index 000000000..cd47ec147 --- /dev/null +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css @@ -0,0 +1,9 @@ +.layout { + --max-width: 101.4rem; + --header-height: 4.5rem; + + display: grid; + font-family: var(--ff-fira-sans); + grid-template-rows: var(--header-height) auto 1fr; + min-height: 100dvh; +} diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.tsx b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx new file mode 100644 index 000000000..e925346f6 --- /dev/null +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx @@ -0,0 +1,21 @@ +import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts" + +import Header from "@/components/MyPages/Header" + +import styles from "./layout.module.css" + +import type { MyPagesLayoutProps } from "@/types/components/myPages/layout" + +export default async function LoyaltyPagesLayout({ + children, + params, +}: React.PropsWithChildren) { + return ( +
+
+ {children} +
+ ) +} diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.module.css b/app/[lang]/(live)/(public)/loyalty-page/page.module.css new file mode 100644 index 000000000..ebc27135f --- /dev/null +++ b/app/[lang]/(live)/(public)/loyalty-page/page.module.css @@ -0,0 +1,18 @@ +.content { + display: grid; + padding-bottom: 7.7rem; + padding-left: 0; + padding-right: 0; + position: relative; +} + +@media screen and (min-width: 950px) { + .content { + gap: 10rem; + grid-template-columns: 25rem 1fr; + padding-bottom: 17.5rem; + padding-left: 2.4rem; + padding-right: 2.4rem; + padding-top: 5.8rem; + } +} diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.tsx b/app/[lang]/(live)/(public)/loyalty-page/page.tsx new file mode 100644 index 000000000..7b03e2191 --- /dev/null +++ b/app/[lang]/(live)/(public)/loyalty-page/page.tsx @@ -0,0 +1,40 @@ +import Title from "@/components/Title" +import MaxWidth from "@/components/MaxWidth" + +import { serverClient } from "@/lib/trpc/server" +import { LangParams, PageArgs, UriParams } from "@/types/params" +import { notFound } from "next/navigation" + +import styles from "./page.module.css" + +export default async function LoyaltyPage({ + params, + searchParams, +}: PageArgs) { + try { + if (!searchParams.uri) { + throw new Error("Bad URI") + } + + const loyaltyPage = await serverClient().contentstack.loyaltyPage.get({ + uri: searchParams.uri, + lang: params.lang, + }) + + return ( +
+ + + {loyaltyPage.title} + + +
+ ) + } catch (err) { + return notFound() + } +} + +function Content(content: any) { + return <> +} diff --git a/components/MyPages/Blocks/Benefits/CurrentLevel/index.tsx b/components/MyPages/Blocks/Benefits/CurrentLevel/index.tsx index 9371fde25..e106697c4 100644 --- a/components/MyPages/Blocks/Benefits/CurrentLevel/index.tsx +++ b/components/MyPages/Blocks/Benefits/CurrentLevel/index.tsx @@ -2,7 +2,7 @@ import Link from "next/link" import { serverClient } from "@/lib/trpc/server" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./current.module.css" diff --git a/components/MyPages/Blocks/Benefits/NextLevel/index.tsx b/components/MyPages/Blocks/Benefits/NextLevel/index.tsx index 1e0fe88ed..70bf4d4c8 100644 --- a/components/MyPages/Blocks/Benefits/NextLevel/index.tsx +++ b/components/MyPages/Blocks/Benefits/NextLevel/index.tsx @@ -3,7 +3,7 @@ import { Lock } from "react-feather" import { serverClient } from "@/lib/trpc/server" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import Button from "@/components/TempDesignSystem/Button" import styles from "./next.module.css" diff --git a/components/MyPages/Blocks/Challenges/index.tsx b/components/MyPages/Blocks/Challenges/index.tsx index faa96b342..b1c5036e7 100644 --- a/components/MyPages/Blocks/Challenges/index.tsx +++ b/components/MyPages/Blocks/Challenges/index.tsx @@ -1,5 +1,5 @@ import Image from "@/components/Image" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./challenges.module.css" diff --git a/components/MyPages/Blocks/Overview/index.tsx b/components/MyPages/Blocks/Overview/index.tsx index 9725c5ca2..fb63d7415 100644 --- a/components/MyPages/Blocks/Overview/index.tsx +++ b/components/MyPages/Blocks/Overview/index.tsx @@ -1,4 +1,4 @@ -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import Friend from "./Friend" import Stats from "./Stats" diff --git a/components/MyPages/Blocks/Shortcuts/index.tsx b/components/MyPages/Blocks/Shortcuts/index.tsx index 100ecda0b..cb80b61c0 100644 --- a/components/MyPages/Blocks/Shortcuts/index.tsx +++ b/components/MyPages/Blocks/Shortcuts/index.tsx @@ -1,7 +1,7 @@ import Link from "next/link" import Image from "@/components/Image" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./shortcuts.module.css" diff --git a/components/MyPages/Blocks/Stays/StayCard/index.tsx b/components/MyPages/Blocks/Stays/StayCard/index.tsx index 459f7fb6e..c7e36641a 100644 --- a/components/MyPages/Blocks/Stays/StayCard/index.tsx +++ b/components/MyPages/Blocks/Stays/StayCard/index.tsx @@ -1,7 +1,7 @@ import { dt } from "@/lib/dt" import Image from "@/components/Image" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./stay.module.css" diff --git a/components/MyPages/Sidebar/index.tsx b/components/MyPages/Sidebar/index.tsx index 9134e8cbe..9c43e0a6e 100644 --- a/components/MyPages/Sidebar/index.tsx +++ b/components/MyPages/Sidebar/index.tsx @@ -4,9 +4,10 @@ import { LogOut } from "react-feather" import { GetNavigationMyPages } from "@/lib/graphql/Query/NavigationMyPages.graphql" import { request } from "@/lib/graphql/request" +import Title from "@/components/Title" import Link from "@/components/TempDesignSystem/Link" -import Title from "../Title" + import { mapMenuItems } from "./helpers" import styles from "./sidebar.module.css" diff --git a/components/MyPages/Title/index.tsx b/components/Title/index.tsx similarity index 100% rename from components/MyPages/Title/index.tsx rename to components/Title/index.tsx diff --git a/components/MyPages/Title/title.module.css b/components/Title/title.module.css similarity index 99% rename from components/MyPages/Title/title.module.css rename to components/Title/title.module.css index b8b2490f7..a51d9629b 100644 --- a/components/MyPages/Title/title.module.css +++ b/components/Title/title.module.css @@ -82,4 +82,4 @@ font-size: var(--typography-Title5-Desktop-fontSize); line-height: var(--typography-Title5-Desktop-lineHeight); } -} \ No newline at end of file +} diff --git a/components/MyPages/Title/variants.ts b/components/Title/variants.ts similarity index 100% rename from components/MyPages/Title/variants.ts rename to components/Title/variants.ts diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql new file mode 100644 index 000000000..5cfb1d59b --- /dev/null +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -0,0 +1,140 @@ +query GetLoyaltyPage($locale: String!, $url: String!) { + all_loyalty_page(where: { url: $url, locale: $locale }) { + items { + content { + ... on LoyaltyPageContentLoyaltyLevels { + __typename + loyalty_levels { + heading + sub_heading + level_card { + loyalty_level + } + } + } + ... on LoyaltyPageContentCardGrid { + __typename + card_grid { + heading + subheading + cards { + referenceConnection { + edges { + node { + ... on LoyaltyPage { + url + } + ... on ContentPage { + web { + url + } + } + ... on AccountPage { + url + } + } + } + } + heading + subheading + } + } + } + } + title + sidebar { + ... on LoyaltyPageSidebarLoyaltyJoinContact { + __typename + loyalty_join_contact { + title + contact { + ... on LoyaltyPageSidebarLoyaltyJoinContactBlockContactContact { + __typename + contact { + contact_fields + } + } + } + login_button_text + body { + json + embedded_itemsConnection { + edges { + node { + ... on SysAsset { + title + dimension { + width + height + } + file_size + filename + url + } + } + } + } + } + } + } + ... on LoyaltyPageSidebarContent { + __typename + content { + content { + json + embedded_itemsConnection { + edges { + node { + ... on SysAsset { + title + url + file_size + filename + dimension { + width + height + } + } + } + } + } + } + } + } + } + web { + breadcrumbs { + title + parents { + href + title + } + } + original_url + seo_metadata { + description + title + imageConnection { + edges { + node { + file_size + filename + dimension { + height + width + } + url + title + } + } + } + } + } + system { + uid + created_at + updated_at + } + } + } +} diff --git a/server/routers/contentstack/index.ts b/server/routers/contentstack/index.ts index 8098575a0..1a570fa8e 100644 --- a/server/routers/contentstack/index.ts +++ b/server/routers/contentstack/index.ts @@ -1,7 +1,9 @@ import { router } from "@/server/trpc" import { breadcrumbsRouter } from "./breadcrumbs" +import { loyaltyPageRouter } from "./loyaltyPage" export const contentstackRouter = router({ breadcrumbs: breadcrumbsRouter, + loyaltyPage: loyaltyPageRouter, }) diff --git a/server/routers/contentstack/loyaltyPage/index.tsx b/server/routers/contentstack/loyaltyPage/index.tsx new file mode 100644 index 000000000..5e47223a1 --- /dev/null +++ b/server/routers/contentstack/loyaltyPage/index.tsx @@ -0,0 +1,5 @@ +import { mergeRouters } from "@/server/trpc" + +import { loyaltyPageQueryRouter } from "./query" + +export const loyaltyPageRouter = mergeRouters(loyaltyPageQueryRouter) diff --git a/server/routers/contentstack/loyaltyPage/query.ts b/server/routers/contentstack/loyaltyPage/query.ts new file mode 100644 index 000000000..3d22cf6a3 --- /dev/null +++ b/server/routers/contentstack/loyaltyPage/query.ts @@ -0,0 +1,33 @@ +import { z } from "zod" + +import { badRequestError } from "@/server/errors/trpc" +import { publicProcedure, router } from "@/server/trpc" +import { request } from "@/lib/graphql/request" +import { Lang } from "@/constants/languages" + +import GetLoyaltyPage from "@/lib/graphql/Query/LoyaltyPage.graphql" + +import type { GetLoyaltyPageData } from "@/types/requests/loyaltyPage" + +export const loyaltyPageQueryRouter = router({ + get: publicProcedure + .input(z.object({ uri: z.string(), lang: z.nativeEnum(Lang) })) + .query(async ({ input }) => { + const loyaltyPage = await request( + GetLoyaltyPage, + { + locale: input.lang, + url: input.uri, + }, + { + tags: [`${input.uri}-${input.lang}`], + } + ) + + if (loyaltyPage.data && loyaltyPage.data.all_loyalty_page.items.length) { + return loyaltyPage.data.all_loyalty_page.items[0] + } + + throw badRequestError() + }), +}) diff --git a/types/components/myPages/title.ts b/types/components/myPages/title.ts index fa73698bf..9b93f2807 100644 --- a/types/components/myPages/title.ts +++ b/types/components/myPages/title.ts @@ -1,10 +1,12 @@ -import { headingVariants } from "@/components/MyPages/Title/variants" +import { headingVariants } from "@/components/Title/variants" import type { VariantProps } from "class-variance-authority" type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" -export interface HeadingProps extends React.HTMLAttributes, VariantProps { +export interface HeadingProps + extends React.HTMLAttributes, + VariantProps { as?: HeadingLevel level?: HeadingLevel uppercase?: boolean diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts new file mode 100644 index 000000000..ea71c3813 --- /dev/null +++ b/types/requests/loyaltyPage.ts @@ -0,0 +1,104 @@ +import type { AllRequestResponse } from "./utils/all" +import type { Typename } from "./utils/typename" + +enum SidebarTypenameEnum { + LoyaltyPageSidebarLoyaltyJoinContact = "LoyaltyPageSidebarLoyaltyJoinContact", + LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", +} + +type SidebarContent = {} + +type JoinContact = {} + +export type Sidebar = + | Typename + | Typename< + JoinContact, + SidebarTypenameEnum.LoyaltyPageSidebarLoyaltyJoinContact + > + +enum ContentBlocks { + LoyaltyPageContentLoyaltyLevels = "LoyaltyPageContentLoyaltyLevels", + LoyaltyPageContentCardGrid = "LoyaltyPageContentCardGrid", +} + +enum LinkedPageConnection { + LoyaltyPage = "LoyaltyPage", + ContentPage = "ContentPage", + AccountPage = "AccountPage", +} + +type ContentPageLink = { + web: { url: string } +} +type LoyaltyPageLink = { + url: string +} + +type AccountPageLink = { + url: string +} + +type LinkedPage = + | Typename + | Typename + | Typename + +type CardGrid = { + card_grid: { + heading: string + subheading: string + cards: { + referenceConnection: { + edges: { + node: LinkedPage + } + } + heading: string + subheading: string + } + } +} + +type LoyaltyLevels = { + loyalty_levels: { + heading: string + sub_heading?: string + level_card: { + loyalty_level: number + }[] + } +} + +export type Content = + | Typename + | Typename + +export type Breadcrumb = { + href: string + title: string +} + +export type Breadcrumbs = { + parents: Breadcrumb[] + title: string +} + +export type LoyaltyPage = { + sidebar: Sidebar[] + content: Content[] + web: { + breadcrumbs: Breadcrumbs + } + system: { + created_at: string + uid: string + updated_at: string + } + title: string + url: string +} + +export type GetLoyaltyPageData = { + all_loyalty_page: AllRequestResponse +} From b57665ce6237218b7ee3e23b96b749b070e4a86c Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Thu, 18 Apr 2024 11:44:52 +0200 Subject: [PATCH 02/11] fix: typings for contactConfig --- lib/graphql/Query/LoyaltyPage.graphql | 17 +++++++- middlewares/cmsContent.ts | 16 ++++--- types/requests/contactConfig.ts | 48 ++++++++++++++++++++ types/requests/loyaltyPage.ts | 63 ++++++++++++++------------- 4 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 types/requests/contactConfig.ts diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 5cfb1d59b..e8f884fbc 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -22,15 +22,28 @@ query GetLoyaltyPage($locale: String!, $url: String!) { edges { node { ... on LoyaltyPage { + system { + locale + uid + } url + title } ... on ContentPage { - web { - url + system { + locale + uid } + url + title } ... on AccountPage { + system { + locale + uid + } url + title } } } diff --git a/middlewares/cmsContent.ts b/middlewares/cmsContent.ts index 097322576..183e4a2f5 100644 --- a/middlewares/cmsContent.ts +++ b/middlewares/cmsContent.ts @@ -10,7 +10,7 @@ export const middleware: NextMiddleware = async (request) => { const { nextUrl } = request const lang = findLang(nextUrl.pathname) - const contentType = "currentContentPage" + const contentType = "loyaltyPage" const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "") const searchParams = new URLSearchParams(request.nextUrl.searchParams) @@ -23,12 +23,16 @@ export const middleware: NextMiddleware = async (request) => { searchParams.set("uri", pathNameWithoutLang) switch (contentType) { - case "currentContentPage": + // case "currentContentPage": + // return NextResponse.rewrite( + // new URL( + // `/${lang}/current-content-page?${searchParams.toString()}`, + // nextUrl + // ) + // ) + case "loyaltyPage": return NextResponse.rewrite( - new URL( - `/${lang}/current-content-page?${searchParams.toString()}`, - nextUrl - ) + new URL(`/${lang}/loyalty-page?${searchParams.toString()}`, nextUrl) ) default: return NextResponse.next() diff --git a/types/requests/contactConfig.ts b/types/requests/contactConfig.ts new file mode 100644 index 000000000..ba34c2b0e --- /dev/null +++ b/types/requests/contactConfig.ts @@ -0,0 +1,48 @@ +import { AllRequestResponse } from "./utils/all" + +export type ContactConfig = { + items: [ + { + email: { + name: string + address: string + } + email_loyalty: { + name: string + address: string + } + mailing_address: { + zip: string + street: string + name: string + city: string + country: string + } + phone: { + number: string + naem: string + } + phone_loyalty: { + number: string + name: string + } + visiting_address: { + zip: string + country: string + city: string + street: string + } + }, + ] +} +type FlattenKeys = T extends object + ? { + [K in keyof T]-?: `${K & string}.${FlattenKeys}` + }[keyof T] + : "" + +export type ContactField = FlattenKeys + +export type GetContactConfigData = { + all_contact_config: AllRequestResponse +} diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts index ea71c3813..6b04dd127 100644 --- a/types/requests/loyaltyPage.ts +++ b/types/requests/loyaltyPage.ts @@ -1,19 +1,48 @@ +import { PageLink } from "./myPages/navigation" import type { AllRequestResponse } from "./utils/all" import type { Typename } from "./utils/typename" +import { Edges } from "./utils/edges" +import type { RTEDocument } from "../rte/node" +import type { Embeds } from "./embeds" +import type { ContactField } from "./contactConfig" enum SidebarTypenameEnum { LoyaltyPageSidebarLoyaltyJoinContact = "LoyaltyPageSidebarLoyaltyJoinContact", LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", } -type SidebarContent = {} +type SidebarContent = { + content: { + embedded_itemsConnection: Edges + json: RTEDocument + } +} -type JoinContact = {} +type Contact = { + contact: { + contact_fields: ContactField[] + } +} + +type LoyaltyJoinContact = { + loyalty_join_contact: { + title: string + contact: Typename< + Contact, + "LoyaltyPageSidebarLoyaltyJoinContactBlockContactContact" + > + login_button_text: string + body: { + embedded_itemsConnection: Edges + json: RTEDocument + } + } +} export type Sidebar = | Typename | Typename< - JoinContact, + LoyaltyJoinContact, SidebarTypenameEnum.LoyaltyPageSidebarLoyaltyJoinContact > @@ -22,38 +51,12 @@ enum ContentBlocks { LoyaltyPageContentCardGrid = "LoyaltyPageContentCardGrid", } -enum LinkedPageConnection { - LoyaltyPage = "LoyaltyPage", - ContentPage = "ContentPage", - AccountPage = "AccountPage", -} - -type ContentPageLink = { - web: { url: string } -} -type LoyaltyPageLink = { - url: string -} - -type AccountPageLink = { - url: string -} - -type LinkedPage = - | Typename - | Typename - | Typename - type CardGrid = { card_grid: { heading: string subheading: string cards: { - referenceConnection: { - edges: { - node: LinkedPage - } - } + referenceConnection: Edges heading: string subheading: string } From 3a0c8610dc8b95d649363893ff366268a9e5906b Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Fri, 19 Apr 2024 13:11:02 +0200 Subject: [PATCH 03/11] feat: add JoinLoyalty component --- .../(public)/loyalty-page/layout.module.css | 1 + .../(public)/loyalty-page/page.module.css | 13 ++++++ .../(live)/(public)/loyalty-page/page.tsx | 19 ++++++--- components/JsonToHtml/renderOptions.tsx | 10 +++++ .../Sidebar/JoinLoyalty/Contact/index.tsx | 8 ++++ .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 37 ++++++++++++++++ .../JoinLoyalty/joinLoyalty.module.css | 42 +++++++++++++++++++ components/Loyalty/Sidebar/index.tsx | 20 +++++++++ lib/graphql/Query/ContactConfig.graphql | 37 ++++++++++++++++ lib/graphql/Query/LoyaltyPage.graphql | 34 ++++----------- .../contentstack/contactConfig/index.ts | 5 +++ .../contentstack/contactConfig/query.ts | 32 ++++++++++++++ server/routers/contentstack/index.ts | 2 + .../loyaltyPage/{index.tsx => index.ts} | 0 types/requests/loyaltyPage.ts | 16 +++---- 15 files changed, 238 insertions(+), 38 deletions(-) create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/index.tsx create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css create mode 100644 components/Loyalty/Sidebar/index.tsx create mode 100644 lib/graphql/Query/ContactConfig.graphql create mode 100644 server/routers/contentstack/contactConfig/index.ts create mode 100644 server/routers/contentstack/contactConfig/query.ts rename server/routers/contentstack/loyaltyPage/{index.tsx => index.ts} (100%) diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css index cd47ec147..4ddc7b911 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css @@ -6,4 +6,5 @@ font-family: var(--ff-fira-sans); grid-template-rows: var(--header-height) auto 1fr; min-height: 100dvh; + background-color: var(--Brand-Coffee-Subtle); } diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.module.css b/app/[lang]/(live)/(public)/loyalty-page/page.module.css index ebc27135f..c8531f21d 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/page.module.css @@ -6,6 +6,13 @@ position: relative; } +.blocks { + display: grid; + gap: 4.2rem; + padding-left: 2rem; + padding-right: 2rem; +} + @media screen and (min-width: 950px) { .content { gap: 10rem; @@ -15,4 +22,10 @@ padding-right: 2.4rem; padding-top: 5.8rem; } + + .blocks { + gap: 6.4rem; + padding-left: 0; + padding-right: 0; + } } diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.tsx b/app/[lang]/(live)/(public)/loyalty-page/page.tsx index 7b03e2191..7abe910d3 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/page.tsx @@ -6,6 +6,7 @@ import { LangParams, PageArgs, UriParams } from "@/types/params" import { notFound } from "next/navigation" import styles from "./page.module.css" +import Sidebar from "@/components/Loyalty/Sidebar" export default async function LoyaltyPage({ params, @@ -22,13 +23,19 @@ export default async function LoyaltyPage({ }) return ( -
- - - {loyaltyPage.title} + + +
+ {loyaltyPage.title} - -
+ + ) } catch (err) { return notFound() diff --git a/components/JsonToHtml/renderOptions.tsx b/components/JsonToHtml/renderOptions.tsx index 2a6eb0d86..b9423cc48 100644 --- a/components/JsonToHtml/renderOptions.tsx +++ b/components/JsonToHtml/renderOptions.tsx @@ -29,6 +29,10 @@ function extractPossibleAttributes(attrs: Attributes) { props.className = attrs["class-name"] } else if (attrs.classname) { props.className = attrs.classname + } else if (attrs?.style?.["text-align"]) { + props.style = { + textAlign: attrs?.style?.["text-align"], + } } return props @@ -250,6 +254,11 @@ export const renderOptions: RenderOptions = { const image = embeds?.[node?.attrs?.["asset-uid"]] if (image.node.__typename === EmbedEnum.SysAsset) { const alt = image?.node?.title ?? node.attrs.alt + const alignment = node.attrs?.style?.["text-align"] + ? { + alignSelf: node.attrs?.style?.["text-align"], + } + : {} return ( ) } diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx new file mode 100644 index 000000000..351362cff --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx @@ -0,0 +1,8 @@ +import { Lang } from "@/constants/languages" +import { serverClient } from "@/lib/trpc/server" + +export default function Contact({ lang }: { lang: Lang }) { + const data = serverClient().contentstack.contactConfig.get({ lang }) + + return
+} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx new file mode 100644 index 000000000..25ef804e2 --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx @@ -0,0 +1,37 @@ +import Title from "@/components/Title" +import JsonToHtml from "@/components/JsonToHtml" +import Button from "@/components/TempDesignSystem/Button" +import Link from "@/components/TempDesignSystem/Link" + +import styles from "./joinLoyalty.module.css" + +import type { JoinLoyaltyContact } from "@/types/requests/loyaltyPage" + +export default function JoinLoyaltyContact({ + block, +}: { + block: JoinLoyaltyContact["join_loyalty_contact"] +}) { + return ( +
+
+ + +
+ + Already a friend?
+ Click here to log in + +
+
+
+ Contact +
+
+ ) +} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css new file mode 100644 index 000000000..cd22348e6 --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css @@ -0,0 +1,42 @@ +.container { + display: grid; + font-weight: 600; + background-color: var(--Base-Background-Elevated); + border-radius: 32px 4px 4px 32px; +} + +.wrapper { + display: flex; + align-items: center; + flex-direction: column; + gap: 2rem; + padding: 4rem 2rem; +} + +.logoutLink { + text-decoration: none; + color: var(--some-black-color, #2e2e2e); + font-size: 1.2rem; +} + +.linkContainer { + text-align: center; +} + +.contactContainer { + display: none; +} + +@media screen and (min-width: 950px) { + .wrapper { + gap: 3rem; + } + + .contactContainer { + display: block; + border-top: 0.5px solid var(--Base-Border-Disabled); + display: flex; + justify-content: center; + padding: 3.4rem; + } +} diff --git a/components/Loyalty/Sidebar/index.tsx b/components/Loyalty/Sidebar/index.tsx new file mode 100644 index 000000000..94cd937d2 --- /dev/null +++ b/components/Loyalty/Sidebar/index.tsx @@ -0,0 +1,20 @@ +import JsonToHtml from "@/components/JsonToHtml" +import JoinLoyaltyContact from "./JoinLoyalty" + +import { Sidebar, SidebarTypenameEnum } from "@/types/requests/loyaltyPage" + +export default function SidebarLoyalty({ block }: { block: Sidebar }) { + switch (block.__typename) { + case SidebarTypenameEnum.LoyaltyPageSidebarContent: + return ( + + ) + case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact: + return + default: + return null + } +} diff --git a/lib/graphql/Query/ContactConfig.graphql b/lib/graphql/Query/ContactConfig.graphql new file mode 100644 index 000000000..381d00941 --- /dev/null +++ b/lib/graphql/Query/ContactConfig.graphql @@ -0,0 +1,37 @@ +query GetContactConfig($locale: String!) { + all_contact_config(locale: $locale) { + items { + email { + address + name + } + email_loyalty { + address + name + } + mailing_address { + name + street + zip + country + city + } + phone { + number + name + } + phone_loyalty { + name + number + } + title + visiting_address { + country + city + street + zip + } + } + total + } +} diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index e8f884fbc..07eebacb6 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -1,3 +1,5 @@ +#import "../Fragments/Image.graphql" + query GetLoyaltyPage($locale: String!, $url: String!) { all_loyalty_page(where: { url: $url, locale: $locale }) { items { @@ -56,12 +58,12 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } title sidebar { - ... on LoyaltyPageSidebarLoyaltyJoinContact { + ... on LoyaltyPageSidebarJoinLoyaltyContact { __typename - loyalty_join_contact { + join_loyalty_contact { title contact { - ... on LoyaltyPageSidebarLoyaltyJoinContactBlockContactContact { + ... on LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact { __typename contact { contact_fields @@ -71,19 +73,11 @@ query GetLoyaltyPage($locale: String!, $url: String!) { login_button_text body { json - embedded_itemsConnection { + embedded_itemsConnection(limit: 30) { edges { node { - ... on SysAsset { - title - dimension { - width - height - } - file_size - filename - url - } + __typename + ...Image } } } @@ -98,16 +92,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { embedded_itemsConnection { edges { node { - ... on SysAsset { - title - url - file_size - filename - dimension { - width - height - } - } + ...Image } } } @@ -123,7 +108,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { title } } - original_url seo_metadata { description title diff --git a/server/routers/contentstack/contactConfig/index.ts b/server/routers/contentstack/contactConfig/index.ts new file mode 100644 index 000000000..155949945 --- /dev/null +++ b/server/routers/contentstack/contactConfig/index.ts @@ -0,0 +1,5 @@ +import { mergeRouters } from "@/server/trpc" + +import { contactConfigQueryRouter } from "./query" + +export const contactConfigRouter = mergeRouters(contactConfigQueryRouter) diff --git a/server/routers/contentstack/contactConfig/query.ts b/server/routers/contentstack/contactConfig/query.ts new file mode 100644 index 000000000..83d183c54 --- /dev/null +++ b/server/routers/contentstack/contactConfig/query.ts @@ -0,0 +1,32 @@ +import { z } from "zod" + +import { badRequestError } from "@/server/errors/trpc" +import { publicProcedure, router } from "@/server/trpc" +import { request } from "@/lib/graphql/request" +import { Lang } from "@/constants/languages" + +import GetContactConfig from "@/lib/graphql/Query/ContactConfig.graphql" + +import type { GetContactConfigData } from "@/types/requests/contactConfig" + +export const contactConfigQueryRouter = router({ + get: publicProcedure + .input(z.object({ lang: z.nativeEnum(Lang) })) + .query(async ({ input }) => { + const contactConfig = await request( + GetContactConfig, + { + locale: input.lang, + }, + { + tags: [`contact-config-${input.lang}`], + } + ) + + if (contactConfig.data && contactConfig.data.all_contact_config.total) { + return contactConfig.data.all_contact_config.items[0] + } + + throw badRequestError() + }), +}) diff --git a/server/routers/contentstack/index.ts b/server/routers/contentstack/index.ts index 1a570fa8e..3f765813d 100644 --- a/server/routers/contentstack/index.ts +++ b/server/routers/contentstack/index.ts @@ -2,8 +2,10 @@ import { router } from "@/server/trpc" import { breadcrumbsRouter } from "./breadcrumbs" import { loyaltyPageRouter } from "./loyaltyPage" +import { contactConfigRouter } from "./contactConfig" export const contentstackRouter = router({ breadcrumbs: breadcrumbsRouter, loyaltyPage: loyaltyPageRouter, + contactConfig: contactConfigRouter, }) diff --git a/server/routers/contentstack/loyaltyPage/index.tsx b/server/routers/contentstack/loyaltyPage/index.ts similarity index 100% rename from server/routers/contentstack/loyaltyPage/index.tsx rename to server/routers/contentstack/loyaltyPage/index.ts diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts index 6b04dd127..598a1f724 100644 --- a/types/requests/loyaltyPage.ts +++ b/types/requests/loyaltyPage.ts @@ -1,16 +1,18 @@ import { PageLink } from "./myPages/navigation" +import { Edges } from "./utils/edges" import type { AllRequestResponse } from "./utils/all" import type { Typename } from "./utils/typename" -import { Edges } from "./utils/edges" import type { RTEDocument } from "../rte/node" import type { Embeds } from "./embeds" import type { ContactField } from "./contactConfig" -enum SidebarTypenameEnum { - LoyaltyPageSidebarLoyaltyJoinContact = "LoyaltyPageSidebarLoyaltyJoinContact", +export enum SidebarTypenameEnum { + LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact", LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", } +export type SidebarTypename = keyof typeof SidebarTypenameEnum + type SidebarContent = { content: { embedded_itemsConnection: Edges @@ -24,8 +26,8 @@ type Contact = { } } -type LoyaltyJoinContact = { - loyalty_join_contact: { +export type JoinLoyaltyContact = { + join_loyalty_contact: { title: string contact: Typename< Contact, @@ -42,8 +44,8 @@ type LoyaltyJoinContact = { export type Sidebar = | Typename | Typename< - LoyaltyJoinContact, - SidebarTypenameEnum.LoyaltyPageSidebarLoyaltyJoinContact + JoinLoyaltyContact, + SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact > enum ContentBlocks { From fc0e5aed02e69f41649b510a3a27e519a6084429 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 22 Apr 2024 09:54:24 +0200 Subject: [PATCH 04/11] feat: create blocks component for loyalty --- .../(live)/(public)/loyalty-page/page.tsx | 20 ++-- components/Loyalty/Blocks/index.tsx | 26 ++++ .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 26 ++-- .../JoinLoyalty/joinLoyalty.module.css | 9 ++ .../Blocks/Overview/UpcomingStays/index.tsx | 2 +- .../MyPages/Blocks/Stays/Header/index.tsx | 2 +- .../Previous/EmptyPreviousStays/index.tsx | 2 +- .../Upcoming/EmptyUpcomingStays/index.tsx | 2 +- .../PageLink/AccountPageLink.graphql | 8 ++ .../PageLink/ContentPageLink.graphql | 8 ++ .../PageLink/CurrentContentPageLink.graphql | 8 ++ .../PageLink/LoyaltyPageLink.graphql | 8 ++ lib/graphql/Fragments/PageLinks.graphql | 35 ------ lib/graphql/Query/LoyaltyPage.graphql | 113 ++++++++---------- lib/graphql/Query/NavigationMyPages.graphql | 4 +- public/_static/icons/scandic-friends.png | Bin 0 -> 7252 bytes types/requests/loyaltyPage.ts | 69 +++++++---- 17 files changed, 193 insertions(+), 149 deletions(-) create mode 100644 components/Loyalty/Blocks/index.tsx create mode 100644 lib/graphql/Fragments/PageLink/AccountPageLink.graphql create mode 100644 lib/graphql/Fragments/PageLink/ContentPageLink.graphql create mode 100644 lib/graphql/Fragments/PageLink/CurrentContentPageLink.graphql create mode 100644 lib/graphql/Fragments/PageLink/LoyaltyPageLink.graphql delete mode 100644 lib/graphql/Fragments/PageLinks.graphql create mode 100644 public/_static/icons/scandic-friends.png diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.tsx b/app/[lang]/(live)/(public)/loyalty-page/page.tsx index 7abe910d3..88fab0647 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/page.tsx @@ -1,12 +1,13 @@ -import Title from "@/components/Title" -import MaxWidth from "@/components/MaxWidth" - -import { serverClient } from "@/lib/trpc/server" -import { LangParams, PageArgs, UriParams } from "@/types/params" import { notFound } from "next/navigation" +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Sidebar from "@/components/Loyalty/Sidebar" +import { Blocks } from "@/components/Loyalty/Blocks" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" import styles from "./page.module.css" -import Sidebar from "@/components/Loyalty/Sidebar" export default async function LoyaltyPage({ params, @@ -32,8 +33,7 @@ export default async function LoyaltyPage({ : null}
- {loyaltyPage.title} - +
) @@ -41,7 +41,3 @@ export default async function LoyaltyPage({ return notFound() } } - -function Content(content: any) { - return <> -} diff --git a/components/Loyalty/Blocks/index.tsx b/components/Loyalty/Blocks/index.tsx new file mode 100644 index 000000000..659a5e22d --- /dev/null +++ b/components/Loyalty/Blocks/index.tsx @@ -0,0 +1,26 @@ +import JsonToHtml from "@/components/JsonToHtml" + +import { + Blocks as BlocksType, + LoyaltyBlocksTypenameEnum, +} from "@/types/requests/loyaltyPage" + +export function Blocks({ blocks }: { blocks: BlocksType[] }) { + return blocks.map((block) => { + switch (block.__typename) { + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: + return

Cards

+ case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: + return ( + + ) + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: + return

Dynamic

+ default: + return null + } + }) +} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx index 25ef804e2..ec44c4df9 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx @@ -1,12 +1,13 @@ import Title from "@/components/Title" -import JsonToHtml from "@/components/JsonToHtml" +import Contact from "./Contact" import Button from "@/components/TempDesignSystem/Button" import Link from "@/components/TempDesignSystem/Link" - -import styles from "./joinLoyalty.module.css" +import Image from "@/components/Image" import type { JoinLoyaltyContact } from "@/types/requests/loyaltyPage" +import styles from "./joinLoyalty.module.css" + export default function JoinLoyaltyContact({ block, }: { @@ -15,10 +16,15 @@ export default function JoinLoyaltyContact({ return (
- {block.title}} + Scandic Friends + {block.preamble &&

{block.preamble}

} @@ -29,9 +35,11 @@ export default function JoinLoyaltyContact({
-
- Contact -
+ {block.contact + ? block.contact.map((contact, i) => ( + + )) + : null} ) } diff --git a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css index cd22348e6..8c3b0ac84 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css +++ b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css @@ -13,6 +13,15 @@ padding: 4rem 2rem; } +.preamble { + font-family: var(--fira-sans); + font-size: 1.6rem; + font-weight: 400; + line-height: 2.4rem; + text-align: center; + margin: 0; +} + .logoutLink { text-decoration: none; color: var(--some-black-color, #2e2e2e); diff --git a/components/MyPages/Blocks/Overview/UpcomingStays/index.tsx b/components/MyPages/Blocks/Overview/UpcomingStays/index.tsx index 592276300..98786b46e 100644 --- a/components/MyPages/Blocks/Overview/UpcomingStays/index.tsx +++ b/components/MyPages/Blocks/Overview/UpcomingStays/index.tsx @@ -3,8 +3,8 @@ import { serverClient } from "@/lib/trpc/server" import StayCard from "@/components/MyPages/Blocks/Stays/StayCard" import EmptyUpcomingStaysBlock from "@/components/MyPages/Blocks/Stays/Upcoming/EmptyUpcomingStays" -import Title from "@/components/MyPages/Title" import Link from "@/components/TempDesignSystem/Link" +import Title from "@/components/Title" import styles from "./upcoming.module.css" diff --git a/components/MyPages/Blocks/Stays/Header/index.tsx b/components/MyPages/Blocks/Stays/Header/index.tsx index d4fbeee68..683817b20 100644 --- a/components/MyPages/Blocks/Stays/Header/index.tsx +++ b/components/MyPages/Blocks/Stays/Header/index.tsx @@ -1,4 +1,4 @@ -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./header.module.css" diff --git a/components/MyPages/Blocks/Stays/Previous/EmptyPreviousStays/index.tsx b/components/MyPages/Blocks/Stays/Previous/EmptyPreviousStays/index.tsx index 1d7675412..0c390e390 100644 --- a/components/MyPages/Blocks/Stays/Previous/EmptyPreviousStays/index.tsx +++ b/components/MyPages/Blocks/Stays/Previous/EmptyPreviousStays/index.tsx @@ -1,6 +1,6 @@ import { _ } from "@/lib/translation" -import Title from "@/components/MyPages/Title" +import Title from "@/components/Title" import styles from "./emptyPreviousStays.module.css" diff --git a/components/MyPages/Blocks/Stays/Upcoming/EmptyUpcomingStays/index.tsx b/components/MyPages/Blocks/Stays/Upcoming/EmptyUpcomingStays/index.tsx index ec6fe206d..08ce36f32 100644 --- a/components/MyPages/Blocks/Stays/Upcoming/EmptyUpcomingStays/index.tsx +++ b/components/MyPages/Blocks/Stays/Upcoming/EmptyUpcomingStays/index.tsx @@ -2,8 +2,8 @@ import Link from "next/link" import { _ } from "@/lib/translation" -import Title from "@/components/MyPages/Title" import Button from "@/components/TempDesignSystem/Button" +import Title from "@/components/Title" import styles from "./emptyUpcomingStays.module.css" diff --git a/lib/graphql/Fragments/PageLink/AccountPageLink.graphql b/lib/graphql/Fragments/PageLink/AccountPageLink.graphql new file mode 100644 index 000000000..dc5ed6667 --- /dev/null +++ b/lib/graphql/Fragments/PageLink/AccountPageLink.graphql @@ -0,0 +1,8 @@ +fragment AccountPageLink on AccountPage { + system { + locale + uid + } + title + url +} diff --git a/lib/graphql/Fragments/PageLink/ContentPageLink.graphql b/lib/graphql/Fragments/PageLink/ContentPageLink.graphql new file mode 100644 index 000000000..f6eb6474b --- /dev/null +++ b/lib/graphql/Fragments/PageLink/ContentPageLink.graphql @@ -0,0 +1,8 @@ +fragment ContentPageLink on ContentPage { + system { + locale + uid + } + url + title +} diff --git a/lib/graphql/Fragments/PageLink/CurrentContentPageLink.graphql b/lib/graphql/Fragments/PageLink/CurrentContentPageLink.graphql new file mode 100644 index 000000000..1b82b2a59 --- /dev/null +++ b/lib/graphql/Fragments/PageLink/CurrentContentPageLink.graphql @@ -0,0 +1,8 @@ +fragment CurrentBlocksPageLink on CurrentBlocksPage { + system { + locale + uid + } + title + url +} diff --git a/lib/graphql/Fragments/PageLink/LoyaltyPageLink.graphql b/lib/graphql/Fragments/PageLink/LoyaltyPageLink.graphql new file mode 100644 index 000000000..d0c6ac0a9 --- /dev/null +++ b/lib/graphql/Fragments/PageLink/LoyaltyPageLink.graphql @@ -0,0 +1,8 @@ +fragment LoyaltyPageLink on LoyaltyPage { + system { + locale + uid + } + title + url +} diff --git a/lib/graphql/Fragments/PageLinks.graphql b/lib/graphql/Fragments/PageLinks.graphql deleted file mode 100644 index 61f380efa..000000000 --- a/lib/graphql/Fragments/PageLinks.graphql +++ /dev/null @@ -1,35 +0,0 @@ -fragment CurrentBlocksPageLink on CurrentBlocksPage { - system { - locale - uid - } - title - url -} - -fragment AccountPageLink on AccountPage { - system { - locale - uid - } - title - url -} - -fragment LoyaltyPageLink on LoyaltyPage { - system { - locale - uid - } - title - url -} - -fragment ContentPageLink on ContentPage { - system { - locale - uid - } - url - title -} diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 07eebacb6..c8c50b246 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -1,57 +1,71 @@ #import "../Fragments/Image.graphql" +#import "../Fragments/PageLink/AccountPageLink.graphql" +#import "../Fragments/PageLink/ContentPageLink.graphql" +#import "../Fragments/PageLink/LoyaltyPageLink.graphql" query GetLoyaltyPage($locale: String!, $url: String!) { all_loyalty_page(where: { url: $url, locale: $locale }) { items { - content { - ... on LoyaltyPageContentLoyaltyLevels { + blocks { + ... on LoyaltyPageBlocksDynamicContent { __typename - loyalty_levels { - heading - sub_heading - level_card { - loyalty_level + dynamic_content { + title + preamble + component + link { + text + pageConnection { + edges { + node { + ...ContentPageLink + ...LoyaltyPageLink + } + } + } } } } - ... on LoyaltyPageContentCardGrid { + ... on LoyaltyPageBlocksCardGrid { __typename card_grid { heading - subheading + preamble cards { referenceConnection { edges { node { - ... on LoyaltyPage { - system { - locale - uid - } - url - title - } - ... on ContentPage { - system { - locale - uid - } - url - title - } - ... on AccountPage { - system { - locale - uid - } - url - title - } + __typename + ...LoyaltyPageLink + ...ContentPageLink + ...AccountPageLink } } } heading - subheading + preamble + } + } + } + ... on LoyaltyPageBlocksContent { + __typename + content { + content { + json + embedded_itemsConnection { + edges { + node { + ... on SysAsset { + title + url + dimension { + width + height + } + } + } + } + } } } } @@ -62,6 +76,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { __typename join_loyalty_contact { title + preamble contact { ... on LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact { __typename @@ -71,17 +86,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } login_button_text - body { - json - embedded_itemsConnection(limit: 30) { - edges { - node { - __typename - ...Image - } - } - } - } } } ... on LoyaltyPageSidebarContent { @@ -92,6 +96,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { embedded_itemsConnection { edges { node { + __typename ...Image } } @@ -108,24 +113,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { title } } - seo_metadata { - description - title - imageConnection { - edges { - node { - file_size - filename - dimension { - height - width - } - url - title - } - } - } - } } system { uid diff --git a/lib/graphql/Query/NavigationMyPages.graphql b/lib/graphql/Query/NavigationMyPages.graphql index 8968c61f4..a4663d6dd 100644 --- a/lib/graphql/Query/NavigationMyPages.graphql +++ b/lib/graphql/Query/NavigationMyPages.graphql @@ -1,4 +1,6 @@ -#import "../Fragments/PageLinks.graphql" +#import "../Fragments/PageLink/AccountPageLink.graphql" +#import "../Fragments/PageLink/ContentPageLink.graphql" +#import "../Fragments/PageLink/LoyaltyPageLink.graphql" query GetNavigationMyPages($locale: String!) { all_navigation_my_pages(locale: $locale) { diff --git a/public/_static/icons/scandic-friends.png b/public/_static/icons/scandic-friends.png new file mode 100644 index 0000000000000000000000000000000000000000..32234040946c620f842dd60d77eccbb056c3bbd5 GIT binary patch literal 7252 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 ih=Jemra0}A8~+cXq;~J$z#Ak00000 + export type JoinLoyaltyContact = { join_loyalty_contact: { - title: string - contact: Typename< - Contact, - "LoyaltyPageSidebarLoyaltyJoinContactBlockContactContact" - > + title?: string + preamble?: string + contact: JoinLoyaltyContactEnum[] login_button_text: string - body: { - embedded_itemsConnection: Edges - json: RTEDocument - } } } @@ -48,36 +51,52 @@ export type Sidebar = SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact > -enum ContentBlocks { - LoyaltyPageContentLoyaltyLevels = "LoyaltyPageContentLoyaltyLevels", - LoyaltyPageContentCardGrid = "LoyaltyPageContentCardGrid", +export enum LoyaltyBlocksTypenameEnum { + LoyaltyPageBlocksDynamicContent = "LoyaltyPageBlocksDynamicContent", + LoyaltyPageBlocksCardGrid = "LoyaltyPageBlocksCardGrid", + LoyaltyPageBlocksContent = "LoyaltyPageBlocksContent", } type CardGrid = { card_grid: { heading: string - subheading: string + preamble: string cards: { referenceConnection: Edges heading: string - subheading: string + preamble: string } } } -type LoyaltyLevels = { - loyalty_levels: { - heading: string - sub_heading?: string - level_card: { - loyalty_level: number - }[] +type Content = { + content: { + embedded_itemsConnection: Edges + json: RTEDocument } } -export type Content = - | Typename - | Typename +type LoyaltyComponent = "loyalty_levels" | "how_it_works" | "overview_table" + +type DynamicContent = { + dynamic_content: { + title: string + preamble?: string + component: LoyaltyComponent + link: { + text?: string + page: Edges + } + } +} + +export type Blocks = + | Typename + | Typename< + DynamicContent, + LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent + > + | Typename export type Breadcrumb = { href: string @@ -91,7 +110,7 @@ export type Breadcrumbs = { export type LoyaltyPage = { sidebar: Sidebar[] - content: Content[] + blocks: Blocks[] web: { breadcrumbs: Breadcrumbs } From 2ddabf1e50bcd5ad5360507e279fa81050d12b41 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Tue, 23 Apr 2024 16:54:34 +0200 Subject: [PATCH 05/11] feat: add blocks for loyalty page --- .../(public)/loyalty-page/page.module.css | 4 +- .../DynamicContent/HowItWorks/index.tsx | 3 + .../DynamicContent/LoyaltyLevels/index.tsx | 26 +++++ .../DynamicContent/OverviewTable/index.tsx | 3 + .../DynamicContent/dynamicContent.module.css | 0 .../Loyalty/Blocks/DynamicContent/index.tsx | 34 ++++++ components/Loyalty/Blocks/index.tsx | 3 +- .../Contact/ContactRow/contactRow.module.css | 21 ++++ .../JoinLoyalty/Contact/ContactRow/index.tsx | 25 +++++ .../JoinLoyalty/Contact/contact.module.css | 16 +++ .../Sidebar/JoinLoyalty/Contact/index.tsx | 27 ++++- .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 10 +- lib/graphql/Query/LoyaltyPage.graphql | 3 +- server/index.ts | 2 + server/routers/loyalty/index.ts | 5 + server/routers/loyalty/query.ts | 27 +++++ server/routers/loyalty/temp.ts | 68 ++++++++++++ types/components/loyalty/blocks.ts | 47 ++++++++ types/components/loyalty/sidebar.ts | 20 ++++ types/requests/contactConfig.ts | 103 +++++++++++------- types/requests/loyaltyPage.ts | 63 ++--------- utils/contactConfig.ts | 17 +++ 22 files changed, 418 insertions(+), 109 deletions(-) create mode 100644 components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx create mode 100644 components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx create mode 100644 components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx create mode 100644 components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css create mode 100644 components/Loyalty/Blocks/DynamicContent/index.tsx create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/contactRow.module.css create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx create mode 100644 components/Loyalty/Sidebar/JoinLoyalty/Contact/contact.module.css create mode 100644 server/routers/loyalty/index.ts create mode 100644 server/routers/loyalty/query.ts create mode 100644 server/routers/loyalty/temp.ts create mode 100644 types/components/loyalty/blocks.ts create mode 100644 types/components/loyalty/sidebar.ts create mode 100644 utils/contactConfig.ts diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.module.css b/app/[lang]/(live)/(public)/loyalty-page/page.module.css index c8531f21d..2fc0d7f7a 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/page.module.css @@ -15,8 +15,8 @@ @media screen and (min-width: 950px) { .content { - gap: 10rem; - grid-template-columns: 25rem 1fr; + gap: 2.7rem; + grid-template-columns: 30rem 1fr; padding-bottom: 17.5rem; padding-left: 2.4rem; padding-right: 2.4rem; diff --git a/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx new file mode 100644 index 000000000..26b3880bd --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx @@ -0,0 +1,3 @@ +export default function HowItWorks() { + return
+} diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx new file mode 100644 index 000000000..52c542a17 --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx @@ -0,0 +1,26 @@ +import { serverClient } from "@/lib/trpc/server" + +export default async function LoyaltyLevels() { + const data = await serverClient().loyalty.levels.all() + + return ( +
+ {data.map((level) => ( + + ))} +
+ ) +} + +type LevelCardProps = { + level: { + tier: number + name: string + requiredPoints: number + requiredNights: string + topBenefits: string[] + } +} +function LevelCard({ level }: LevelCardProps) { + return
+} diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx new file mode 100644 index 000000000..c90c221e6 --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx @@ -0,0 +1,3 @@ +export default function OverviewTable() { + return
+} diff --git a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css new file mode 100644 index 000000000..e69de29bb diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx new file mode 100644 index 000000000..d82699024 --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -0,0 +1,34 @@ +import Title from "@/components/Title" +import { + DynamicContentProps, + LoyaltyComponent, + LoyaltyComponentEnum, +} from "@/types/components/loyalty/blocks" + +function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) { + switch (component) { + case LoyaltyComponentEnum.how_it_works: + return

How it works

+ case LoyaltyComponentEnum.loyalty_levels: + return

loyalty_levels

+ case LoyaltyComponentEnum.overview_table: + return

overview_table

+ default: + return null + } +} + +export default function DynamicContent({ + dynamicContent, +}: DynamicContentProps) { + return ( +
+
+ {dynamicContent.title} + {dynamicContent.preamble ?

{dynamicContent.preamble}

: null} + {dynamicContent.link ? <> : null} +
+ +
+ ) +} diff --git a/components/Loyalty/Blocks/index.tsx b/components/Loyalty/Blocks/index.tsx index 659a5e22d..c496fa3e7 100644 --- a/components/Loyalty/Blocks/index.tsx +++ b/components/Loyalty/Blocks/index.tsx @@ -1,4 +1,5 @@ import JsonToHtml from "@/components/JsonToHtml" +import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent" import { Blocks as BlocksType, @@ -18,7 +19,7 @@ export function Blocks({ blocks }: { blocks: BlocksType[] }) { /> ) case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: - return

Dynamic

+ return default: return null } diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/contactRow.module.css b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/contactRow.module.css new file mode 100644 index 000000000..47bd6b899 --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/contactRow.module.css @@ -0,0 +1,21 @@ +.container { + display: grid; + text-align: center; + gap: 0.4rem; + padding: 1rem; +} + +.title { + font-family: var(--fira-sans); + font-size: 1.6rem; + font-weight: 700; + + margin: 0; +} + +.value { + font-family: var(--fira-sans); + font-size: 1.6rem; + font-weight: 400; + margin: 0; +} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx new file mode 100644 index 000000000..d7a95aec9 --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx @@ -0,0 +1,25 @@ +import { serverClient } from "@/lib/trpc/server" +import { getValueFromContactConfig } from "@/utils/contactConfig" + +import styles from "./contactRow.module.css" + +import { Lang } from "@/constants/languages" +import type { ContactFields } from "@/types/requests/contactConfig" + +export default async function ContactRow({ + contact, +}: { + contact: ContactFields +}) { + const data = await serverClient().contentstack.contactConfig.get({ + lang: Lang.en, + }) + + const val = getValueFromContactConfig(contact.contact_field, data) + return ( +
+

{contact.display_text}

+

{val}

+
+ ) +} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/contact.module.css b/components/Loyalty/Sidebar/JoinLoyalty/Contact/contact.module.css new file mode 100644 index 000000000..38dbcdf35 --- /dev/null +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/contact.module.css @@ -0,0 +1,16 @@ +.contactContainer { + display: none; +} + +@media screen and (min-width: 950px) { + .contactContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-top: 0.5px solid var(--Base-Border-Disabled); + padding: 3.4rem; + text-align: center; + gap: 6.2rem; + } +} diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx index 351362cff..639219dd4 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx @@ -1,8 +1,25 @@ -import { Lang } from "@/constants/languages" -import { serverClient } from "@/lib/trpc/server" +import Title from "@/components/Title" +import ContactRow from "./ContactRow" -export default function Contact({ lang }: { lang: Lang }) { - const data = serverClient().contentstack.contactConfig.get({ lang }) +import styles from "./contact.module.css" - return
+import { JoinLoyaltyContactTypenameEnum } from "@/types/requests/loyaltyPage" +import type { ContactProps } from "@/types/components/loyalty/sidebar" + +export default async function Contact({ contactBlock }: ContactProps) { + return ( +
+ Contact us +
+ {contactBlock.map(({ contact, __typename }) => { + switch (__typename) { + case JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact: + return + default: + return null + } + })} +
+
+ ) } diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx index ec44c4df9..7e2586f24 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx @@ -4,10 +4,10 @@ import Button from "@/components/TempDesignSystem/Button" import Link from "@/components/TempDesignSystem/Link" import Image from "@/components/Image" -import type { JoinLoyaltyContact } from "@/types/requests/loyaltyPage" - import styles from "./joinLoyalty.module.css" +import type { JoinLoyaltyContact } from "@/types/requests/loyaltyPage" + export default function JoinLoyaltyContact({ block, }: { @@ -35,11 +35,7 @@ export default function JoinLoyaltyContact({ - {block.contact - ? block.contact.map((contact, i) => ( - - )) - : null} + {block.contact && } ) } diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index c8c50b246..8c6891059 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -81,7 +81,8 @@ query GetLoyaltyPage($locale: String!, $url: String!) { ... on LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact { __typename contact { - contact_fields + display_text + contact_field } } } diff --git a/server/index.ts b/server/index.ts index f9aa0e258..ceb06b7fa 100644 --- a/server/index.ts +++ b/server/index.ts @@ -3,10 +3,12 @@ import { router } from "./trpc" /** Routers */ import { contentstackRouter } from "./routers/contentstack" import { userRouter } from "./routers/user" +import { loyaltyRouter } from "./routers/loyalty" export const appRouter = router({ contentstack: contentstackRouter, user: userRouter, + loyalty: loyaltyRouter, }) export type AppRouter = typeof appRouter diff --git a/server/routers/loyalty/index.ts b/server/routers/loyalty/index.ts new file mode 100644 index 000000000..412aa2846 --- /dev/null +++ b/server/routers/loyalty/index.ts @@ -0,0 +1,5 @@ +import { mergeRouters } from "@/server/trpc" + +import { lotaltyQueryRouter } from "./query" + +export const loyaltyRouter = mergeRouters(lotaltyQueryRouter) diff --git a/server/routers/loyalty/query.ts b/server/routers/loyalty/query.ts new file mode 100644 index 000000000..2c58d1773 --- /dev/null +++ b/server/routers/loyalty/query.ts @@ -0,0 +1,27 @@ +import { allLevels } from "./temp" +import { protectedProcedure, publicProcedure, router } from "@/server/trpc" + +function fakingRequest(payload: T): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(payload) + }, 1500) + }) +} + +export const lotaltyQueryRouter = router({ + levels: router({ + all: publicProcedure.query(async function ({ ctx }) { + // TODO: Make request to get user data from Scandic API + return await fakingRequest(allLevels) + }), + current: protectedProcedure.query(async function (opts) { + // TODO: Make request to get user data from Scandic API + return await fakingRequest<(typeof allLevels)[number]>(allLevels[1]) + }), + next: protectedProcedure.query(async function (opts) { + // TODO: Make request to get user data from Scandic API + return await fakingRequest<(typeof allLevels)[number]>(allLevels[2]) + }), + }), +}) diff --git a/server/routers/loyalty/temp.ts b/server/routers/loyalty/temp.ts new file mode 100644 index 000000000..a2cefa2a3 --- /dev/null +++ b/server/routers/loyalty/temp.ts @@ -0,0 +1,68 @@ +export const allLevels = [ + { + tier: 1, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, + { + tier: 2, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, + { + tier: 3, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, + { + tier: 4, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, + { + tier: 5, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, + { + tier: 6, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + }, +] diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts new file mode 100644 index 000000000..684974712 --- /dev/null +++ b/types/components/loyalty/blocks.ts @@ -0,0 +1,47 @@ +import { Embeds } from "@/types/requests/embeds" +import { PageLink } from "@/types/requests/myPages/navigation" +import { Edges } from "@/types/requests/utils/edges" +import { RTEDocument } from "@/types/rte/node" + +export enum LoyaltyComponentEnum { + loyalty_levels = "loyalty_levels", + how_it_works = "how_it_works", + overview_table = "overview_table", +} + +export type LoyaltyComponent = keyof typeof LoyaltyComponentEnum + +export type DynamicContentBlock = { + dynamic_content: { + title: string + preamble?: string + component: LoyaltyComponent + link: { + text?: string + page: Edges + } + } +} + +export type DynamicContentProps = { + dynamicContent: DynamicContentBlock["dynamic_content"] +} + +export type CardGrid = { + card_grid: { + heading: string + preamble: string + cards: { + referenceConnection: Edges + heading: string + preamble: string + } + } +} + +export type Content = { + content: { + embedded_itemsConnection: Edges + json: RTEDocument + } +} diff --git a/types/components/loyalty/sidebar.ts b/types/components/loyalty/sidebar.ts new file mode 100644 index 000000000..e41441994 --- /dev/null +++ b/types/components/loyalty/sidebar.ts @@ -0,0 +1,20 @@ +import { ContactFields } from "@/types/requests/contactConfig" +import { Embeds } from "@/types/requests/embeds" +import { JoinLoyaltyContactEnum } from "@/types/requests/loyaltyPage" +import { Edges } from "@/types/requests/utils/edges" +import { RTEDocument } from "@/types/rte/node" + +export type SidebarContent = { + content: { + embedded_itemsConnection: Edges + json: RTEDocument + } +} + +export type Contact = { + contact: ContactFields +} + +export type ContactProps = { + contactBlock: JoinLoyaltyContactEnum[] +} diff --git a/types/requests/contactConfig.ts b/types/requests/contactConfig.ts index ba34c2b0e..642f6d4ce 100644 --- a/types/requests/contactConfig.ts +++ b/types/requests/contactConfig.ts @@ -1,48 +1,73 @@ import { AllRequestResponse } from "./utils/all" export type ContactConfig = { - items: [ - { - email: { - name: string - address: string - } - email_loyalty: { - name: string - address: string - } - mailing_address: { - zip: string - street: string - name: string - city: string - country: string - } - phone: { - number: string - naem: string - } - phone_loyalty: { - number: string - name: string - } - visiting_address: { - zip: string - country: string - city: string - street: string - } - }, - ] + email: { + name?: string + address?: string + } + email_loyalty: { + name?: string + address?: string + } + mailing_address: { + zip?: string + street?: string + name?: string + city?: string + country?: string + } + phone: { + number?: string + name?: string + } + phone_loyalty: { + number?: string + name?: string + } + visiting_address: { + zip?: string + country?: string + city?: string + street?: string + } } -type FlattenKeys = T extends object - ? { - [K in keyof T]-?: `${K & string}.${FlattenKeys}` - }[keyof T] - : "" -export type ContactField = FlattenKeys +export enum ContactFieldGroupsEnum { + email = "email", + email_loyalty = "email_loyalty", + mailing_address = "mailing_address", + phone = "phone", + phone_loyalty = "phone_loyalty", + visiting_address = "visiting_address", +} + +export type ContactFieldGroups = keyof typeof ContactFieldGroupsEnum export type GetContactConfigData = { all_contact_config: AllRequestResponse } + +// Utility types that extract the possible strings of ContactConfigField, +// Which is all the dot notated values of ContactConfig (for example: 'email.name') +type PathsToStringProps = T extends string + ? [] + : { + [K in Extract]: [K, ...PathsToStringProps] + }[Extract] + +type Join = T extends [] + ? never + : T extends [infer F] + ? F + : T extends [infer F, ...infer R] + ? F extends string + ? `${F}${D}${Join, D>}` + : never + : string + +type ContactConfigField = Join, "."> + +export type ContactFields = { + display_text?: string + contact_field: ContactConfigField +} diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts index 2318ffdd2..8ebe53154 100644 --- a/types/requests/loyaltyPage.ts +++ b/types/requests/loyaltyPage.ts @@ -1,10 +1,11 @@ -import { PageLink } from "./myPages/navigation" -import { Edges } from "./utils/edges" import type { AllRequestResponse } from "./utils/all" import type { Typename } from "./utils/typename" -import type { RTEDocument } from "../rte/node" -import type { Embeds } from "./embeds" -import type { ContactField } from "./contactConfig" +import { Contact, SidebarContent } from "../components/loyalty/sidebar" +import { + CardGrid, + Content, + DynamicContentBlock, +} from "../components/loyalty/blocks" export enum SidebarTypenameEnum { LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact", @@ -13,24 +14,11 @@ export enum SidebarTypenameEnum { export type SidebarTypename = keyof typeof SidebarTypenameEnum -type SidebarContent = { - content: { - embedded_itemsConnection: Edges - json: RTEDocument - } -} - -type Contact = { - contact: { - contact_fields: ContactField[] - } -} - -enum JoinLoyaltyContactTypenameEnum { +export enum JoinLoyaltyContactTypenameEnum { LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact = "LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact", } -type JoinLoyaltyContactEnum = Typename< +export type JoinLoyaltyContactEnum = Typename< Contact, JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact > @@ -57,43 +45,10 @@ export enum LoyaltyBlocksTypenameEnum { LoyaltyPageBlocksContent = "LoyaltyPageBlocksContent", } -type CardGrid = { - card_grid: { - heading: string - preamble: string - cards: { - referenceConnection: Edges - heading: string - preamble: string - } - } -} - -type Content = { - content: { - embedded_itemsConnection: Edges - json: RTEDocument - } -} - -type LoyaltyComponent = "loyalty_levels" | "how_it_works" | "overview_table" - -type DynamicContent = { - dynamic_content: { - title: string - preamble?: string - component: LoyaltyComponent - link: { - text?: string - page: Edges - } - } -} - export type Blocks = | Typename | Typename< - DynamicContent, + DynamicContentBlock, LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent > | Typename diff --git a/utils/contactConfig.ts b/utils/contactConfig.ts new file mode 100644 index 000000000..e94eef75f --- /dev/null +++ b/utils/contactConfig.ts @@ -0,0 +1,17 @@ +import { + ContactConfig, + ContactFieldGroups, +} from "@/types/requests/contactConfig" + +export function getValueFromContactConfig( + keyStrings: string, + data: ContactConfig +): string | undefined { + const [groupName, key] = keyStrings.split(".") as [ + ContactFieldGroups, + keyof ContactConfig[ContactFieldGroups], + ] + const fieldGroup = data[groupName] + + return fieldGroup[key] +} From 00f30811cfcd572d2ced860823cccae19d3d3c6d Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Fri, 26 Apr 2024 08:19:19 +0200 Subject: [PATCH 06/11] feat: add card grid component --- .../(public)/loyalty-page/layout.module.css | 3 - .../(live)/(public)/loyalty-page/layout.tsx | 4 - .../(public)/loyalty-page/page.module.css | 3 +- .../(live)/(public)/loyalty-page/page.tsx | 21 ++--- .../Blocks/CardGrid/cardGrid.module.css | 28 ++++++ components/Loyalty/Blocks/CardGrid/index.tsx | 50 +++++++++++ .../HowItWorks/howItWorks.module.css | 11 +++ .../DynamicContent/HowItWorks/index.tsx | 12 ++- .../DynamicContent/LoyaltyLevels/index.tsx | 56 ++++++++---- .../LoyaltyLevels/loyaltyLevels.module.css | 83 ++++++++++++++++++ .../DynamicContent/dynamicContent.module.css | 28 ++++++ .../Loyalty/Blocks/DynamicContent/index.tsx | 59 ++++++++++--- components/Loyalty/Blocks/index.tsx | 4 +- .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 15 ++-- .../JoinLoyalty/joinLoyalty.module.css | 7 +- components/Loyalty/Sidebar/index.tsx | 1 + .../TempDesignSystem/Card/card.module.css | 15 ++++ components/TempDesignSystem/Card/card.ts | 9 ++ components/TempDesignSystem/Card/index.tsx | 38 ++++++++ lib/graphql/Query/ContentTypeUid.graphql | 17 ++++ lib/graphql/Query/LoyaltyPage.graphql | 10 +-- middlewares/cmsContent.ts | 69 +++++++++++++-- public/_static/icons/new-friend.png | Bin 0 -> 3543 bytes .../routers/contentstack/breadcrumbs/query.ts | 7 +- .../routers/contentstack/loyaltyPage/input.ts | 10 +++ server/routers/loyalty/query.ts | 3 +- server/routers/loyalty/temp.ts | 18 ++++ types/components/loyalty/blocks.ts | 53 ++++++----- types/components/loyalty/sidebar.ts | 4 +- types/requests/contactConfig.ts | 3 +- types/requests/loyaltyPage.ts | 50 +++++++---- types/requests/utils/asset.ts | 2 +- utils/contactConfig.ts | 3 +- 33 files changed, 575 insertions(+), 121 deletions(-) create mode 100644 components/Loyalty/Blocks/CardGrid/cardGrid.module.css create mode 100644 components/Loyalty/Blocks/CardGrid/index.tsx create mode 100644 components/Loyalty/Blocks/DynamicContent/HowItWorks/howItWorks.module.css create mode 100644 components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css create mode 100644 components/TempDesignSystem/Card/card.module.css create mode 100644 components/TempDesignSystem/Card/card.ts create mode 100644 components/TempDesignSystem/Card/index.tsx create mode 100644 lib/graphql/Query/ContentTypeUid.graphql create mode 100644 public/_static/icons/new-friend.png create mode 100644 server/routers/contentstack/loyaltyPage/input.ts diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css index 4ddc7b911..e17395732 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css @@ -1,10 +1,7 @@ .layout { --max-width: 101.4rem; - --header-height: 4.5rem; display: grid; font-family: var(--ff-fira-sans); - grid-template-rows: var(--header-height) auto 1fr; - min-height: 100dvh; background-color: var(--Brand-Coffee-Subtle); } diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.tsx b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx index e925346f6..5812eca64 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/layout.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx @@ -1,20 +1,16 @@ import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts" -import Header from "@/components/MyPages/Header" - import styles from "./layout.module.css" import type { MyPagesLayoutProps } from "@/types/components/myPages/layout" export default async function LoyaltyPagesLayout({ children, - params, }: React.PropsWithChildren) { return (
-
{children}
) diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.module.css b/app/[lang]/(live)/(public)/loyalty-page/page.module.css index 2fc0d7f7a..3311af02b 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/page.module.css @@ -9,8 +9,7 @@ .blocks { display: grid; gap: 4.2rem; - padding-left: 2rem; - padding-right: 2rem; + padding: 1.6rem; } @media screen and (min-width: 950px) { diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.tsx b/app/[lang]/(live)/(public)/loyalty-page/page.tsx index 88fab0647..152f7ed34 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/page.tsx @@ -1,14 +1,15 @@ import { notFound } from "next/navigation" + import { serverClient } from "@/lib/trpc/server" -import MaxWidth from "@/components/MaxWidth" -import Sidebar from "@/components/Loyalty/Sidebar" import { Blocks } from "@/components/Loyalty/Blocks" - -import type { LangParams, PageArgs, UriParams } from "@/types/params" +import Sidebar from "@/components/Loyalty/Sidebar" +import MaxWidth from "@/components/MaxWidth" import styles from "./page.module.css" +import type { LangParams, PageArgs, UriParams } from "@/types/params" + export default async function LoyaltyPage({ params, searchParams, @@ -19,12 +20,12 @@ export default async function LoyaltyPage({ } const loyaltyPage = await serverClient().contentstack.loyaltyPage.get({ - uri: searchParams.uri, - lang: params.lang, + href: searchParams.uri, + locale: params.lang, }) return ( - +
-
+ -
- + +
) } catch (err) { return notFound() diff --git a/components/Loyalty/Blocks/CardGrid/cardGrid.module.css b/components/Loyalty/Blocks/CardGrid/cardGrid.module.css new file mode 100644 index 000000000..915c86f35 --- /dev/null +++ b/components/Loyalty/Blocks/CardGrid/cardGrid.module.css @@ -0,0 +1,28 @@ +.container { + display: grid; + gap: 2.4rem; +} + +.subtitle { + margin: 0; +} + +.titleContainer { + display: grid; + gap: 0.8rem; +} + +.cardContainer { + display: grid; + gap: 1.6rem; +} + +@media screen and (min-width: 950px) { + .cardContainer { + grid-template-columns: 1fr 1fr; + } + + .cardWrapper:last-child { + grid-column: span 2; + } +} diff --git a/components/Loyalty/Blocks/CardGrid/index.tsx b/components/Loyalty/Blocks/CardGrid/index.tsx new file mode 100644 index 000000000..b6568ce2b --- /dev/null +++ b/components/Loyalty/Blocks/CardGrid/index.tsx @@ -0,0 +1,50 @@ +import { _ } from "@/lib/translation" + +import Card from "@/components/TempDesignSystem/Card" +import Title from "@/components/Title" + +import styles from "./cardGrid.module.css" + +import { CardGridProps, CardProps } from "@/types/components/loyalty/blocks" + +export default function CardGrid({ card_grid }: CardGridProps) { + return ( +
+
+ + {card_grid.title} + + {card_grid.subtitle ? ( + + {card_grid.subtitle} + + ) : null} +
+
+ {card_grid.cards.map((card, i) => ( + + ))} +
+
+ ) +} + +function CardWrapper({ card }: CardProps) { + const link = card.referenceConnection.edges.length + ? { + href: card.referenceConnection.edges[0].node.url, + title: _("Read more"), + } + : undefined + + return ( +
+ +
+ ) +} diff --git a/components/Loyalty/Blocks/DynamicContent/HowItWorks/howItWorks.module.css b/components/Loyalty/Blocks/DynamicContent/HowItWorks/howItWorks.module.css new file mode 100644 index 000000000..27a9a7561 --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/HowItWorks/howItWorks.module.css @@ -0,0 +1,11 @@ +.container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 37rem; + border-radius: 1.6rem; + background-color: var(--Base-Fill-Normal); + text-align: center; + margin-right: 1.6rem; +} diff --git a/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx index 26b3880bd..426cda514 100644 --- a/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx @@ -1,3 +1,13 @@ +import Title from "@/components/Title" + +import styles from "./howItWorks.module.css" + export default function HowItWorks() { - return
+ return ( +
+ + How it works Placeholder + +
+ ) } diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx index 52c542a17..888dc3d99 100644 --- a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx @@ -1,26 +1,50 @@ +import { Check } from "react-feather" + +import { _ } from "@/lib/translation" import { serverClient } from "@/lib/trpc/server" +import Image from "@/components/Image" +import Button from "@/components/TempDesignSystem/Button" +import Link from "@/components/TempDesignSystem/Link" +import Title from "@/components/Title" + +import styles from "./loyaltyLevels.module.css" + +import { LevelCardProps } from "@/types/components/loyalty/blocks" + export default async function LoyaltyLevels() { const data = await serverClient().loyalty.levels.all() return ( -
- {data.map((level) => ( - +
+
+ {data.map((level) => ( + + ))} +
+
+ +
+
+ ) +} + +function LevelCard({ level }: LevelCardProps) { + return ( +
+ {level.tier} + {level.name} +

+ {level.requiredPoints} {_("or")} {level.requiredNights} {_("nights")} +

+ {level.topBenefits.map((benefit) => ( +

+ + {benefit} +

))}
) } - -type LevelCardProps = { - level: { - tier: number - name: string - requiredPoints: number - requiredNights: string - topBenefits: string[] - } -} -function LevelCard({ level }: LevelCardProps) { - return
-} diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css new file mode 100644 index 000000000..affa9a47e --- /dev/null +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/loyaltyLevels.module.css @@ -0,0 +1,83 @@ +.container { + display: grid; + gap: 2.4rem; +} + +.buttonContainer { + display: flex; + justify-content: center; +} + +.cardContainer { + display: flex; + gap: 0.8rem; + overflow-x: auto; + padding-right: 1.6rem; + margin-right: -1.6rem; + /* Hide scrollbar IE and Edge */ + -ms-overflow-style: none; + /* Hide Scrollbar Firefox */ + scrollbar-width: none; +} + +.card { + display: flex; + flex-direction: column; + align-items: center; + height: 37rem; + min-width: 32rem; + padding: 4rem 1rem; + background-color: var(--Base-Fill-Normal); + border-radius: 1.6rem; + gap: 1.8rem; +} + +.qualifications { + margin: 0; + font-size: var(--typography-Body-Bold-fontSize); + line-height: var(--typography-Body-Bold-lineHeight); + /* font-weight: var(--typography-Body-Bold-fontWeight); -- Tokens not parsable*/ + font-weight: 600; +} + +.benefits { + font-family: var(--fira-sans); + font-size: var(--typography-Body-Regular-fontSize); + line-height: var(--typography-Body-Regular-lineHeight); + margin: 0; + text-align: center; +} + +.icon { + font-family: var(--fira-sans); + position: relative; + top: 0.3rem; + height: 1.4rem; +} + +@media screen and (min-width: 950px) { + .container { + gap: 3.2rem; + } + .cardContainer { + display: grid; + grid-template-columns: repeat( + 12, + auto + ); /* Three columns in the first row */ + padding-right: 0; + margin-right: 0rem; + } + + .card { + min-width: auto; + } + + .card:nth-child(-n + 3) { + grid-column: span 4; + } + + .card:nth-last-child(-n + 4) { + grid-column: span 3; + } +} diff --git a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css index e69de29bb..a448e08f6 100644 --- a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css +++ b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css @@ -0,0 +1,28 @@ +.container { + display: grid; + gap: 2.4rem; + overflow: hidden; + margin-right: -1.6rem; + padding-right: 1.6rem; +} + +.titleContainer { + display: grid; + grid-template-areas: "title link"; + grid-template-columns: 1fr max-content; + padding-bottom: 0.8rem; +} + +.title { + grid-area: title; +} + +.link { + grid-area: link; + font-size: var(--typography-Body-Underlined-fontSize); + color: var(--some-black-color, #000); +} + +.subtitle { + margin: 0; +} diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx index d82699024..0bb4fe45b 100644 --- a/components/Loyalty/Blocks/DynamicContent/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -1,18 +1,27 @@ +import Link from "@/components/TempDesignSystem/Link" import Title from "@/components/Title" + +import HowItWorks from "./HowItWorks" +import LoyaltyLevels from "./LoyaltyLevels" +import OverviewTable from "./OverviewTable" + +import styles from "./dynamicContent.module.css" + +import { DynamicContentProps } from "@/types/components/loyalty/blocks" import { - DynamicContentProps, LoyaltyComponent, LoyaltyComponentEnum, -} from "@/types/components/loyalty/blocks" +} from "@/types/requests/loyaltyPage" function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) { switch (component) { case LoyaltyComponentEnum.how_it_works: - return

How it works

+ return case LoyaltyComponentEnum.loyalty_levels: - return

loyalty_levels

+ return case LoyaltyComponentEnum.overview_table: - return

overview_table

+ // TODO: IMPLEMENT OVERVIEW TABLE! + return default: return null } @@ -21,14 +30,44 @@ function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) { export default function DynamicContent({ dynamicContent, }: DynamicContentProps) { + const link = dynamicContent.link.pageConnection.edges.length + ? dynamicContent.link.pageConnection.edges[0].node.url + : null return ( -
+
- {dynamicContent.title} - {dynamicContent.preamble ?

{dynamicContent.preamble}

: null} - {dynamicContent.link ? <> : null} +
+ {dynamicContent.title && ( + + {dynamicContent.title} + + )} + {link && ( + + {dynamicContent.link.text} + + )} +
+ {dynamicContent.subtitle && ( + + {dynamicContent.subtitle} + + )}
- +
+ +
) } diff --git a/components/Loyalty/Blocks/index.tsx b/components/Loyalty/Blocks/index.tsx index c496fa3e7..3b8b77744 100644 --- a/components/Loyalty/Blocks/index.tsx +++ b/components/Loyalty/Blocks/index.tsx @@ -1,6 +1,8 @@ import JsonToHtml from "@/components/JsonToHtml" import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent" +import CardGrid from "./CardGrid" + import { Blocks as BlocksType, LoyaltyBlocksTypenameEnum, @@ -10,7 +12,7 @@ export function Blocks({ blocks }: { blocks: BlocksType[] }) { return blocks.map((block) => { switch (block.__typename) { case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: - return

Cards

+ return case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: return ( {block.preamble &&

{block.preamble}

}
- Already a friend?
- Click here to log in + {_("Already a friend?")}
+ {_("Click here to log in")}
diff --git a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css index 8c3b0ac84..a2983576f 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css +++ b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css @@ -2,7 +2,6 @@ display: grid; font-weight: 600; background-color: var(--Base-Background-Elevated); - border-radius: 32px 4px 4px 32px; } .wrapper { @@ -10,7 +9,7 @@ align-items: center; flex-direction: column; gap: 2rem; - padding: 4rem 2rem; + padding: 6rem 2rem; } .preamble { @@ -37,6 +36,10 @@ } @media screen and (min-width: 950px) { + .container { + border-radius: 32px 4px 4px 32px; + } + .wrapper { gap: 3rem; } diff --git a/components/Loyalty/Sidebar/index.tsx b/components/Loyalty/Sidebar/index.tsx index 94cd937d2..f6128fcbe 100644 --- a/components/Loyalty/Sidebar/index.tsx +++ b/components/Loyalty/Sidebar/index.tsx @@ -1,4 +1,5 @@ import JsonToHtml from "@/components/JsonToHtml" + import JoinLoyaltyContact from "./JoinLoyalty" import { Sidebar, SidebarTypenameEnum } from "@/types/requests/loyaltyPage" diff --git a/components/TempDesignSystem/Card/card.module.css b/components/TempDesignSystem/Card/card.module.css new file mode 100644 index 000000000..5f8f8a3e6 --- /dev/null +++ b/components/TempDesignSystem/Card/card.module.css @@ -0,0 +1,15 @@ +.linkCard { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 37rem; + width: 100%; + margin-right: 1.6rem; + border-radius: 1.6rem; + gap: 1rem; + padding: 1.6rem; + + background-color: var(--Base-Fill-Normal); + text-align: center; +} diff --git a/components/TempDesignSystem/Card/card.ts b/components/TempDesignSystem/Card/card.ts new file mode 100644 index 000000000..3c346f423 --- /dev/null +++ b/components/TempDesignSystem/Card/card.ts @@ -0,0 +1,9 @@ +export type CardProps = { + link?: { + href: string + title: string + } + title?: string + subtitle?: string + openInNewTab?: boolean +} diff --git a/components/TempDesignSystem/Card/index.tsx b/components/TempDesignSystem/Card/index.tsx new file mode 100644 index 000000000..11523155c --- /dev/null +++ b/components/TempDesignSystem/Card/index.tsx @@ -0,0 +1,38 @@ +import { _ } from "@/lib/translation" + +import Title from "@/components/Title" + +import Button from "../Button" +import Link from "../Link" +import { CardProps } from "./card" + +import styles from "./card.module.css" + +export default function Card({ + link, + subtitle, + title, + openInNewTab = false, +}: CardProps) { + return ( +
+ {title ? ( + + {title} + + ) : null} + {subtitle ? ( + + {subtitle} + + ) : null} + {link ? ( + + ) : null} +
+ ) +} diff --git a/lib/graphql/Query/ContentTypeUid.graphql b/lib/graphql/Query/ContentTypeUid.graphql new file mode 100644 index 000000000..57dd92233 --- /dev/null +++ b/lib/graphql/Query/ContentTypeUid.graphql @@ -0,0 +1,17 @@ +query GetContentTypeUid($locale: String!, $url: String!) { + all_content_page(where: { url: $url, locale: $locale }) { + items { + __typename + } + } + all_current_blocks_page(where: { url: $url, locale: $locale }) { + items { + __typename + } + } + all_loyalty_page(where: { url: $url, locale: $locale }) { + items { + __typename + } + } +} diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 8c6891059..1efefc2c9 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -11,7 +11,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { __typename dynamic_content { title - preamble + subtitle component link { text @@ -29,8 +29,8 @@ query GetLoyaltyPage($locale: String!, $url: String!) { ... on LoyaltyPageBlocksCardGrid { __typename card_grid { - heading - preamble + title + subtitle cards { referenceConnection { edges { @@ -42,8 +42,8 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } } - heading - preamble + title + subtitle } } } diff --git a/middlewares/cmsContent.ts b/middlewares/cmsContent.ts index 183e4a2f5..202d23241 100644 --- a/middlewares/cmsContent.ts +++ b/middlewares/cmsContent.ts @@ -1,19 +1,70 @@ +import { DocumentNode } from "graphql" import { NextResponse } from "next/server" import { findLang } from "@/constants/languages" +import { env } from "@/env/server" +import GetContentTypeUid from "@/lib/graphql/Query/ContentTypeUid.graphql" import type { NextMiddleware } from "next/server" import { MiddlewareMatcher } from "@/types/middleware" +enum PageTypeEnum { + CurrentBlocksPage = "CurrentBlocksPage", + LoyaltyPage = "LoyaltyPage", + ContentPage = "contentPage", +} + +type GetContentTypeUidType = { + all_content_page: { + items: { + __typename: PageTypeEnum.ContentPage + }[] + } + all_loyalty_page: { + items: { + __typename: PageTypeEnum.LoyaltyPage + }[] + } + all_current_blocks_page: { + items: { + __typename?: PageTypeEnum.CurrentBlocksPage + }[] + } +} + +type PageType = keyof typeof PageTypeEnum + export const middleware: NextMiddleware = async (request) => { const { nextUrl } = request const lang = findLang(nextUrl.pathname) - const contentType = "loyaltyPage" const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "") const searchParams = new URLSearchParams(request.nextUrl.searchParams) + const print = (await import("graphql/language/printer")).print + const result = await fetch(env.CMS_URL, { + method: "POST", + headers: { + access_token: env.CMS_ACCESS_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: print(GetContentTypeUid as DocumentNode), + variables: { + locale: lang, + url: pathNameWithoutLang, + }, + }), + }) + + const pageTypeData = await result.json() + const pageType = pageTypeData.data as GetContentTypeUidType + + const contentType = Object.values(pageType) + .map((val) => val.items[0]) + .find((item) => item?.__typename)?.__typename + if (request.nextUrl.pathname.includes("preview")) { searchParams.set("uri", pathNameWithoutLang.replace("/preview", "")) return NextResponse.rewrite( @@ -23,14 +74,14 @@ export const middleware: NextMiddleware = async (request) => { searchParams.set("uri", pathNameWithoutLang) switch (contentType) { - // case "currentContentPage": - // return NextResponse.rewrite( - // new URL( - // `/${lang}/current-content-page?${searchParams.toString()}`, - // nextUrl - // ) - // ) - case "loyaltyPage": + case PageTypeEnum.CurrentBlocksPage: + return NextResponse.rewrite( + new URL( + `/${lang}/current-content-page?${searchParams.toString()}`, + nextUrl + ) + ) + case PageTypeEnum.LoyaltyPage: return NextResponse.rewrite( new URL(`/${lang}/loyalty-page?${searchParams.toString()}`, nextUrl) ) diff --git a/public/_static/icons/new-friend.png b/public/_static/icons/new-friend.png new file mode 100644 index 0000000000000000000000000000000000000000..0dfa36710d57700824decbc053fb55721870c287 GIT binary patch literal 3543 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 R_?Q3y002ovPDHLkV1hsltjGWW literal 0 HcmV?d00001 diff --git a/server/routers/contentstack/breadcrumbs/query.ts b/server/routers/contentstack/breadcrumbs/query.ts index 7fe6db0f7..e9e141cfa 100644 --- a/server/routers/contentstack/breadcrumbs/query.ts +++ b/server/routers/contentstack/breadcrumbs/query.ts @@ -1,10 +1,11 @@ +import { GetMyPagesBreadcrumbs } from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql" +import { request } from "@/lib/graphql/request" import { badRequestError, internalServerError } from "@/server/errors/trpc" -import { validateBreadcrumbsConstenstackSchema } from "./output" import { publicProcedure, router } from "@/server/trpc" -import { request } from "@/lib/graphql/request" -import { GetMyPagesBreadcrumbs } from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql" import { getBreadcrumbsInput } from "./input" +import { validateBreadcrumbsConstenstackSchema } from "./output" + import { GetMyPagesBreadcrumbsData } from "@/types/requests/myPages/breadcrumbs" export const breadcrumbsQueryRouter = router({ diff --git a/server/routers/contentstack/loyaltyPage/input.ts b/server/routers/contentstack/loyaltyPage/input.ts new file mode 100644 index 000000000..1cddb2b6f --- /dev/null +++ b/server/routers/contentstack/loyaltyPage/input.ts @@ -0,0 +1,10 @@ +import { z } from "zod" + +import { Lang } from "@/constants/languages" + +const langs = Object.keys(Lang) as [keyof typeof Lang] + +export const getLoyaltyPageInput = z.object({ + href: z.string().min(1, { message: "href is required" }), + locale: z.enum(langs), +}) diff --git a/server/routers/loyalty/query.ts b/server/routers/loyalty/query.ts index 2c58d1773..490623a88 100644 --- a/server/routers/loyalty/query.ts +++ b/server/routers/loyalty/query.ts @@ -1,6 +1,7 @@ -import { allLevels } from "./temp" import { protectedProcedure, publicProcedure, router } from "@/server/trpc" +import { allLevels } from "./temp" + function fakingRequest(payload: T): Promise { return new Promise((resolve) => { setTimeout(() => { diff --git a/server/routers/loyalty/temp.ts b/server/routers/loyalty/temp.ts index a2cefa2a3..e8fc56550 100644 --- a/server/routers/loyalty/temp.ts +++ b/server/routers/loyalty/temp.ts @@ -9,6 +9,7 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", }, { tier: 2, @@ -20,6 +21,7 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", }, { tier: 3, @@ -31,6 +33,7 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", }, { tier: 4, @@ -42,6 +45,7 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", }, { tier: 5, @@ -53,6 +57,7 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", }, { tier: 6, @@ -64,5 +69,18 @@ export const allLevels = [ "Always best price", "Book reward nights with points", ], + logo: "/_static/icons/new-friend.png", + }, + { + tier: 7, + name: "New Friend", + requiredPoints: 50000, + requiredNights: "X", + topBenefits: [ + "15% on food on weekends", + "Always best price", + "Book reward nights with points", + ], + logo: "/_static/icons/new-friend.png", }, ] diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts index 684974712..4efdc7f43 100644 --- a/types/components/loyalty/blocks.ts +++ b/types/components/loyalty/blocks.ts @@ -1,47 +1,46 @@ import { Embeds } from "@/types/requests/embeds" +import { DynamicContentBlock } from "@/types/requests/loyaltyPage" import { PageLink } from "@/types/requests/myPages/navigation" import { Edges } from "@/types/requests/utils/edges" import { RTEDocument } from "@/types/rte/node" -export enum LoyaltyComponentEnum { - loyalty_levels = "loyalty_levels", - how_it_works = "how_it_works", - overview_table = "overview_table", -} - -export type LoyaltyComponent = keyof typeof LoyaltyComponentEnum - -export type DynamicContentBlock = { - dynamic_content: { - title: string - preamble?: string - component: LoyaltyComponent - link: { - text?: string - page: Edges - } - } -} - export type DynamicContentProps = { dynamicContent: DynamicContentBlock["dynamic_content"] } +type Card = { + referenceConnection: Edges + title?: string + subtitle?: string + open_in_new_tab: boolean +} + +export type CardProps = { card: Card } + export type CardGrid = { card_grid: { - heading: string - preamble: string - cards: { - referenceConnection: Edges - heading: string - preamble: string - } + title?: string + subtitle?: string + cards: Card[] } } +export type CardGridProps = CardGrid + export type Content = { content: { embedded_itemsConnection: Edges json: RTEDocument } } + +export type LevelCardProps = { + level: { + tier: number + name: string + requiredPoints: number + requiredNights: string + topBenefits: string[] + logo: string + } +} diff --git a/types/components/loyalty/sidebar.ts b/types/components/loyalty/sidebar.ts index e41441994..2dcb38cbe 100644 --- a/types/components/loyalty/sidebar.ts +++ b/types/components/loyalty/sidebar.ts @@ -1,6 +1,6 @@ import { ContactFields } from "@/types/requests/contactConfig" import { Embeds } from "@/types/requests/embeds" -import { JoinLoyaltyContactEnum } from "@/types/requests/loyaltyPage" +import { JoinLoyaltyContactContact } from "@/types/requests/loyaltyPage" import { Edges } from "@/types/requests/utils/edges" import { RTEDocument } from "@/types/rte/node" @@ -16,5 +16,5 @@ export type Contact = { } export type ContactProps = { - contactBlock: JoinLoyaltyContactEnum[] + contactBlock: JoinLoyaltyContactContact[] } diff --git a/types/requests/contactConfig.ts b/types/requests/contactConfig.ts index 642f6d4ce..5995d432a 100644 --- a/types/requests/contactConfig.ts +++ b/types/requests/contactConfig.ts @@ -49,6 +49,7 @@ export type GetContactConfigData = { // Utility types that extract the possible strings of ContactConfigField, // Which is all the dot notated values of ContactConfig (for example: 'email.name') +// From: https://stackoverflow.com/questions/47057649/typescript-string-dot-notation-of-nested-object#47058976 type PathsToStringProps = T extends string ? [] : { @@ -65,7 +66,7 @@ type Join = T extends [] : never : string -type ContactConfigField = Join, "."> +export type ContactConfigField = Join, "."> export type ContactFields = { display_text?: string diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts index 8ebe53154..b97669164 100644 --- a/types/requests/loyaltyPage.ts +++ b/types/requests/loyaltyPage.ts @@ -1,24 +1,16 @@ +import { CardGrid, Content } from "../components/loyalty/blocks" +import { Contact, SidebarContent } from "../components/loyalty/sidebar" +import { PageLink } from "./myPages/navigation" +import { Edges } from "./utils/edges" + import type { AllRequestResponse } from "./utils/all" import type { Typename } from "./utils/typename" -import { Contact, SidebarContent } from "../components/loyalty/sidebar" -import { - CardGrid, - Content, - DynamicContentBlock, -} from "../components/loyalty/blocks" - -export enum SidebarTypenameEnum { - LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact", - LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", -} - -export type SidebarTypename = keyof typeof SidebarTypenameEnum export enum JoinLoyaltyContactTypenameEnum { LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact = "LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact", } -export type JoinLoyaltyContactEnum = Typename< +export type JoinLoyaltyContactContact = Typename< Contact, JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact > @@ -27,11 +19,17 @@ export type JoinLoyaltyContact = { join_loyalty_contact: { title?: string preamble?: string - contact: JoinLoyaltyContactEnum[] - login_button_text: string + contact: JoinLoyaltyContactContact[] } } +export enum SidebarTypenameEnum { + LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact", + LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", +} + +export type SidebarTypename = keyof typeof SidebarTypenameEnum + export type Sidebar = | Typename | Typename< @@ -39,6 +37,26 @@ export type Sidebar = SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact > +export enum LoyaltyComponentEnum { + loyalty_levels = "loyalty_levels", + how_it_works = "how_it_works", + overview_table = "overview_table", +} + +export type LoyaltyComponent = keyof typeof LoyaltyComponentEnum + +export type DynamicContentBlock = { + dynamic_content: { + title?: string + subtitle?: string + component: LoyaltyComponent + link: { + text?: string + pageConnection: Edges + } + } +} + export enum LoyaltyBlocksTypenameEnum { LoyaltyPageBlocksDynamicContent = "LoyaltyPageBlocksDynamicContent", LoyaltyPageBlocksCardGrid = "LoyaltyPageBlocksCardGrid", diff --git a/types/requests/utils/asset.ts b/types/requests/utils/asset.ts index b1d35d8e6..d9dae927e 100644 --- a/types/requests/utils/asset.ts +++ b/types/requests/utils/asset.ts @@ -1,5 +1,5 @@ -import type { EmbedEnum } from "./embeds" import type { Image } from "@/types/image" +import type { EmbedEnum } from "./embeds" import type { Typename } from "./typename" export type SysAsset = Typename diff --git a/utils/contactConfig.ts b/utils/contactConfig.ts index e94eef75f..83a24abf8 100644 --- a/utils/contactConfig.ts +++ b/utils/contactConfig.ts @@ -1,10 +1,11 @@ import { ContactConfig, + ContactConfigField, ContactFieldGroups, } from "@/types/requests/contactConfig" export function getValueFromContactConfig( - keyStrings: string, + keyStrings: ContactConfigField, data: ContactConfig ): string | undefined { const [groupName, key] = keyStrings.split(".") as [ From 49b7aa89f8735f06560718eec561d4efc65903fe Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 29 Apr 2024 09:53:54 +0200 Subject: [PATCH 07/11] refactor: infer types from zod validation --- components/Loyalty/Blocks/CardGrid/index.tsx | 9 +- .../Loyalty/Blocks/DynamicContent/index.tsx | 21 +- components/Loyalty/Blocks/index.tsx | 12 +- .../JoinLoyalty/Contact/ContactRow/index.tsx | 8 +- .../Sidebar/JoinLoyalty/Contact/index.tsx | 16 +- .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 8 +- components/Loyalty/Sidebar/index.tsx | 9 +- lib/graphql/Query/LoyaltyPage.graphql | 16 +- .../contentstack/loyaltyPage/output.ts | 208 ++++++++++++++++++ .../routers/contentstack/loyaltyPage/query.ts | 143 ++++++++++-- types/components/jsontohtml.ts | 7 +- types/components/loyalty/blocks.ts | 44 ++-- types/components/loyalty/sidebar.ts | 22 +- types/image.ts | 2 +- types/requests/contactConfig.ts | 23 +- types/requests/loyaltyPage.ts | 73 +----- types/rte/node.ts | 3 +- utils/contactConfig.ts | 11 +- 18 files changed, 418 insertions(+), 217 deletions(-) create mode 100644 server/routers/contentstack/loyaltyPage/output.ts diff --git a/components/Loyalty/Blocks/CardGrid/index.tsx b/components/Loyalty/Blocks/CardGrid/index.tsx index b6568ce2b..f288979cd 100644 --- a/components/Loyalty/Blocks/CardGrid/index.tsx +++ b/components/Loyalty/Blocks/CardGrid/index.tsx @@ -35,16 +35,9 @@ export default function CardGrid({ card_grid }: CardGridProps) { } function CardWrapper({ card }: CardProps) { - const link = card.referenceConnection.edges.length - ? { - href: card.referenceConnection.edges[0].node.url, - title: _("Read more"), - } - : undefined - return (
- +
) } diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx index 0bb4fe45b..9f324d4fa 100644 --- a/components/Loyalty/Blocks/DynamicContent/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -7,13 +7,13 @@ import OverviewTable from "./OverviewTable" import styles from "./dynamicContent.module.css" -import { DynamicContentProps } from "@/types/components/loyalty/blocks" -import { - LoyaltyComponent, - LoyaltyComponentEnum, -} from "@/types/requests/loyaltyPage" +import type { + DynamicComponentProps, + DynamicContentProps, +} from "@/types/components/loyalty/blocks" +import { LoyaltyComponentEnum } from "@/types/requests/loyaltyPage" -function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) { +function DynamicComponentBlock({ component }: DynamicComponentProps) { switch (component) { case LoyaltyComponentEnum.how_it_works: return @@ -30,9 +30,6 @@ function DynamicComponentBlock({ component }: { component: LoyaltyComponent }) { export default function DynamicContent({ dynamicContent, }: DynamicContentProps) { - const link = dynamicContent.link.pageConnection.edges.length - ? dynamicContent.link.pageConnection.edges[0].node.url - : null return (
@@ -48,11 +45,11 @@ export default function DynamicContent({ {dynamicContent.title} )} - {link && ( - + {dynamicContent.link ? ( + {dynamicContent.link.text} - )} + ) : null} {dynamicContent.subtitle && ( { switch (block.__typename) { case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: @@ -16,8 +14,8 @@ export function Blocks({ blocks }: { blocks: BlocksType[] }) { case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: return ( <JsonToHtml - nodes={block.content.json.children} - embeds={block.content.embedded_itemsConnection.edges} + nodes={block.content.content.json.children} + embeds={block.content.content.embedded_itemsConnection.edges} /> ) case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx index d7a95aec9..fe5fa82d2 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx @@ -1,9 +1,10 @@ +import { Lang } from "@/constants/languages" import { serverClient } from "@/lib/trpc/server" + import { getValueFromContactConfig } from "@/utils/contactConfig" import styles from "./contactRow.module.css" -import { Lang } from "@/constants/languages" import type { ContactFields } from "@/types/requests/contactConfig" export default async function ContactRow({ @@ -16,6 +17,11 @@ export default async function ContactRow({ }) const val = getValueFromContactConfig(contact.contact_field, data) + + if (!val) { + return null + } + return ( <div className={styles.container}> <h4 className={styles.title}>{contact.display_text}</h4> diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx index 639219dd4..5eeb35a66 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx @@ -1,20 +1,28 @@ +import { _ } from "@/lib/translation" + import Title from "@/components/Title" + import ContactRow from "./ContactRow" import styles from "./contact.module.css" -import { JoinLoyaltyContactTypenameEnum } from "@/types/requests/loyaltyPage" import type { ContactProps } from "@/types/components/loyalty/sidebar" +import { JoinLoyaltyContactTypenameEnum } from "@/types/requests/loyaltyPage" export default async function Contact({ contactBlock }: ContactProps) { return ( <div className={styles.contactContainer}> - <Title level="h5">Contact us + {_("Contact us")}
- {contactBlock.map(({ contact, __typename }) => { + {contactBlock.map(({ contact, __typename }, i) => { switch (__typename) { case JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact: - return + return ( + + ) default: return null } diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx index 15f55c4df..59ee01a0c 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx @@ -9,13 +9,9 @@ import Contact from "./Contact" import styles from "./joinLoyalty.module.css" -import type { JoinLoyaltyContact } from "@/types/requests/loyaltyPage" +import type { JoinLoyaltyContactProps } from "@/types/components/loyalty/sidebar" -export default function JoinLoyaltyContact({ - block, -}: { - block: JoinLoyaltyContact["join_loyalty_contact"] -}) { +export default function JoinLoyaltyContact({ block }: JoinLoyaltyContactProps) { return (
diff --git a/components/Loyalty/Sidebar/index.tsx b/components/Loyalty/Sidebar/index.tsx index f6128fcbe..85afa27c4 100644 --- a/components/Loyalty/Sidebar/index.tsx +++ b/components/Loyalty/Sidebar/index.tsx @@ -2,15 +2,16 @@ import JsonToHtml from "@/components/JsonToHtml" import JoinLoyaltyContact from "./JoinLoyalty" -import { Sidebar, SidebarTypenameEnum } from "@/types/requests/loyaltyPage" +import { SidebarProps } from "@/types/components/loyalty/sidebar" +import { SidebarTypenameEnum } from "@/types/requests/loyaltyPage" -export default function SidebarLoyalty({ block }: { block: Sidebar }) { +export default function SidebarLoyalty({ block }: SidebarProps) { switch (block.__typename) { case SidebarTypenameEnum.LoyaltyPageSidebarContent: return ( ) case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact: diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 1efefc2c9..743df0539 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -22,6 +22,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { ...LoyaltyPageLink } } + totalCount } } } @@ -41,9 +42,11 @@ query GetLoyaltyPage($locale: String!, $url: String!) { ...AccountPageLink } } + totalCount } title subtitle + open_in_new_tab } } } @@ -55,16 +58,11 @@ query GetLoyaltyPage($locale: String!, $url: String!) { embedded_itemsConnection { edges { node { - ... on SysAsset { - title - url - dimension { - width - height - } - } + __typename + ...Image } } + totalCount } } } @@ -86,7 +84,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } } - login_button_text } } ... on LoyaltyPageSidebarContent { @@ -101,6 +98,7 @@ query GetLoyaltyPage($locale: String!, $url: String!) { ...Image } } + totalCount } } } diff --git a/server/routers/contentstack/loyaltyPage/output.ts b/server/routers/contentstack/loyaltyPage/output.ts new file mode 100644 index 000000000..73a74c072 --- /dev/null +++ b/server/routers/contentstack/loyaltyPage/output.ts @@ -0,0 +1,208 @@ +import { z } from "zod" + +import { Embeds } from "@/types/requests/embeds" +import { + JoinLoyaltyContactTypenameEnum, + LoyaltyBlocksTypenameEnum, + LoyaltyComponentEnum, + SidebarTypenameEnum, +} from "@/types/requests/loyaltyPage" +import { Edges } from "@/types/requests/utils/edges" +import { RTEDocument } from "@/types/rte/node" + +const loyaltyPageBlockCardGrid = z.object({ + __typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid), + card_grid: z.object({ + title: z.string().optional(), + subtitle: z.string().optional(), + cards: z.array( + z.object({ + title: z.string().optional(), + subtitle: z.string().optional(), + referenceConnection: z.object({ + edges: z.array( + z.object({ + node: z.object({ + system: z.object({ + uid: z.string(), + }), + url: z.string(), + title: z.string(), + __typename: z.string(), + }), + }) + ), + totalCount: z.number(), + }), + open_in_new_tab: z.boolean(), + }) + ), + }), +}) +const loyaltyPageDynamicContent = z.object({ + __typename: z.literal( + LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent + ), + dynamic_content: z.object({ + title: z.string().optional(), + subtitle: z.string().optional(), + component: z.nativeEnum(LoyaltyComponentEnum), + link: z.object({ + text: z.string().optional(), + pageConnection: z.object({ + edges: z.array( + z.object({ + node: z.object({ + system: z.object({ + uid: z.string(), + }), + url: z.string(), + title: z.string(), + }), + }) + ), + totalCount: z.number(), + }), + }), + }), +}) + +const loyaltyPageBlockTextContent = z.object({ + __typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent), + content: z.object({ + content: z.object({ + embedded_itemsConnection: z.object({ + edges: z.array(z.any()), + totalCount: z.number(), + }), + json: z.any(), + }), + }), +}) + +const loyaltyPageBlockItem = z.discriminatedUnion("__typename", [ + loyaltyPageBlockCardGrid, + loyaltyPageDynamicContent, + loyaltyPageBlockTextContent, +]) + +const loyaltyPageSidebarTextContent = z.object({ + __typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarContent), + content: z.object({ + content: z.object({ + embedded_itemsConnection: z.object({ + edges: z.array(z.any()), + totalCount: z.number(), + }), + json: z.any(), + }), + }), +}) + +const loyaltyPageJoinLoyaltyContact = z.object({ + __typename: z.literal( + SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact + ), + join_loyalty_contact: z.object({ + title: z.string().optional(), + preamble: z.string().optional(), + contact: z.array( + z.object({ + __typename: z.literal( + JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact + ), + contact: z.object({ + display_text: z.string().optional(), + + contact_field: z.string(), + }), + }) + ), + }), +}) + +const loyaltyPageSidebarItem = z.discriminatedUnion("__typename", [ + loyaltyPageSidebarTextContent, + loyaltyPageJoinLoyaltyContact, +]) + +export const validateLoyaltyPageSchema = z.object({ + all_loyalty_page: z.object({ + items: z.array( + z.object({ + title: z.string(), + blocks: z.array(loyaltyPageBlockItem), + sidebar: z.array(loyaltyPageSidebarItem), + }) + ), + }), +}) + +// Block types +type CardGridRaw = z.infer + +export type CardGridCard = Omit< + CardGridRaw["card_grid"]["cards"][number], + "referenceConnection" +> & { + link: + | { + href: string + title: string + } + | undefined +} + +export type CardGrid = Omit & { + card_grid: Omit & { + cards: CardGridCard[] + } +} + +type DynamicContentRaw = z.infer + +export type DynamicContent = Omit & { + dynamic_content: Omit & { + link: + | { + href: string + title: string + text?: string + } + | undefined + } +} +type BlockContentRaw = z.infer + +export interface RteBlockContent extends BlockContentRaw { + content: { + content: { + json: RTEDocument + embedded_itemsConnection: Edges + } + } +} +export type Block = CardGrid | RteBlockContent | DynamicContent + +// Sidebar block types +type SidebarContentRaw = z.infer + +export type RteSidebarContent = Omit & { + content: { + content: { + json: RTEDocument + embedded_itemsConnection: Edges + } + } +} +export type JoinLoyaltyContact = z.infer +export type Sidebar = JoinLoyaltyContact | RteSidebarContent + +type LoyaltyPageDataRaw = z.infer + +type LoyaltyPageRaw = LoyaltyPageDataRaw["all_loyalty_page"]["items"][0] + +export type LoyaltyPage = Omit & { + blocks: Block[] + sidebar: Sidebar[] +} diff --git a/server/routers/contentstack/loyaltyPage/query.ts b/server/routers/contentstack/loyaltyPage/query.ts index 3d22cf6a3..0169f7b41 100644 --- a/server/routers/contentstack/loyaltyPage/query.ts +++ b/server/routers/contentstack/loyaltyPage/query.ts @@ -1,33 +1,132 @@ -import { z } from "zod" - +import GetLoyaltyPage from "@/lib/graphql/Query/LoyaltyPage.graphql" +import { request } from "@/lib/graphql/request" import { badRequestError } from "@/server/errors/trpc" import { publicProcedure, router } from "@/server/trpc" -import { request } from "@/lib/graphql/request" -import { Lang } from "@/constants/languages" -import GetLoyaltyPage from "@/lib/graphql/Query/LoyaltyPage.graphql" +import { getLoyaltyPageInput } from "./input" +import { type LoyaltyPage, validateLoyaltyPageSchema } from "./output" -import type { GetLoyaltyPageData } from "@/types/requests/loyaltyPage" +import { Embeds } from "@/types/requests/embeds" +import { + LoyaltyBlocksTypenameEnum, + SidebarTypenameEnum, +} from "@/types/requests/loyaltyPage" +import { Edges } from "@/types/requests/utils/edges" +import { RTEDocument } from "@/types/rte/node" export const loyaltyPageQueryRouter = router({ - get: publicProcedure - .input(z.object({ uri: z.string(), lang: z.nativeEnum(Lang) })) - .query(async ({ input }) => { - const loyaltyPage = await request( - GetLoyaltyPage, - { - locale: input.lang, - url: input.uri, - }, - { - tags: [`${input.uri}-${input.lang}`], - } - ) + get: publicProcedure.input(getLoyaltyPageInput).query(async ({ input }) => { + try { + const loyaltyPageRes = await request(GetLoyaltyPage, { + locale: input.locale, + url: input.href, + }) - if (loyaltyPage.data && loyaltyPage.data.all_loyalty_page.items.length) { - return loyaltyPage.data.all_loyalty_page.items[0] + if (!loyaltyPageRes.data) { + throw badRequestError() } + const validatedLoyaltyPage = validateLoyaltyPageSchema.safeParse( + loyaltyPageRes.data + ) + + if (!validatedLoyaltyPage.success) { + throw badRequestError() + } + + const sidebar = + validatedLoyaltyPage.data.all_loyalty_page.items[0].sidebar.map( + (block) => { + if ( + block.__typename == SidebarTypenameEnum.LoyaltyPageSidebarContent + ) { + return { + ...block, + content: { + content: { + json: block.content.content.json as RTEDocument, + embedded_itemsConnection: block.content.content + .embedded_itemsConnection as Edges, + }, + }, + } + } else { + return block + } + } + ) + + const blocks = + validatedLoyaltyPage.data.all_loyalty_page.items[0].blocks.map( + (block) => { + switch (block.__typename) { + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: + return { + ...block, + card_grid: { + ...block.card_grid, + cards: block.card_grid.cards.map((card) => { + return { + ...card, + link: + card.referenceConnection.totalCount > 0 + ? { + href: card.referenceConnection.edges[0].node + .url, + title: + card.referenceConnection.edges[0].node.title, + } + : undefined, + } + }), + }, + } + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: + return { + ...block, + dynamic_content: { + ...block.dynamic_content, + link: + block.dynamic_content.link.pageConnection.totalCount > 0 + ? { + text: block.dynamic_content.link.text, + href: block.dynamic_content.link.pageConnection + .edges[0].node.url, + title: + block.dynamic_content.link.pageConnection.edges[0] + .node.title, + } + : undefined, + }, + } + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: + return { + ...block, + content: { + content: { + json: block.content.content.json as RTEDocument, + embedded_itemsConnection: block.content.content + .embedded_itemsConnection as Edges, + }, + }, + } + default: + return block + } + } + ) + + const loyaltyPage = { + ...validatedLoyaltyPage.data.all_loyalty_page.items[0], + blocks, + sidebar, + } as LoyaltyPage + + return loyaltyPage + } catch (error) { + console.info(`Get Loyalty Page Error`) + console.error(error) throw badRequestError() - }), + } + }), }) diff --git a/types/components/jsontohtml.ts b/types/components/jsontohtml.ts index 4f319e6ed..c12ef513f 100644 --- a/types/components/jsontohtml.ts +++ b/types/components/jsontohtml.ts @@ -1,8 +1,7 @@ -import type { RTENode } from "../rte/node" - -import type { Node } from "@/types/requests/utils/edges" -import type { RenderOptions } from "../rte/option" import type { Embeds } from "@/types/requests/embeds" +import type { Node } from "@/types/requests/utils/edges" +import type { RTENode } from "../rte/node" +import type { RenderOptions } from "../rte/option" export type JsonToHtmlProps = { embeds: Node[] diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts index 4efdc7f43..b3fc03c85 100644 --- a/types/components/loyalty/blocks.ts +++ b/types/components/loyalty/blocks.ts @@ -1,38 +1,28 @@ -import { Embeds } from "@/types/requests/embeds" -import { DynamicContentBlock } from "@/types/requests/loyaltyPage" -import { PageLink } from "@/types/requests/myPages/navigation" -import { Edges } from "@/types/requests/utils/edges" -import { RTEDocument } from "@/types/rte/node" +import { + Block, + CardGrid, + CardGridCard, + DynamicContent, + RteBlockContent, +} from "@/server/routers/contentstack/loyaltyPage/output" + +export type BlocksProps = { + blocks: Block[] +} export type DynamicContentProps = { - dynamicContent: DynamicContentBlock["dynamic_content"] + dynamicContent: DynamicContent["dynamic_content"] } -type Card = { - referenceConnection: Edges - title?: string - subtitle?: string - open_in_new_tab: boolean +export type DynamicComponentProps = { + component: DynamicContent["dynamic_content"]["component"] } -export type CardProps = { card: Card } +export type CardProps = { card: CardGridCard } -export type CardGrid = { - card_grid: { - title?: string - subtitle?: string - cards: Card[] - } -} +export type CardGridProps = Pick -export type CardGridProps = CardGrid - -export type Content = { - content: { - embedded_itemsConnection: Edges - json: RTEDocument - } -} +export type Content = { content: RteBlockContent["content"]["content"] } export type LevelCardProps = { level: { diff --git a/types/components/loyalty/sidebar.ts b/types/components/loyalty/sidebar.ts index 2dcb38cbe..e1bdb1098 100644 --- a/types/components/loyalty/sidebar.ts +++ b/types/components/loyalty/sidebar.ts @@ -1,20 +1,16 @@ -import { ContactFields } from "@/types/requests/contactConfig" -import { Embeds } from "@/types/requests/embeds" -import { JoinLoyaltyContactContact } from "@/types/requests/loyaltyPage" -import { Edges } from "@/types/requests/utils/edges" -import { RTEDocument } from "@/types/rte/node" +import { + JoinLoyaltyContact, + Sidebar, +} from "@/server/routers/contentstack/loyaltyPage/output" -export type SidebarContent = { - content: { - embedded_itemsConnection: Edges - json: RTEDocument - } +export type SidebarProps = { + block: Sidebar } -export type Contact = { - contact: ContactFields +export type JoinLoyaltyContactProps = { + block: JoinLoyaltyContact["join_loyalty_contact"] } export type ContactProps = { - contactBlock: JoinLoyaltyContactContact[] + contactBlock: JoinLoyaltyContact["join_loyalty_contact"]["contact"] } diff --git a/types/image.ts b/types/image.ts index 8cbed40b1..5c442c4df 100644 --- a/types/image.ts +++ b/types/image.ts @@ -4,7 +4,7 @@ export type Image = { height: number width: number } - metadata: JSON + metadata: JSON | null system: { uid: string } diff --git a/types/requests/contactConfig.ts b/types/requests/contactConfig.ts index 5995d432a..d68090d16 100644 --- a/types/requests/contactConfig.ts +++ b/types/requests/contactConfig.ts @@ -47,28 +47,7 @@ export type GetContactConfigData = { all_contact_config: AllRequestResponse } -// Utility types that extract the possible strings of ContactConfigField, -// Which is all the dot notated values of ContactConfig (for example: 'email.name') -// From: https://stackoverflow.com/questions/47057649/typescript-string-dot-notation-of-nested-object#47058976 -type PathsToStringProps = T extends string - ? [] - : { - [K in Extract]: [K, ...PathsToStringProps] - }[Extract] - -type Join = T extends [] - ? never - : T extends [infer F] - ? F - : T extends [infer F, ...infer R] - ? F extends string - ? `${F}${D}${Join, D>}` - : never - : string - -export type ContactConfigField = Join, "."> - export type ContactFields = { display_text?: string - contact_field: ContactConfigField + contact_field: string } diff --git a/types/requests/loyaltyPage.ts b/types/requests/loyaltyPage.ts index b97669164..2a9016a2c 100644 --- a/types/requests/loyaltyPage.ts +++ b/types/requests/loyaltyPage.ts @@ -1,9 +1,4 @@ -import { CardGrid, Content } from "../components/loyalty/blocks" -import { Contact, SidebarContent } from "../components/loyalty/sidebar" -import { PageLink } from "./myPages/navigation" -import { Edges } from "./utils/edges" - -import type { AllRequestResponse } from "./utils/all" +import type { JoinLoyaltyContact } from "@/server/routers/contentstack/loyaltyPage/output" import type { Typename } from "./utils/typename" export enum JoinLoyaltyContactTypenameEnum { @@ -11,18 +6,10 @@ export enum JoinLoyaltyContactTypenameEnum { } export type JoinLoyaltyContactContact = Typename< - Contact, + JoinLoyaltyContact["join_loyalty_contact"], JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact > -export type JoinLoyaltyContact = { - join_loyalty_contact: { - title?: string - preamble?: string - contact: JoinLoyaltyContactContact[] - } -} - export enum SidebarTypenameEnum { LoyaltyPageSidebarJoinLoyaltyContact = "LoyaltyPageSidebarJoinLoyaltyContact", LoyaltyPageSidebarContent = "LoyaltyPageSidebarContent", @@ -30,13 +17,6 @@ export enum SidebarTypenameEnum { export type SidebarTypename = keyof typeof SidebarTypenameEnum -export type Sidebar = - | Typename - | Typename< - JoinLoyaltyContact, - SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact - > - export enum LoyaltyComponentEnum { loyalty_levels = "loyalty_levels", how_it_works = "how_it_works", @@ -45,57 +25,8 @@ export enum LoyaltyComponentEnum { export type LoyaltyComponent = keyof typeof LoyaltyComponentEnum -export type DynamicContentBlock = { - dynamic_content: { - title?: string - subtitle?: string - component: LoyaltyComponent - link: { - text?: string - pageConnection: Edges - } - } -} - export enum LoyaltyBlocksTypenameEnum { LoyaltyPageBlocksDynamicContent = "LoyaltyPageBlocksDynamicContent", LoyaltyPageBlocksCardGrid = "LoyaltyPageBlocksCardGrid", LoyaltyPageBlocksContent = "LoyaltyPageBlocksContent", } - -export type Blocks = - | Typename - | Typename< - DynamicContentBlock, - LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent - > - | Typename - -export type Breadcrumb = { - href: string - title: string -} - -export type Breadcrumbs = { - parents: Breadcrumb[] - title: string -} - -export type LoyaltyPage = { - sidebar: Sidebar[] - blocks: Blocks[] - web: { - breadcrumbs: Breadcrumbs - } - system: { - created_at: string - uid: string - updated_at: string - } - title: string - url: string -} - -export type GetLoyaltyPageData = { - all_loyalty_page: AllRequestResponse -} diff --git a/types/rte/node.ts b/types/rte/node.ts index 25e9080bd..c762f33bb 100644 --- a/types/rte/node.ts +++ b/types/rte/node.ts @@ -1,11 +1,12 @@ import { RTETypeEnum } from "./enums" + +import type { EmbedByUid } from "../components/jsontohtml" import type { Attributes, RTEAnchorAttrs, RTEAssetAttrs, RTELinkAttrs, } from "./attrs" -import type { EmbedByUid } from "../components/jsontohtml" import type { RenderOptions } from "./option" export interface RTEDefaultNode { diff --git a/utils/contactConfig.ts b/utils/contactConfig.ts index 83a24abf8..3eed6da76 100644 --- a/utils/contactConfig.ts +++ b/utils/contactConfig.ts @@ -1,18 +1,19 @@ import { ContactConfig, - ContactConfigField, ContactFieldGroups, } from "@/types/requests/contactConfig" export function getValueFromContactConfig( - keyStrings: ContactConfigField, + keyString: string, data: ContactConfig ): string | undefined { - const [groupName, key] = keyStrings.split(".") as [ + const [groupName, key] = keyString.split(".") as [ ContactFieldGroups, keyof ContactConfig[ContactFieldGroups], ] - const fieldGroup = data[groupName] + if (data[groupName]) { + const fieldGroup = data[groupName] - return fieldGroup[key] + return fieldGroup[key] + } } From 91dfd381411298c9f28572fe8692f6594d4ec516 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 29 Apr 2024 10:00:49 +0200 Subject: [PATCH 08/11] fix: add padding to rte in sidebar --- components/Loyalty/Sidebar/index.tsx | 12 ++++++++---- components/Loyalty/Sidebar/sidebar.module.css | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 components/Loyalty/Sidebar/sidebar.module.css diff --git a/components/Loyalty/Sidebar/index.tsx b/components/Loyalty/Sidebar/index.tsx index 85afa27c4..12cdc81f6 100644 --- a/components/Loyalty/Sidebar/index.tsx +++ b/components/Loyalty/Sidebar/index.tsx @@ -2,6 +2,8 @@ import JsonToHtml from "@/components/JsonToHtml" import JoinLoyaltyContact from "./JoinLoyalty" +import styles from "./sidebar.module.css" + import { SidebarProps } from "@/types/components/loyalty/sidebar" import { SidebarTypenameEnum } from "@/types/requests/loyaltyPage" @@ -9,10 +11,12 @@ export default function SidebarLoyalty({ block }: SidebarProps) { switch (block.__typename) { case SidebarTypenameEnum.LoyaltyPageSidebarContent: return ( - +
+ +
) case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact: return diff --git a/components/Loyalty/Sidebar/sidebar.module.css b/components/Loyalty/Sidebar/sidebar.module.css new file mode 100644 index 000000000..71e960078 --- /dev/null +++ b/components/Loyalty/Sidebar/sidebar.module.css @@ -0,0 +1,9 @@ +.content { + padding: 0 1.6rem; +} + +@media screen and (min-width: 950px) { + .content { + padding: 0; + } +} From af96964fb260378f7f84acf86a815b1ca2ab21d2 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 29 Apr 2024 10:07:15 +0200 Subject: [PATCH 09/11] fix: linting --- components/MyPages/Blocks/Benefits/NextLevel/index.tsx | 2 +- components/MyPages/Sidebar/index.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/components/MyPages/Blocks/Benefits/NextLevel/index.tsx b/components/MyPages/Blocks/Benefits/NextLevel/index.tsx index 70bf4d4c8..8bb4fc277 100644 --- a/components/MyPages/Blocks/Benefits/NextLevel/index.tsx +++ b/components/MyPages/Blocks/Benefits/NextLevel/index.tsx @@ -3,8 +3,8 @@ import { Lock } from "react-feather" import { serverClient } from "@/lib/trpc/server" -import Title from "@/components/Title" import Button from "@/components/TempDesignSystem/Button" +import Title from "@/components/Title" import styles from "./next.module.css" diff --git a/components/MyPages/Sidebar/index.tsx b/components/MyPages/Sidebar/index.tsx index 9c43e0a6e..311510545 100644 --- a/components/MyPages/Sidebar/index.tsx +++ b/components/MyPages/Sidebar/index.tsx @@ -4,9 +4,8 @@ import { LogOut } from "react-feather" import { GetNavigationMyPages } from "@/lib/graphql/Query/NavigationMyPages.graphql" import { request } from "@/lib/graphql/request" -import Title from "@/components/Title" import Link from "@/components/TempDesignSystem/Link" - +import Title from "@/components/Title" import { mapMenuItems } from "./helpers" From 9f0b044daa8e601b7608c2d07aa115ce606c64ac Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 29 Apr 2024 10:49:19 +0200 Subject: [PATCH 10/11] fix: break out content type uid fetch to utils function --- .../(live)/(public)/loyalty-page/layout.tsx | 6 +- lib/graphql/Query/ContentTypeUid.graphql | 12 +--- middlewares/cmsContent.ts | 58 +++---------------- types/requests/contentTypeUid.ts | 11 ++++ utils/contentType.ts | 45 ++++++++++++++ 5 files changed, 68 insertions(+), 64 deletions(-) create mode 100644 types/requests/contentTypeUid.ts create mode 100644 utils/contentType.ts diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.tsx b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx index 5812eca64..aa011671a 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/layout.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.tsx @@ -2,11 +2,9 @@ import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts" import styles from "./layout.module.css" -import type { MyPagesLayoutProps } from "@/types/components/myPages/layout" - -export default async function LoyaltyPagesLayout({ +export default function LoyaltyPagesLayout({ children, -}: React.PropsWithChildren) { +}: React.PropsWithChildren) { return (
{ const { nextUrl } = request const lang = findLang(nextUrl.pathname) @@ -42,28 +15,7 @@ export const middleware: NextMiddleware = async (request) => { const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "") const searchParams = new URLSearchParams(request.nextUrl.searchParams) - const print = (await import("graphql/language/printer")).print - const result = await fetch(env.CMS_URL, { - method: "POST", - headers: { - access_token: env.CMS_ACCESS_TOKEN, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - query: print(GetContentTypeUid as DocumentNode), - variables: { - locale: lang, - url: pathNameWithoutLang, - }, - }), - }) - - const pageTypeData = await result.json() - const pageType = pageTypeData.data as GetContentTypeUidType - - const contentType = Object.values(pageType) - .map((val) => val.items[0]) - .find((item) => item?.__typename)?.__typename + const contentType = await getContentTypeByPathName(pathNameWithoutLang, lang) if (request.nextUrl.pathname.includes("preview")) { searchParams.set("uri", pathNameWithoutLang.replace("/preview", "")) @@ -85,6 +37,10 @@ export const middleware: NextMiddleware = async (request) => { return NextResponse.rewrite( new URL(`/${lang}/loyalty-page?${searchParams.toString()}`, nextUrl) ) + // case PageTypeEnum.ContentPage: + // return NextResponse.rewrite( + // new URL(`/${lang}/content-page?${searchParams.toString()}`, nextUrl) + // ) default: return NextResponse.next() } diff --git a/types/requests/contentTypeUid.ts b/types/requests/contentTypeUid.ts new file mode 100644 index 000000000..e8f358d07 --- /dev/null +++ b/types/requests/contentTypeUid.ts @@ -0,0 +1,11 @@ +export type GetContentTypeUidType = { + all_content_page: { + total: number + } + all_loyalty_page: { + total: number + } + all_current_blocks_page: { + total: number + } +} diff --git a/utils/contentType.ts b/utils/contentType.ts new file mode 100644 index 000000000..2d3668c90 --- /dev/null +++ b/utils/contentType.ts @@ -0,0 +1,45 @@ +import { DocumentNode } from "graphql" + +import { Lang } from "@/constants/languages" +import { env } from "@/env/server" +import GetContentTypeUid from "@/lib/graphql/Query/ContentTypeUid.graphql" + +import type { GetContentTypeUidType } from "@/types/requests/contentTypeUid" + +export enum PageTypeEnum { + CurrentBlocksPage = "CurrentBlocksPage", + LoyaltyPage = "LoyaltyPage", + ContentPage = "ContentPage", +} + +export async function getContentTypeByPathName( + pathNameWithoutLang: string, + lang = Lang.en +) { + const print = (await import("graphql/language/printer")).print + const result = await fetch(env.CMS_URL, { + method: "POST", + headers: { + access_token: env.CMS_ACCESS_TOKEN, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: print(GetContentTypeUid as DocumentNode), + variables: { + locale: lang, + url: pathNameWithoutLang, + }, + }), + }) + + const pageTypeData = await result.json() + const pageType = pageTypeData.data as GetContentTypeUidType + + if (pageType.all_content_page.total) { + return PageTypeEnum.ContentPage + } else if (pageType.all_loyalty_page.total) { + return PageTypeEnum.LoyaltyPage + } else if (pageType.all_current_blocks_page.total) { + return PageTypeEnum.CurrentBlocksPage + } +} From d9f1470eb7ae0d758595368f976657ca23a2fefb Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 29 Apr 2024 14:00:24 +0200 Subject: [PATCH 11/11] refactor: zod validation and pr comments --- .../(protected)/my-pages/layout.module.css | 3 +- .../(public)/loyalty-page/layout.module.css | 2 - .../(public)/loyalty-page/page.module.css | 1 + .../(live)/(public)/loyalty-page/page.tsx | 9 +-- app/globals.css | 1 + components/Loyalty/Blocks/CardGrid/index.tsx | 18 +++--- .../DynamicContent/HowItWorks/index.tsx | 4 +- .../DynamicContent/LoyaltyLevels/index.tsx | 4 +- .../DynamicContent/dynamicContent.module.css | 3 +- .../Loyalty/Blocks/DynamicContent/index.tsx | 36 ++++++----- components/Loyalty/Blocks/index.tsx | 10 ++-- .../JoinLoyalty/Contact/ContactRow/index.tsx | 8 +-- .../Sidebar/JoinLoyalty/Contact/index.tsx | 2 +- .../Loyalty/Sidebar/JoinLoyalty/index.tsx | 2 +- .../JoinLoyalty/joinLoyalty.module.css | 2 +- components/Loyalty/Sidebar/index.tsx | 38 +++++++----- components/Loyalty/Sidebar/sidebar.module.css | 8 +-- components/TempDesignSystem/Card/index.tsx | 4 +- lib/graphql/Query/LoyaltyPage.graphql | 7 +-- .../contentstack/contactConfig/input.ts | 5 ++ .../contentstack/contactConfig/output.ts | 60 +++++++++++++++++++ .../contentstack/contactConfig/query.ts | 46 +++++++------- server/routers/contentstack/index.ts | 2 +- .../routers/contentstack/loyaltyPage/query.ts | 38 ++++++------ server/trpc.ts | 7 ++- types/components/loyalty/blocks.ts | 3 - types/components/loyalty/sidebar.ts | 7 ++- types/requests/contactConfig.ts | 53 ---------------- types/requests/contentTypeUid.ts | 24 ++++---- utils/contactConfig.ts | 3 +- utils/contentType.ts | 19 ++++-- 31 files changed, 222 insertions(+), 207 deletions(-) create mode 100644 server/routers/contentstack/contactConfig/input.ts create mode 100644 server/routers/contentstack/contactConfig/output.ts delete mode 100644 types/requests/contactConfig.ts diff --git a/app/[lang]/(live)/(protected)/my-pages/layout.module.css b/app/[lang]/(live)/(protected)/my-pages/layout.module.css index bc197d5f7..4782b4cb4 100644 --- a/app/[lang]/(live)/(protected)/my-pages/layout.module.css +++ b/app/[lang]/(live)/(protected)/my-pages/layout.module.css @@ -1,5 +1,4 @@ .layout { - --max-width: 101.4rem; --header-height: 4.5rem; display: grid; @@ -25,4 +24,4 @@ padding-right: 2.4rem; padding-top: 5.8rem; } -} \ No newline at end of file +} diff --git a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css index e17395732..55219a7f1 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/layout.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/layout.module.css @@ -1,6 +1,4 @@ .layout { - --max-width: 101.4rem; - display: grid; font-family: var(--ff-fira-sans); background-color: var(--Brand-Coffee-Subtle); diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.module.css b/app/[lang]/(live)/(public)/loyalty-page/page.module.css index 3311af02b..d6b3c2533 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.module.css +++ b/app/[lang]/(live)/(public)/loyalty-page/page.module.css @@ -26,5 +26,6 @@ gap: 6.4rem; padding-left: 0; padding-right: 0; + grid-column: 2 / -1; } } diff --git a/app/[lang]/(live)/(public)/loyalty-page/page.tsx b/app/[lang]/(live)/(public)/loyalty-page/page.tsx index 152f7ed34..d35d2947c 100644 --- a/app/[lang]/(live)/(public)/loyalty-page/page.tsx +++ b/app/[lang]/(live)/(public)/loyalty-page/page.tsx @@ -26,13 +26,8 @@ export default async function LoyaltyPage({ return (
- + {loyaltyPage.sidebar ? : null} + diff --git a/app/globals.css b/app/globals.css index e6547e169..f6581020a 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,5 +1,6 @@ :root { font-size: 62.5%; + --max-width: 113.5rem; } * { diff --git a/components/Loyalty/Blocks/CardGrid/index.tsx b/components/Loyalty/Blocks/CardGrid/index.tsx index f288979cd..cd457f550 100644 --- a/components/Loyalty/Blocks/CardGrid/index.tsx +++ b/components/Loyalty/Blocks/CardGrid/index.tsx @@ -5,7 +5,7 @@ import Title from "@/components/Title" import styles from "./cardGrid.module.css" -import { CardGridProps, CardProps } from "@/types/components/loyalty/blocks" +import { CardGridProps } from "@/types/components/loyalty/blocks" export default function CardGrid({ card_grid }: CardGridProps) { return ( @@ -27,17 +27,15 @@ export default function CardGrid({ card_grid }: CardGridProps) {
{card_grid.cards.map((card, i) => ( - +
+ +
))}
) } - -function CardWrapper({ card }: CardProps) { - return ( -
- -
- ) -} diff --git a/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx index 426cda514..1ede3b337 100644 --- a/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/HowItWorks/index.tsx @@ -4,10 +4,10 @@ import styles from "./howItWorks.module.css" export default function HowItWorks() { return ( -
+
How it works Placeholder -
+ ) } diff --git a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx index 888dc3d99..5541f649d 100644 --- a/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/LoyaltyLevels/index.tsx @@ -33,7 +33,7 @@ export default async function LoyaltyLevels() { function LevelCard({ level }: LevelCardProps) { return ( -
+
{level.tier} {level.name}

@@ -45,6 +45,6 @@ function LevelCard({ level }: LevelCardProps) { {benefit}

))} -
+ ) } diff --git a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css index a448e08f6..b1ebc5a58 100644 --- a/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css +++ b/components/Loyalty/Blocks/DynamicContent/dynamicContent.module.css @@ -8,7 +8,7 @@ .titleContainer { display: grid; - grid-template-areas: "title link"; + grid-template-areas: "title link" "subtitle subtitle"; grid-template-columns: 1fr max-content; padding-bottom: 0.8rem; } @@ -25,4 +25,5 @@ .subtitle { margin: 0; + grid-area: subtitle; } diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx index 9f324d4fa..ebf241098 100644 --- a/components/Loyalty/Blocks/DynamicContent/index.tsx +++ b/components/Loyalty/Blocks/DynamicContent/index.tsx @@ -32,25 +32,23 @@ export default function DynamicContent({ }: DynamicContentProps) { return (
-
-
- {dynamicContent.title && ( - - {dynamicContent.title} - - )} - {dynamicContent.link ? ( - - {dynamicContent.link.text} - - ) : null} -
+
+ {dynamicContent.title && ( + + {dynamicContent.title} + + )} + {dynamicContent.link ? ( + + {dynamicContent.link.text} + + ) : null} {dynamicContent.subtitle && ( case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: return ( - <JsonToHtml - nodes={block.content.content.json.children} - embeds={block.content.content.embedded_itemsConnection.edges} - /> + <section> + <JsonToHtml + nodes={block.content.content.json.children} + embeds={block.content.content.embedded_itemsConnection.edges} + /> + </section> ) case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: return <DynamicContentBlock dynamicContent={block.dynamic_content} /> diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx index fe5fa82d2..1e395f5a8 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/ContactRow/index.tsx @@ -5,13 +5,9 @@ import { getValueFromContactConfig } from "@/utils/contactConfig" import styles from "./contactRow.module.css" -import type { ContactFields } from "@/types/requests/contactConfig" +import type { ContactRowProps } from "@/types/components/loyalty/sidebar" -export default async function ContactRow({ - contact, -}: { - contact: ContactFields -}) { +export default async function ContactRow({ contact }: ContactRowProps) { const data = await serverClient().contentstack.contactConfig.get({ lang: Lang.en, }) diff --git a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx index 5eeb35a66..577b4d48f 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/Contact/index.tsx @@ -19,7 +19,7 @@ export default async function Contact({ contactBlock }: ContactProps) { case JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact: return ( <ContactRow - key={`${contact.display_text}-i`} + key={`${contact.display_text}-${i}`} contact={contact} /> ) diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx index 59ee01a0c..6fc54eb0b 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx +++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx @@ -28,7 +28,7 @@ export default function JoinLoyaltyContact({ block }: JoinLoyaltyContactProps) { <span>{_("Join Scandic Friends")}</span> </Button> <div className={styles.linkContainer}> - <Link href="/login" className={styles.logoutLink}> + <Link href="/login" className={styles.loginLink}> {_("Already a friend?")} <br /> {_("Click here to log in")} </Link> diff --git a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css index a2983576f..442cb4dc0 100644 --- a/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css +++ b/components/Loyalty/Sidebar/JoinLoyalty/joinLoyalty.module.css @@ -21,7 +21,7 @@ margin: 0; } -.logoutLink { +.loginLink { text-decoration: none; color: var(--some-black-color, #2e2e2e); font-size: 1.2rem; diff --git a/components/Loyalty/Sidebar/index.tsx b/components/Loyalty/Sidebar/index.tsx index 12cdc81f6..75fdcf92f 100644 --- a/components/Loyalty/Sidebar/index.tsx +++ b/components/Loyalty/Sidebar/index.tsx @@ -7,20 +7,26 @@ import styles from "./sidebar.module.css" import { SidebarProps } from "@/types/components/loyalty/sidebar" import { SidebarTypenameEnum } from "@/types/requests/loyaltyPage" -export default function SidebarLoyalty({ block }: SidebarProps) { - switch (block.__typename) { - case SidebarTypenameEnum.LoyaltyPageSidebarContent: - return ( - <section className={styles.content}> - <JsonToHtml - embeds={block.content.content.embedded_itemsConnection.edges} - nodes={block.content.content.json.children} - /> - </section> - ) - case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact: - return <JoinLoyaltyContact block={block.join_loyalty_contact} /> - default: - return null - } +export default function SidebarLoyalty({ blocks }: SidebarProps) { + return ( + <aside> + {blocks.map((block) => { + switch (block.__typename) { + case SidebarTypenameEnum.LoyaltyPageSidebarContent: + return ( + <section className={styles.content}> + <JsonToHtml + embeds={block.content.content.embedded_itemsConnection.edges} + nodes={block.content.content.json.children} + /> + </section> + ) + case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact: + return <JoinLoyaltyContact block={block.join_loyalty_contact} /> + default: + return null + } + })} + </aside> + ) } diff --git a/components/Loyalty/Sidebar/sidebar.module.css b/components/Loyalty/Sidebar/sidebar.module.css index 71e960078..b757da7a0 100644 --- a/components/Loyalty/Sidebar/sidebar.module.css +++ b/components/Loyalty/Sidebar/sidebar.module.css @@ -1,9 +1,5 @@ -.content { - padding: 0 1.6rem; -} - -@media screen and (min-width: 950px) { +@media screen and (max-width: 950px) { .content { - padding: 0; + padding: 0 1.6rem; } } diff --git a/components/TempDesignSystem/Card/index.tsx b/components/TempDesignSystem/Card/index.tsx index 11523155c..afb352530 100644 --- a/components/TempDesignSystem/Card/index.tsx +++ b/components/TempDesignSystem/Card/index.tsx @@ -15,7 +15,7 @@ export default function Card({ openInNewTab = false, }: CardProps) { return ( - <div className={styles.linkCard}> + <article className={styles.linkCard}> {title ? ( <Title level="h3" weight="semiBold"> {title} @@ -33,6 +33,6 @@ export default function Card({ </Link> </Button> ) : null} - </div> + </article> ) } diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 743df0539..b308e6680 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -7,8 +7,8 @@ query GetLoyaltyPage($locale: String!, $url: String!) { all_loyalty_page(where: { url: $url, locale: $locale }) { items { blocks { + __typename ... on LoyaltyPageBlocksDynamicContent { - __typename dynamic_content { title subtitle @@ -28,7 +28,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } ... on LoyaltyPageBlocksCardGrid { - __typename card_grid { title subtitle @@ -51,7 +50,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } ... on LoyaltyPageBlocksContent { - __typename content { content { json @@ -70,8 +68,8 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } title sidebar { + __typename ... on LoyaltyPageSidebarJoinLoyaltyContact { - __typename join_loyalty_contact { title preamble @@ -87,7 +85,6 @@ query GetLoyaltyPage($locale: String!, $url: String!) { } } ... on LoyaltyPageSidebarContent { - __typename content { content { json diff --git a/server/routers/contentstack/contactConfig/input.ts b/server/routers/contentstack/contactConfig/input.ts new file mode 100644 index 000000000..b6be31232 --- /dev/null +++ b/server/routers/contentstack/contactConfig/input.ts @@ -0,0 +1,5 @@ +import { z } from "zod" + +import { Lang } from "@/constants/languages" + +export const getConfigInput = z.object({ lang: z.nativeEnum(Lang) }) diff --git a/server/routers/contentstack/contactConfig/output.ts b/server/routers/contentstack/contactConfig/output.ts new file mode 100644 index 000000000..c42a19dc9 --- /dev/null +++ b/server/routers/contentstack/contactConfig/output.ts @@ -0,0 +1,60 @@ +import { z } from "zod" + +// Help me write this zod schema based on the type ContactConfig +export const validateContactConfigSchema = z.object({ + all_contact_config: z.object({ + items: z.array( + z.object({ + email: z.object({ + name: z.string().nullable(), + address: z.string().nullable(), + }), + email_loyalty: z.object({ + name: z.string().nullable(), + address: z.string().nullable(), + }), + mailing_address: z.object({ + zip: z.string().nullable(), + street: z.string().nullable(), + name: z.string().nullable(), + city: z.string().nullable(), + country: z.string().nullable(), + }), + phone: z.object({ + number: z.string().nullable(), + name: z.string().nullable(), + }), + phone_loyalty: z.object({ + number: z.string().nullable(), + name: z.string().nullable(), + }), + visiting_address: z.object({ + zip: z.string().nullable(), + country: z.string().nullable(), + city: z.string().nullable(), + street: z.string().nullable(), + }), + }) + ), + }), +}) + +export enum ContactFieldGroupsEnum { + email = "email", + email_loyalty = "email_loyalty", + mailing_address = "mailing_address", + phone = "phone", + phone_loyalty = "phone_loyalty", + visiting_address = "visiting_address", +} + +export type ContactFieldGroups = keyof typeof ContactFieldGroupsEnum + +export type ContactConfigData = z.infer<typeof validateContactConfigSchema> + +export type ContactConfig = ContactConfigData["all_contact_config"]["items"][0] + +export type ContactFields = { + display_text?: string + contact_field: string +} diff --git a/server/routers/contentstack/contactConfig/query.ts b/server/routers/contentstack/contactConfig/query.ts index 83d183c54..6985aee67 100644 --- a/server/routers/contentstack/contactConfig/query.ts +++ b/server/routers/contentstack/contactConfig/query.ts @@ -1,32 +1,34 @@ -import { z } from "zod" - +import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql" +import { request } from "@/lib/graphql/request" import { badRequestError } from "@/server/errors/trpc" import { publicProcedure, router } from "@/server/trpc" -import { request } from "@/lib/graphql/request" -import { Lang } from "@/constants/languages" -import GetContactConfig from "@/lib/graphql/Query/ContactConfig.graphql" - -import type { GetContactConfigData } from "@/types/requests/contactConfig" +import { getConfigInput } from "./input" +import { type ContactConfigData, validateContactConfigSchema } from "./output" export const contactConfigQueryRouter = router({ - get: publicProcedure - .input(z.object({ lang: z.nativeEnum(Lang) })) - .query(async ({ input }) => { - const contactConfig = await request<GetContactConfigData>( - GetContactConfig, - { - locale: input.lang, - }, - { - tags: [`contact-config-${input.lang}`], - } - ) + get: publicProcedure.input(getConfigInput).query(async ({ input }) => { + try { + const contactConfig = await request<ContactConfigData>(GetContactConfig, { + locale: input.lang, + }) - if (contactConfig.data && contactConfig.data.all_contact_config.total) { - return contactConfig.data.all_contact_config.items[0] + if (!contactConfig.data) { + throw badRequestError() } + const validatedContactConfigConfig = + validateContactConfigSchema.safeParse(contactConfig.data) + + if (!validatedContactConfigConfig.success) { + console.error(validatedContactConfigConfig.error) + throw badRequestError() + } + + return validatedContactConfigConfig.data.all_contact_config.items[0] + } catch (e) { + console.log(e) throw badRequestError() - }), + } + }), }) diff --git a/server/routers/contentstack/index.ts b/server/routers/contentstack/index.ts index 3f765813d..2841129f4 100644 --- a/server/routers/contentstack/index.ts +++ b/server/routers/contentstack/index.ts @@ -1,8 +1,8 @@ import { router } from "@/server/trpc" import { breadcrumbsRouter } from "./breadcrumbs" -import { loyaltyPageRouter } from "./loyaltyPage" import { contactConfigRouter } from "./contactConfig" +import { loyaltyPageRouter } from "./loyaltyPage" export const contentstackRouter = router({ breadcrumbs: breadcrumbsRouter, diff --git a/server/routers/contentstack/loyaltyPage/query.ts b/server/routers/contentstack/loyaltyPage/query.ts index 0169f7b41..6a23f2a66 100644 --- a/server/routers/contentstack/loyaltyPage/query.ts +++ b/server/routers/contentstack/loyaltyPage/query.ts @@ -31,6 +31,7 @@ export const loyaltyPageQueryRouter = router({ ) if (!validatedLoyaltyPage.success) { + console.error(validatedLoyaltyPage.error) throw badRequestError() } @@ -68,15 +69,13 @@ export const loyaltyPageQueryRouter = router({ cards: block.card_grid.cards.map((card) => { return { ...card, - link: - card.referenceConnection.totalCount > 0 - ? { - href: card.referenceConnection.edges[0].node - .url, - title: - card.referenceConnection.edges[0].node.title, - } - : undefined, + link: card.referenceConnection.totalCount + ? { + href: card.referenceConnection.edges[0].node.url, + title: + card.referenceConnection.edges[0].node.title, + } + : undefined, } }), }, @@ -86,17 +85,16 @@ export const loyaltyPageQueryRouter = router({ ...block, dynamic_content: { ...block.dynamic_content, - link: - block.dynamic_content.link.pageConnection.totalCount > 0 - ? { - text: block.dynamic_content.link.text, - href: block.dynamic_content.link.pageConnection - .edges[0].node.url, - title: - block.dynamic_content.link.pageConnection.edges[0] - .node.title, - } - : undefined, + link: block.dynamic_content.link.pageConnection.totalCount + ? { + text: block.dynamic_content.link.text, + href: block.dynamic_content.link.pageConnection + .edges[0].node.url, + title: + block.dynamic_content.link.pageConnection.edges[0] + .node.title, + } + : undefined, }, } case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: diff --git a/server/trpc.ts b/server/trpc.ts index 428e38796..95d575418 100644 --- a/server/trpc.ts +++ b/server/trpc.ts @@ -1,11 +1,12 @@ import { initTRPC } from "@trpc/server" import { env } from "@/env/server" -import { transformer } from "./transformer" -import { unauthorizedError } from "./errors/trpc" -import type { Context } from "./context" +import { unauthorizedError } from "./errors/trpc" +import { transformer } from "./transformer" + import type { Meta } from "@/types/trpc/meta" +import type { Context } from "./context" const t = initTRPC.context<Context>().meta<Meta>().create({ transformer }) diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts index b3fc03c85..2c73c8d58 100644 --- a/types/components/loyalty/blocks.ts +++ b/types/components/loyalty/blocks.ts @@ -1,7 +1,6 @@ import { Block, CardGrid, - CardGridCard, DynamicContent, RteBlockContent, } from "@/server/routers/contentstack/loyaltyPage/output" @@ -18,8 +17,6 @@ export type DynamicComponentProps = { component: DynamicContent["dynamic_content"]["component"] } -export type CardProps = { card: CardGridCard } - export type CardGridProps = Pick<CardGrid, "card_grid"> export type Content = { content: RteBlockContent["content"]["content"] } diff --git a/types/components/loyalty/sidebar.ts b/types/components/loyalty/sidebar.ts index e1bdb1098..60ef2b99f 100644 --- a/types/components/loyalty/sidebar.ts +++ b/types/components/loyalty/sidebar.ts @@ -1,10 +1,11 @@ +import { ContactFields } from "@/server/routers/contentstack/contactConfig/output" import { JoinLoyaltyContact, Sidebar, } from "@/server/routers/contentstack/loyaltyPage/output" export type SidebarProps = { - block: Sidebar + blocks: Sidebar[] } export type JoinLoyaltyContactProps = { @@ -14,3 +15,7 @@ export type JoinLoyaltyContactProps = { export type ContactProps = { contactBlock: JoinLoyaltyContact["join_loyalty_contact"]["contact"] } + +export type ContactRowProps = { + contact: ContactFields +} diff --git a/types/requests/contactConfig.ts b/types/requests/contactConfig.ts deleted file mode 100644 index d68090d16..000000000 --- a/types/requests/contactConfig.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { AllRequestResponse } from "./utils/all" - -export type ContactConfig = { - email: { - name?: string - address?: string - } - email_loyalty: { - name?: string - address?: string - } - mailing_address: { - zip?: string - street?: string - name?: string - city?: string - country?: string - } - phone: { - number?: string - name?: string - } - phone_loyalty: { - number?: string - name?: string - } - visiting_address: { - zip?: string - country?: string - city?: string - street?: string - } -} - -export enum ContactFieldGroupsEnum { - email = "email", - email_loyalty = "email_loyalty", - mailing_address = "mailing_address", - phone = "phone", - phone_loyalty = "phone_loyalty", - visiting_address = "visiting_address", -} - -export type ContactFieldGroups = keyof typeof ContactFieldGroupsEnum - -export type GetContactConfigData = { - all_contact_config: AllRequestResponse<ContactConfig> -} - -export type ContactFields = { - display_text?: string - contact_field: string -} diff --git a/types/requests/contentTypeUid.ts b/types/requests/contentTypeUid.ts index e8f358d07..5caabfa08 100644 --- a/types/requests/contentTypeUid.ts +++ b/types/requests/contentTypeUid.ts @@ -1,11 +1,13 @@ -export type GetContentTypeUidType = { - all_content_page: { - total: number - } - all_loyalty_page: { - total: number - } - all_current_blocks_page: { - total: number - } -} +import z from "zod" + +export const validateContentTypeUid = z.object({ + all_content_page: z.object({ + total: z.number(), + }), + all_loyalty_page: z.object({ + total: z.number(), + }), + all_current_blocks_page: z.object({ + total: z.number(), + }), +}) diff --git a/utils/contactConfig.ts b/utils/contactConfig.ts index 3eed6da76..7a888fe0a 100644 --- a/utils/contactConfig.ts +++ b/utils/contactConfig.ts @@ -1,7 +1,7 @@ import { ContactConfig, ContactFieldGroups, -} from "@/types/requests/contactConfig" +} from "@/server/routers/contentstack/contactConfig/output" export function getValueFromContactConfig( keyString: string, @@ -16,4 +16,5 @@ export function getValueFromContactConfig( return fieldGroup[key] } + return undefined } diff --git a/utils/contentType.ts b/utils/contentType.ts index 2d3668c90..af30d5815 100644 --- a/utils/contentType.ts +++ b/utils/contentType.ts @@ -1,10 +1,10 @@ -import { DocumentNode } from "graphql" +import { DocumentNode, print } from "graphql" import { Lang } from "@/constants/languages" import { env } from "@/env/server" -import GetContentTypeUid from "@/lib/graphql/Query/ContentTypeUid.graphql" +import { GetContentTypeUid } from "@/lib/graphql/Query/ContentTypeUid.graphql" -import type { GetContentTypeUidType } from "@/types/requests/contentTypeUid" +import { validateContentTypeUid } from "@/types/requests/contentTypeUid" export enum PageTypeEnum { CurrentBlocksPage = "CurrentBlocksPage", @@ -16,7 +16,6 @@ export async function getContentTypeByPathName( pathNameWithoutLang: string, lang = Lang.en ) { - const print = (await import("graphql/language/printer")).print const result = await fetch(env.CMS_URL, { method: "POST", headers: { @@ -33,7 +32,17 @@ export async function getContentTypeByPathName( }) const pageTypeData = await result.json() - const pageType = pageTypeData.data as GetContentTypeUidType + + const validatedContentTypeUid = validateContentTypeUid.safeParse( + pageTypeData.data + ) + + if (!validatedContentTypeUid.success) { + console.error(validatedContentTypeUid.error) + return null + } + + const pageType = validatedContentTypeUid.data if (pageType.all_content_page.total) { return PageTypeEnum.ContentPage