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 (
+
+ )
+}
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]
+}