Merged in feat/reusable-card (pull request #190)

Feat/reusable card and button design

Approved-by: Michael Zetterberg
This commit is contained in:
Christel Westerberg
2024-05-21 15:24:00 +00:00
committed by Michael Zetterberg
42 changed files with 690 additions and 426 deletions

View File

@@ -13,37 +13,28 @@ import { PageLinkEnum } from "@/types/requests/pageLinks"
import { EdgesWithTotalCount } 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().nullable(),
subtitle: z.string().nullable(),
cards: z.array(
z.object({
title: z.string().nullable(),
subtitle: z.string().nullable(),
referenceConnection: z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
}),
url: z.string(),
title: z.string(),
__typename: z.string(),
}),
})
),
totalCount: z.number(),
const pageLink = z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
}),
open_in_new_tab: z.boolean(),
cta_text: z.string().nullable(),
})
),
}),
web: z
.object({
original_url: z.string().nullable(),
})
.nullable()
.optional(),
url: z.string(),
title: z.string(),
__typename: z.string().optional(),
}),
})
),
})
const loyaltyPageDynamicContent = z.object({
__typename: z.literal(
LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent
@@ -54,21 +45,7 @@ const loyaltyPageDynamicContent = z.object({
component: z.nativeEnum(LoyaltyComponentEnum),
link: z.object({
text: z.string().nullable(),
pageConnection: z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
}),
url: z.string(),
title: z.string(),
}),
})
),
totalCount: z.number(),
}),
pageConnection: pageLink,
}),
}),
})
@@ -80,26 +57,7 @@ const loyaltyPageShortcuts = z.object({
preamble: z.string().nullable(),
shortcuts: z.array(
z.object({
linkConnection: z.object({
edges: z.array(
z.object({
node: z.object({
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
}),
url: z.string(),
web: z
.object({
original_url: z.string().nullable(),
})
.nullable(),
title: z.string(),
}),
})
),
totalCount: z.number(),
}),
linkConnection: pageLink,
text: z.string().nullable(),
open_in_new_tab: z.boolean(),
})
@@ -107,6 +65,59 @@ const loyaltyPageShortcuts = z.object({
}),
})
const cardBlock = z.object({
heading: z.string().nullable(),
body_text: z.string().nullable(),
background_image: z.any(),
scripted_top_title: z.string().nullable(),
primary_button: z
.object({
is_contentstack_link: z.boolean(),
cta_text: z.string().nullable(),
open_in_new_tab: z.boolean(),
external_link: z.object({
title: z.string().nullable(),
href: z.string().nullable(),
}),
linkConnection: pageLink,
})
.nullable(),
secondary_button: z
.object({
is_contentstack_link: z.boolean(),
cta_text: z.string().nullable(),
open_in_new_tab: z.boolean().nullable(),
external_link: z.object({
title: z.string().nullable(),
href: z.string().nullable(),
}),
linkConnection: pageLink,
})
.nullable(),
system: z.object({
locale: z.nativeEnum(Lang),
uid: z.string(),
}),
})
const loyaltyPageCards = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
cards_grid: z.object({
title: z.string().nullable(),
preamble: z.string().nullable(),
layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]),
theme: z.enum(["one", "two", "three"]).nullable(),
cardConnection: z.object({
edges: z.array(
z.object({
node: cardBlock,
})
),
}),
}),
})
const loyaltyPageBlockTextContent = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent),
content: z.object({
@@ -121,10 +132,10 @@ const loyaltyPageBlockTextContent = z.object({
})
const loyaltyPageBlockItem = z.discriminatedUnion("__typename", [
loyaltyPageBlockCardGrid,
loyaltyPageDynamicContent,
loyaltyPageBlockTextContent,
loyaltyPageShortcuts,
loyaltyPageCards,
])
const loyaltyPageSidebarTextContent = z.object({
@@ -154,7 +165,6 @@ const loyaltyPageJoinLoyaltyContact = z.object({
),
contact: z.object({
display_text: z.string().nullable(),
contact_field: z.string(),
}),
})
@@ -178,26 +188,6 @@ export const validateLoyaltyPageSchema = z.object({
})
// Block types
type CardGridRaw = z.infer<typeof loyaltyPageBlockCardGrid>
export type CardGridCard = Omit<
CardGridRaw["card_grid"]["cards"][number],
"referenceConnection"
> & {
link:
| {
href: string
title: string
}
| undefined
}
export type CardGrid = Omit<CardGridRaw, "card_grid"> & {
card_grid: Omit<CardGridRaw["card_grid"], "cards"> & {
cards: CardGridCard[]
}
}
type DynamicContentRaw = z.infer<typeof loyaltyPageDynamicContent>
export type DynamicContent = Omit<DynamicContentRaw, "dynamic_content"> & {
@@ -222,6 +212,34 @@ export interface RteBlockContent extends BlockContentRaw {
}
}
type CardsGridRaw = z.infer<typeof loyaltyPageCards>
export type CardsRaw =
CardsGridRaw["cards_grid"]["cardConnection"]["edges"][number]["node"]
export type CardsGrid = Omit<CardsGridRaw, "cards_grid"> & {
cards_grid: Omit<CardsGridRaw["cards_grid"], "cardConnection"> & {
cards: (Omit<CardsRaw, "primaryButton" | "secondaryButton"> & {
primaryButton:
| {
openInNewTab: boolean
title: string
href: string
isExternal: boolean
}
| undefined
secondaryButton:
| {
openInNewTab: boolean
title: string
href: string
isExternal: boolean
}
| undefined
})[]
}
}
type ShortcutsRaw = z.infer<typeof loyaltyPageShortcuts>
export type Shortcuts = Omit<ShortcutsRaw, "shortcuts"> & {
@@ -235,7 +253,7 @@ export type Shortcuts = Omit<ShortcutsRaw, "shortcuts"> & {
}
}
export type Block = CardGrid | RteBlockContent | DynamicContent | Shortcuts
export type Block = RteBlockContent | DynamicContent | Shortcuts | CardsGrid
// Sidebar block types
type SidebarContentRaw = z.infer<typeof loyaltyPageSidebarTextContent>
@@ -275,14 +293,34 @@ const pageConnectionRefs = z.object({
),
})
const loyaltyPageBlockCardGridRefs = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid),
card_grid: z.object({
cards: z.array(
z.object({
referenceConnection: pageConnectionRefs,
})
),
const cardBlockRefs = z.object({
primary_button: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
secondary_button: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
})
const loyaltyPageCardsRefs = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
cards_grid: z.object({
cardConnection: z.object({
edges: z.array(
z.object({
node: cardBlockRefs,
})
),
}),
}),
})
@@ -318,10 +356,10 @@ const loyaltyPageBlockTextContentRefs = z.object({
})
const loyaltyPageBlocRefsItem = z.discriminatedUnion("__typename", [
loyaltyPageBlockCardGridRefs,
loyaltyPageDynamicContentRefs,
loyaltyPageBlockTextContentRefs,
loyaltyPageShortcutsRefs,
loyaltyPageCardsRefs,
])
const loyaltyPageSidebarTextContentRef = z.object({

View File

@@ -5,7 +5,7 @@ import {
import { request } from "@/lib/graphql/request"
import { _ } from "@/lib/translation"
import { internalServerError, notFound } from "@/server/errors/trpc"
import { contentstackProcedure, publicProcedure, router } from "@/server/trpc"
import { contentstackProcedure, router } from "@/server/trpc"
import {
generateRefsResponseTag,
@@ -15,6 +15,7 @@ import {
import { removeEmptyObjects } from "../../utils"
import {
CardsRaw,
type LoyaltyPage,
type LoyaltyPageDataRaw,
type LoyaltyPageRefsDataRaw,
@@ -31,6 +32,26 @@ import { Embeds } from "@/types/requests/embeds"
import { Edges } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node"
function makeButtonObject(
button: CardsRaw["primary_button" | "secondary_button"]
) {
if (!button) return undefined
return {
openInNewTab: button.open_in_new_tab,
title:
button.cta_text ||
(button.is_contentstack_link && button.linkConnection.edges.length
? button.linkConnection.edges[0].node.title
: button.external_link.title),
href:
button.is_contentstack_link && button.linkConnection.edges.length
? button.linkConnection.edges[0].node.web?.original_url ||
`/${button.linkConnection.edges[0].node.system.locale}${button.linkConnection.edges[0].node.url}`
: button.external_link.href,
isExternal: !button.is_contentstack_link,
}
}
export const loyaltyPageQueryRouter = router({
get: contentstackProcedure.query(async ({ ctx }) => {
const { lang, uid } = ctx
@@ -60,6 +81,8 @@ export const loyaltyPageQueryRouter = router({
const validatedLoyaltyPageRefs =
validateLoyaltyPageRefsSchema.safeParse(cleanedData)
if (!validatedLoyaltyPageRefs.success) {
console.error("Bad validation for `GetLoyaltyPageRefs`")
console.error(validatedLoyaltyPageRefs.error)
throw internalServerError(validatedLoyaltyPageRefs.error)
}
@@ -114,30 +137,12 @@ export const loyaltyPageQueryRouter = router({
const blocks = validatedLoyaltyPage.data.loyalty_page.blocks
? validatedLoyaltyPage.data.loyalty_page.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
? {
href: `/${card.referenceConnection.edges[0].node.system.locale}${card.referenceConnection.edges[0].node.url}`,
title: card.cta_text || _("Read more"),
}
: undefined,
}
}),
},
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
return {
...block,
dynamic_content: {
...block.dynamic_content,
link: block.dynamic_content.link.pageConnection.totalCount
link: block.dynamic_content.link.pageConnection.edges.length
? {
text: block.dynamic_content.link.text,
href: `/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}${block.dynamic_content.link.pageConnection.edges[0].node.url}`,
@@ -174,6 +179,24 @@ export const loyaltyPageQueryRouter = router({
})),
},
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
return {
...block,
cards_grid: {
...block.cards_grid,
cards: block.cards_grid.cardConnection.edges.map(
({ node: card }) => {
return {
...card,
primaryButton: makeButtonObject(card.primary_button),
secondaryButton: makeButtonObject(
card.secondary_button
),
}
}
),
},
}
default:
return block
}

View File

@@ -15,12 +15,16 @@ export function getConnections(refs: LoyaltyPageRefsDataRaw) {
}
break
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: {
item.card_grid.cards.forEach((card) => {
if (card.referenceConnection.edges.length) {
connections.push(card.referenceConnection)
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid: {
connections.push(item.cards_grid.cardConnection)
item.cards_grid.cardConnection.edges.forEach((card) => {
if (card.node.primary_button) {
connections.push(card.node.primary_button?.linkConnection)
} else if (card.node.secondary_button) {
connections.push(card.node.secondary_button?.linkConnection)
}
})
break
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {