feat(SW-66, SW-348): search functionality and ui

This commit is contained in:
Simon Emanuelsson
2024-08-28 10:47:57 +02:00
parent b9dbcf7d90
commit af850c90e7
437 changed files with 7663 additions and 9881 deletions
+164 -404
View File
@@ -1,432 +1,192 @@
import { z } from "zod"
import { Lang } from "@/constants/languages"
import { discriminatedUnionArray } from "@/lib/discriminatedUnion"
import { imageVaultAssetSchema } from "../schemas/imageVault"
import { ImageVaultAsset } from "@/types/components/imageVault"
import {
JoinLoyaltyContactTypenameEnum,
LoyaltyBlocksTypenameEnum,
LoyaltyCardsGridEnum,
LoyaltyComponentEnum,
LoyaltySidebarDynamicComponentEnum,
SidebarTypenameEnum,
} from "@/types/components/loyalty/enums"
import { Embeds } from "@/types/requests/embeds"
import { PageLinkEnum } from "@/types/requests/pageLinks"
import { RTEEmbedsEnum } from "@/types/requests/rte"
import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node"
cardGridRefsSchema,
cardsGridSchema,
} from "../schemas/blocks/cardsGrid"
import {
contentRefsSchema as blockContentRefsSchema,
contentSchema as blockContentSchema,
} from "../schemas/blocks/content"
import {
dynamicContentRefsSchema,
dynamicContentSchema as blockDynamicContentSchema,
} from "../schemas/blocks/dynamicContent"
import {
shortcutsRefsSchema,
shortcutsSchema,
} from "../schemas/blocks/shortcuts"
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
import {
contentRefsSchema as sidebarContentRefsSchema,
contentSchema as sidebarContentSchema,
} from "../schemas/sidebar/content"
import { dynamicContentSchema as sidebarDynamicContentSchema } from "../schemas/sidebar/dynamicContent"
import {
joinLoyaltyContactRefsSchema,
joinLoyaltyContactSchema,
} from "../schemas/sidebar/joinLoyaltyContact"
import { systemSchema } from "../schemas/system"
const loyaltyPageDynamicContent = z.object({
__typename: z.literal(
LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent
),
dynamic_content: z.object({
title: z.string().nullable(),
subtitle: z.string().nullable(),
component: z.nativeEnum(LoyaltyComponentEnum),
link: z
.object({
text: z.string(),
href: z.string(),
})
.optional(),
}),
})
import { LoyaltyPageEnum } from "@/types/enums/loyaltyPage"
const loyaltyPageShortcuts = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts),
shortcuts: z.object({
title: z.string().nullable(),
preamble: z.string().nullable(),
shortcuts: z.array(
z.object({
text: z.string().optional(),
openInNewTab: z.boolean(),
url: z.string(),
title: z.string(),
})
// LoyaltyPage Refs
const extendedCardGridRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.CardsGrid),
})
.merge(cardGridRefsSchema)
const extendedContentRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Content),
})
.merge(blockContentRefsSchema)
const extendedDynamicContentRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentRefsSchema)
const extendedShortcutsRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Shortcuts),
})
.merge(shortcutsRefsSchema)
const blocksRefsSchema = z.discriminatedUnion("__typename", [
extendedCardGridRefsSchema,
extendedContentRefsSchema,
extendedDynamicContentRefsSchema,
extendedShortcutsRefsSchema,
])
const contentSidebarRefsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.Content),
})
.merge(sidebarContentRefsSchema)
const extendedJoinLoyaltyContactRefsSchema = z
.object({
__typename: z.literal(
LoyaltyPageEnum.ContentStack.sidebar.JoinLoyaltyContact
),
}),
})
})
.merge(joinLoyaltyContactRefsSchema)
const cardBlock = z.object({
__typename: z.literal(LoyaltyCardsGridEnum.Card),
heading: z.string().nullable(),
body_text: z.string().nullable(),
background_image: z.any(),
scripted_top_title: z.string().nullable(),
primaryButton: z
.object({
openInNewTab: z.boolean(),
title: z.string(),
href: z.string(),
isExternal: z.boolean(),
})
.optional(),
secondaryButton: z
.object({
openInNewTab: z.boolean(),
title: z.string(),
href: z.string(),
isExternal: z.boolean(),
})
.optional(),
system: z.object({
locale: z.nativeEnum(Lang),
uid: z.string(),
const sidebarRefsSchema = z.discriminatedUnion("__typename", [
contentSidebarRefsSchema,
extendedJoinLoyaltyContactRefsSchema,
z.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.DynamicContent),
}),
})
const loyaltyCardBlock = z.object({
__typename: z.literal(LoyaltyCardsGridEnum.LoyaltyCard),
heading: z.string().nullable(),
body_text: z.string().nullable(),
image: z.any(),
link: z
.object({
openInNewTab: z.boolean(),
title: z.string(),
href: z.string(),
isExternal: z.boolean(),
})
.optional(),
system: z.object({
locale: z.nativeEnum(Lang),
uid: z.string(),
}),
})
const loyaltyPageCardsItems = z.discriminatedUnion("__typename", [
loyaltyCardBlock,
cardBlock,
])
const loyaltyPageCards = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
cards_grid: z.object({
title: z.string().nullable(),
preamble: z.string().nullable(),
layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]),
theme: z.enum(["one", "two", "three"]).nullable(),
cards: z.array(loyaltyPageCardsItems),
}),
})
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(),
export const loyaltyPageRefsSchema = z.object({
loyalty_page: z.object({
blocks: discriminatedUnionArray(blocksRefsSchema.options).optional(),
sidebar: discriminatedUnionArray(sidebarRefsSchema.options)
.optional()
.transform((data) => {
if (data) {
return data.filter(
(block) =>
block.__typename !==
LoyaltyPageEnum.ContentStack.sidebar.DynamicContent
)
}
return data
}),
json: z.any(),
}),
system: systemSchema,
}),
})
const loyaltyPageBlockItem = z.discriminatedUnion("__typename", [
loyaltyPageDynamicContent,
loyaltyPageBlockTextContent,
loyaltyPageShortcuts,
loyaltyPageCards,
// LoyaltyPage
export const extendedCardsGridSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.CardsGrid),
})
.merge(cardsGridSchema)
export const extendedContentSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Content),
})
.merge(blockContentSchema)
export const extendedDynamicContentSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(blockDynamicContentSchema)
export const extendedShortcutsSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.blocks.Shortcuts),
})
.merge(shortcutsSchema)
export const blocksSchema = z.discriminatedUnion("__typename", [
extendedCardsGridSchema,
extendedContentSchema,
extendedDynamicContentSchema,
extendedShortcutsSchema,
])
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 contentSidebarSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.Content),
})
.merge(sidebarContentSchema)
const loyaltyPageJoinLoyaltyContact = z.object({
__typename: z.literal(
SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact
),
join_loyalty_contact: z.object({
title: z.string().nullable(),
preamble: z.string().nullable(),
button: z
.object({
openInNewTab: z.boolean(),
title: z.string(),
href: z.string(),
isExternal: z.boolean(),
})
.nullable(),
contact: z.array(
z.object({
__typename: z.literal(
JoinLoyaltyContactTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact
),
contact: z.object({
display_text: z.string().nullable(),
contact_field: z.string(),
footnote: z.string().nullable(),
}),
})
const dynamicContentSidebarSchema = z
.object({
__typename: z.literal(LoyaltyPageEnum.ContentStack.sidebar.DynamicContent),
})
.merge(sidebarDynamicContentSchema)
export const joinLoyaltyContactSidebarSchema = z
.object({
__typename: z.literal(
LoyaltyPageEnum.ContentStack.sidebar.JoinLoyaltyContact
),
}),
})
})
.merge(joinLoyaltyContactSchema)
const loyaltyPageSidebarDynamicContent = z.object({
__typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarDynamicContent),
dynamic_content: z.object({
component: z.nativeEnum(LoyaltySidebarDynamicComponentEnum),
}),
})
const loyaltyPageSidebarItem = z.discriminatedUnion("__typename", [
loyaltyPageSidebarTextContent,
loyaltyPageSidebarDynamicContent,
loyaltyPageJoinLoyaltyContact,
export const sidebarSchema = z.discriminatedUnion("__typename", [
contentSidebarSchema,
dynamicContentSidebarSchema,
joinLoyaltyContactSidebarSchema,
])
export const validateLoyaltyPageSchema = z.object({
heading: z.string().nullable(),
preamble: z.string().nullable(),
hero_image: imageVaultAssetSchema.nullable().optional(),
blocks: z.array(loyaltyPageBlockItem).nullable(),
sidebar: z.array(loyaltyPageSidebarItem).nullable(),
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
created_at: z.string(),
updated_at: z.string(),
}),
})
// Block types
export type DynamicContent = z.infer<typeof loyaltyPageDynamicContent>
type BlockContentRaw = z.infer<typeof loyaltyPageBlockTextContent>
export interface RteBlockContent extends BlockContentRaw {
content: {
content: {
json: RTEDocument
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
}
}
}
type LoyaltyCardRaw = z.infer<typeof loyaltyCardBlock>
type LoyaltyCard = Omit<LoyaltyCardRaw, "image"> & {
image?: ImageVaultAsset
}
type CardRaw = z.infer<typeof cardBlock>
type Card = Omit<CardRaw, "background_image"> & {
backgroundImage?: ImageVaultAsset
}
type CardsGridRaw = z.infer<typeof loyaltyPageCards>
export type CardsGrid = Omit<CardsGridRaw, "cards"> & {
cards: (LoyaltyCard | Card)[]
}
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
export type Shortcuts = z.infer<typeof loyaltyPageShortcuts>
export type Block = RteBlockContent | DynamicContent | Shortcuts | CardsGrid
// Sidebar block types
type SidebarContentRaw = z.infer<typeof loyaltyPageSidebarTextContent>
export type RteSidebarContent = Omit<SidebarContentRaw, "content"> & {
content: {
content: {
json: RTEDocument
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
}
}
}
type SideBarDynamicContent = z.infer<typeof loyaltyPageSidebarDynamicContent>
export type JoinLoyaltyContact = z.infer<typeof loyaltyPageJoinLoyaltyContact>
export type Sidebar =
| JoinLoyaltyContact
| RteSidebarContent
| SideBarDynamicContent
type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema>
export type LoyaltyPage = Omit<
LoyaltyPageDataRaw,
"blocks" | "sidebar" | "hero_image"
> & {
heroImage?: ImageVaultAsset
blocks: Block[]
sidebar: Sidebar[]
}
// Refs types
const pageConnectionRefs = z.object({
edges: z.array(
z.object({
node: z.object({
__typename: z.nativeEnum(PageLinkEnum),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
}),
})
),
})
const rteConnectionRefs = z.object({
edges: z.array(
z.object({
node: z.object({
__typename: z.nativeEnum(RTEEmbedsEnum),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
}),
})
),
})
const cardBlockRefs = z.object({
__typename: z.literal(LoyaltyCardsGridEnum.Card),
primary_button: z
export const loyaltyPageSchema = z.object({
loyalty_page: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
secondary_button: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
})
const loyaltyCardBlockRefs = z.object({
__typename: z.literal(LoyaltyCardsGridEnum.LoyaltyCard),
link: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
})
const cardGridCardsRef = z.discriminatedUnion("__typename", [
loyaltyCardBlockRefs,
cardBlockRefs,
])
const loyaltyPageCardsRefs = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid),
cards_grid: z.object({
cardConnection: z.object({
edges: z.array(
blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
heading: z.string().optional(),
hero_image: tempImageVaultAssetSchema,
preamble: z.string().optional(),
sidebar: discriminatedUnionArray(sidebarSchema.options).nullable(),
title: z.string().optional(),
system: systemSchema.merge(
z.object({
node: cardGridCardsRef,
created_at: z.string(),
updated_at: z.string(),
})
),
})
.transform((data) => {
return {
blocks: data.blocks ? data.blocks : [],
heading: data.heading,
heroImage: data.hero_image,
preamble: data.preamble,
sidebar: data.sidebar ? data.sidebar : [],
system: data.system,
}
}),
}),
})
const loyaltyPageDynamicContentRefs = z.object({
__typename: z.literal(
LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent
),
dynamic_content: z.object({
link: z.object({
pageConnection: pageConnectionRefs,
}),
}),
})
const loyaltyPageShortcutsRefs = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts),
shortcuts: z.object({
shortcuts: z.array(
z.object({
linkConnection: pageConnectionRefs,
})
),
}),
})
const loyaltyPageBlockTextContentRefs = z.object({
__typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent),
content: z.object({
content: z.object({
embedded_itemsConnection: rteConnectionRefs,
}),
}),
})
const loyaltyPageBlocRefsItem = z.discriminatedUnion("__typename", [
loyaltyPageDynamicContentRefs,
loyaltyPageBlockTextContentRefs,
loyaltyPageShortcutsRefs,
loyaltyPageCardsRefs,
])
const loyaltyPageSidebarTextContentRef = z.object({
__typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarContent),
content: z.object({
content: z.object({
embedded_itemsConnection: rteConnectionRefs,
}),
}),
})
const loyaltyPageSidebarJoinLoyaltyContactRef = z.object({
__typename: z.literal(
SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact
),
join_loyalty_contact: z.object({
button: z
.object({
linkConnection: pageConnectionRefs,
})
.nullable(),
}),
})
const loyaltyPageSidebarRefsItem = z.discriminatedUnion("__typename", [
loyaltyPageSidebarTextContentRef,
loyaltyPageSidebarJoinLoyaltyContactRef,
])
export const validateLoyaltyPageRefsSchema = z.object({
loyalty_page: z.object({
blocks: z.array(loyaltyPageBlocRefsItem).nullable(),
sidebar: z.array(loyaltyPageSidebarRefsItem).nullable(),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
}),
})
export type LoyaltyPageRefsDataRaw = z.infer<
typeof validateLoyaltyPageRefsSchema
>
+44 -165
View File
@@ -4,7 +4,7 @@ import { Lang } from "@/constants/languages"
import {
GetLoyaltyPage,
GetLoyaltyPageRefs,
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
} from "@/lib/graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
@@ -12,28 +12,20 @@ import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
import {
generateRefsResponseTag,
generateTag,
generateTags,
generateTagsFromSystem,
} from "@/utils/generateTag"
import { makeImageVaultImage } from "@/utils/imageVault"
import { removeMultipleSlashes } from "@/utils/url"
import { removeEmptyObjects } from "../../utils"
import {
type LoyaltyPageRefsDataRaw,
validateLoyaltyPageRefsSchema,
validateLoyaltyPageSchema,
} from "./output"
import { getConnections, makeButtonObject } from "./utils"
import { loyaltyPageRefsSchema, loyaltyPageSchema } from "./output"
import { getConnections } from "./utils"
import {
LoyaltyBlocksTypenameEnum,
LoyaltyCardsGridEnum,
SidebarTypenameEnum,
} from "@/types/components/loyalty/enums"
import {
TrackingChannelEnum,
TrackingSDKPageData,
} from "@/types/components/tracking"
import type {
GetLoyaltyPageRefsSchema,
GetLoyaltyPageSchema,
} from "@/types/trpc/routers/contentstack/loyaltyPage"
const meter = metrics.getMeter("trpc.loyaltyPage")
// OpenTelemetry metrics: LoyaltyPage
@@ -59,19 +51,18 @@ const getLoyaltyPageFailCounter = meter.createCounter(
export const loyaltyPageQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const { lang, uid } = ctx
getLoyaltyPageRefsCounter.add(1, { lang, uid })
const metricsVariables = { lang, uid }
const variables = { locale: lang, uid }
getLoyaltyPageRefsCounter.add(1, metricsVariables)
console.info(
"contentstack.loyaltyPage.refs start",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
})
)
const refsResponse = await request<LoyaltyPageRefsDataRaw>(
const refsResponse = await request<GetLoyaltyPageRefsSchema>(
GetLoyaltyPageRefs,
{
locale: lang,
uid,
},
variables,
{
cache: "force-cache",
next: {
@@ -83,8 +74,7 @@ export const loyaltyPageQueryRouter = router({
if (!refsResponse.data) {
const notFoundError = notFound(refsResponse)
getLoyaltyPageRefsFailCounter.add(1, {
lang,
uid,
...metricsVariables,
error_type: "http_error",
error: JSON.stringify({
code: notFoundError.code,
@@ -93,222 +83,111 @@ export const loyaltyPageQueryRouter = router({
console.error(
"contentstack.loyaltyPage.refs not found error",
JSON.stringify({
query: {
lang,
uid,
},
query: metricsVariables,
error: { code: notFoundError.code },
})
)
throw notFoundError
}
const cleanedData = removeEmptyObjects(refsResponse.data)
const validatedLoyaltyPageRefs =
validateLoyaltyPageRefsSchema.safeParse(cleanedData)
const validatedLoyaltyPageRefs = loyaltyPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedLoyaltyPageRefs.success) {
getLoyaltyPageRefsFailCounter.add(1, {
lang,
uid,
...metricsVariables,
error_type: "validation_error",
error: JSON.stringify(validatedLoyaltyPageRefs.error),
})
console.error(
"contentstack.loyaltyPage.refs validation error",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
error: validatedLoyaltyPageRefs.error,
})
)
return null
}
getLoyaltyPageRefsSuccessCounter.add(1, { lang, uid })
getLoyaltyPageRefsSuccessCounter.add(1, metricsVariables)
console.info(
"contentstack.loyaltyPage.refs success",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
})
)
const connections = getConnections(validatedLoyaltyPageRefs.data)
const tags = [
generateTags(lang, connections),
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
].flat()
getLoyaltyPageCounter.add(1, { lang, uid })
getLoyaltyPageCounter.add(1, metricsVariables)
console.info(
"contentstack.loyaltyPage start",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
})
)
const response = await request<any>(
const response = await request<GetLoyaltyPageSchema>(
GetLoyaltyPage,
{
locale: lang,
uid,
},
variables,
{
cache: "force-cache",
next: {
tags,
},
next: { tags },
}
)
if (!response.data) {
const notFoundError = notFound(response)
getLoyaltyPageFailCounter.add(1, {
lang,
uid,
...metricsVariables,
error_type: "http_error",
error: JSON.stringify({ code: notFoundError.code }),
})
console.error(
"contentstack.loyaltyPage not found error",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
error: { code: notFoundError.code },
})
)
throw notFound(response)
}
const blocks = response.data.loyalty_page.blocks
? response.data.loyalty_page.blocks.map((block: any) => {
switch (block.__typename) {
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
return {
...block,
dynamic_content: {
...block.dynamic_content,
link: block.dynamic_content.link.pageConnection.edges.length
? {
text: block.dynamic_content.link.text,
href: removeMultipleSlashes(
`/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}/${block.dynamic_content.link.pageConnection.edges[0].node.url}`
),
title:
block.dynamic_content.link.pageConnection.edges[0]
.node.title,
}
: undefined,
},
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
return {
...block,
shortcuts: {
...block.shortcuts,
shortcuts: block.shortcuts.shortcuts.map((shortcut: any) => ({
text: shortcut.text,
openInNewTab: shortcut.open_in_new_tab,
...shortcut.linkConnection.edges[0].node,
url:
shortcut.linkConnection.edges[0].node.web?.original_url ||
removeMultipleSlashes(
`/${shortcut.linkConnection.edges[0].node.system.locale}/${shortcut.linkConnection.edges[0].node.url}`
),
})),
},
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
return {
...block,
cards_grid: {
...block.cards_grid,
cards: block.cards_grid.cardConnection.edges.map(
({ node: card }: { node: any }) => {
switch (card.__typename) {
case LoyaltyCardsGridEnum.LoyaltyCard:
return {
...card,
image: makeImageVaultImage(card.image),
link: makeButtonObject(card.link),
}
case LoyaltyCardsGridEnum.Card:
return {
...card,
backgroundImage: makeImageVaultImage(
card.background_image
),
primaryButton: card.has_primary_button
? makeButtonObject(card.primary_button)
: undefined,
secondaryButton: card.has_secondary_button
? makeButtonObject(card.secondary_button)
: undefined,
}
}
}
),
},
}
default:
return block
}
})
: null
const sidebar = response.data.loyalty_page.sidebar
? response.data.loyalty_page.sidebar.map((item: any) => {
switch (item.__typename) {
case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact:
return {
...item,
join_loyalty_contact: {
...item.join_loyalty_contact,
button: makeButtonObject(item.join_loyalty_contact.button),
},
}
default:
return item
}
})
: null
const loyaltyPage = {
heading: response.data.loyalty_page.heading,
preamble: response.data.loyalty_page.preamble,
heroImage: makeImageVaultImage(response.data.loyalty_page.hero_image),
system: response.data.loyalty_page.system,
blocks,
sidebar,
}
const validatedLoyaltyPage =
validateLoyaltyPageSchema.safeParse(loyaltyPage)
const validatedLoyaltyPage = loyaltyPageSchema.safeParse(response.data)
if (!validatedLoyaltyPage.success) {
getLoyaltyPageFailCounter.add(1, {
lang,
uid,
...metricsVariables,
error_type: "validation_error",
error: JSON.stringify(validatedLoyaltyPage.error),
})
console.error(
"contentstack.loyaltyPage validation error",
JSON.stringify({
query: { lang, uid },
query: metricsVariables,
error: validatedLoyaltyPage.error,
})
)
return null
}
const loyaltyPage = validatedLoyaltyPage.data.loyalty_page
const loyaltyTrackingData: TrackingSDKPageData = {
pageId: response.data.loyalty_page.system.uid,
lang: response.data.loyalty_page.system.locale as Lang,
publishedDate: response.data.loyalty_page.system.updated_at,
createdDate: response.data.loyalty_page.system.created_at,
pageId: loyaltyPage.system.uid,
lang: loyaltyPage.system.locale as Lang,
publishedDate: loyaltyPage.system.updated_at,
createdDate: loyaltyPage.system.created_at,
channel: TrackingChannelEnum["scandic-friends"],
pageType: "loyaltycontentpage",
}
getLoyaltyPageSuccessCounter.add(1, { lang, uid })
getLoyaltyPageSuccessCounter.add(1, metricsVariables)
console.info(
"contentstack.loyaltyPage success",
JSON.stringify({ query: { lang, uid } })
JSON.stringify({ query: metricsVariables })
)
// Assert LoyaltyPage type to get correct typings for RTE fields
return {
loyaltyPage,
@@ -1,106 +1,62 @@
import { removeMultipleSlashes } from "@/utils/url"
import { LoyaltyPageEnum } from "@/types/enums/loyaltyPage"
import type { System } from "@/types/requests/system"
import type { LoyaltyPageRefs } from "@/types/trpc/routers/contentstack/loyaltyPage"
import { LoyaltyPageRefsDataRaw } from "./output"
export function getConnections({ loyalty_page }: LoyaltyPageRefs) {
const connections: System["system"][] = [loyalty_page.system]
import {
LoyaltyBlocksTypenameEnum,
LoyaltyCardsGridEnum,
SidebarTypenameEnum,
} from "@/types/components/loyalty/enums"
import type { Edges } from "@/types/requests/utils/edges"
import type { NodeRefs } from "@/types/requests/utils/refs"
export function getConnections(refs: LoyaltyPageRefsDataRaw) {
const connections: Edges<NodeRefs>[] = []
if (refs.loyalty_page.blocks) {
refs.loyalty_page.blocks.forEach((item) => {
switch (item.__typename) {
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: {
if (item.content.content.embedded_itemsConnection.edges.length) {
connections.push(item.content.content.embedded_itemsConnection)
if (loyalty_page.blocks) {
loyalty_page.blocks.forEach((block) => {
switch (block.__typename) {
case LoyaltyPageEnum.ContentStack.blocks.CardsGrid:
if (block.cards_grid.length) {
connections.push(...block.cards_grid)
}
break
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid: {
connections.push(item.cards_grid.cardConnection)
item.cards_grid.cardConnection.edges.forEach((card) => {
switch (card.node.__typename) {
case LoyaltyCardsGridEnum.LoyaltyCard: {
if (card.node.link) {
connections.push(card.node.link?.linkConnection)
}
break
}
case LoyaltyCardsGridEnum.Card: {
if (card.node.primary_button) {
connections.push(card.node.primary_button?.linkConnection)
} else if (card.node.secondary_button) {
connections.push(card.node.secondary_button?.linkConnection)
}
break
}
}
})
break
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {
item.shortcuts.shortcuts.forEach((shortcut) => {
if (shortcut.linkConnection.edges.length) {
connections.push(shortcut.linkConnection)
}
})
break
}
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: {
if (item.dynamic_content.link.pageConnection.edges.length) {
connections.push(item.dynamic_content.link.pageConnection)
case LoyaltyPageEnum.ContentStack.blocks.Content:
if (block.content.length) {
// TS has trouble infering the filtered types
// @ts-ignore
connections.push(...block.content)
}
break
}
case LoyaltyPageEnum.ContentStack.blocks.DynamicContent:
if (block.dynamic_content.link) {
connections.push(block.dynamic_content.link)
}
break
case LoyaltyPageEnum.ContentStack.blocks.Shortcuts:
if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts)
}
break
default:
break
}
})
}
if (refs.loyalty_page.sidebar) {
refs.loyalty_page.sidebar?.forEach((item) => {
switch (item.__typename) {
case SidebarTypenameEnum.LoyaltyPageSidebarContent:
if (item.content.content.embedded_itemsConnection.edges.length) {
connections.push(item.content.content.embedded_itemsConnection)
if (loyalty_page.sidebar) {
loyalty_page.sidebar.forEach((block) => {
switch (block?.__typename) {
case LoyaltyPageEnum.ContentStack.sidebar.Content:
if (block.content.length) {
// TS has trouble infering the filtered types
// @ts-ignore
connections.push(...block.content)
}
break
case SidebarTypenameEnum.LoyaltyPageSidebarJoinLoyaltyContact:
if (item.join_loyalty_contact.button?.linkConnection) {
connections.push(item.join_loyalty_contact.button.linkConnection)
case LoyaltyPageEnum.ContentStack.sidebar.JoinLoyaltyContact:
if (block.join_loyalty_contact?.button) {
connections.push(block.join_loyalty_contact.button)
}
break
default:
break
}
})
}
return connections
}
export function makeButtonObject(button: any) {
if (!button) return null
const isContenstackLink =
button?.is_contentstack_link || button.linkConnection?.edges?.length
const linkConnnectionNode = isContenstackLink
? button.linkConnection.edges[0]?.node
: null
return {
openInNewTab: button?.open_in_new_tab,
title:
button.cta_text ||
(linkConnnectionNode
? linkConnnectionNode.title
: button.external_link.title),
href: linkConnnectionNode
? linkConnnectionNode.web?.original_url ||
removeMultipleSlashes(
`/${linkConnnectionNode.system.locale}/${linkConnnectionNode.url}`
)
: button.external_link.href,
isExternal: !isContenstackLink,
}
}