feat(SW-66, SW-348): search functionality and ui
This commit is contained in:
@@ -1,386 +1,187 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
|
||||
import { imageVaultAssetSchema } from "../schemas/imageVault"
|
||||
import { discriminatedUnionArray } from "@/lib/discriminatedUnion"
|
||||
|
||||
import {
|
||||
CardsGridEnum,
|
||||
ContentBlocksTypenameEnum,
|
||||
DynamicContentComponentEnum,
|
||||
JoinLoyaltyContactTypenameEnum,
|
||||
SidebarDynamicComponentEnum,
|
||||
SidebarTypenameEnum,
|
||||
} from "@/types/components/content/enums"
|
||||
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||
import { RTEEmbedsEnum } from "@/types/requests/rte"
|
||||
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"
|
||||
|
||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||
import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols"
|
||||
|
||||
// Block schemas
|
||||
export const contentPageBlockTextContent = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(z.any()),
|
||||
totalCount: z.number(),
|
||||
}),
|
||||
json: z.any(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
export const contentPageCards = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.CardsGrid),
|
||||
})
|
||||
.merge(cardsGridSchema)
|
||||
|
||||
export const contentPageShortcuts = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksShortcuts),
|
||||
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(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
export const contentPageContent = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.Content),
|
||||
})
|
||||
.merge(blockContentSchema)
|
||||
|
||||
export const contentPageDynamicContent = z.object({
|
||||
__typename: z.literal(
|
||||
ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent
|
||||
),
|
||||
dynamic_content: z.object({
|
||||
title: z.string().nullable(),
|
||||
subtitle: z.string().nullable(),
|
||||
component: z.nativeEnum(DynamicContentComponentEnum),
|
||||
link: z
|
||||
.object({
|
||||
text: z.string(),
|
||||
href: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
})
|
||||
export const contentPageDynamicContent = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.DynamicContent),
|
||||
})
|
||||
.merge(blockDynamicContentSchema)
|
||||
|
||||
export const cardBlock = z.object({
|
||||
__typename: z.literal(CardsGridEnum.Card),
|
||||
isContentCard: z.boolean(),
|
||||
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(),
|
||||
sidePeekButton: z
|
||||
.object({
|
||||
title: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
system: z.object({
|
||||
locale: z.nativeEnum(Lang),
|
||||
uid: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
export const loyaltyCardBlock = z.object({
|
||||
__typename: z.literal(CardsGridEnum.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 contentPageCardsItems = z.discriminatedUnion("__typename", [
|
||||
loyaltyCardBlock,
|
||||
cardBlock,
|
||||
])
|
||||
|
||||
export const contentPageCards = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid),
|
||||
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(contentPageCardsItems),
|
||||
}),
|
||||
})
|
||||
export const contentPageShortcuts = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.Shortcuts),
|
||||
})
|
||||
.merge(shortcutsSchema)
|
||||
|
||||
export const contentPageTextCols = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksTextCols),
|
||||
text_cols: z.object({
|
||||
columns: z.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
text: z.object({
|
||||
json: z.any(),
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(z.any()),
|
||||
totalCount: z.number(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
const contentPageBlockItem = z.discriminatedUnion("__typename", [
|
||||
contentPageBlockTextContent,
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.TextCols),
|
||||
}).merge(textColsSchema)
|
||||
|
||||
export const blocksSchema = z.discriminatedUnion("__typename", [
|
||||
contentPageCards,
|
||||
contentPageContent,
|
||||
contentPageDynamicContent,
|
||||
contentPageShortcuts,
|
||||
contentPageTextCols,
|
||||
])
|
||||
|
||||
export const contentPageSidebarTextContent = z.object({
|
||||
__typename: z.literal(SidebarTypenameEnum.ContentPageSidebarContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(z.any()),
|
||||
totalCount: z.number(),
|
||||
}),
|
||||
json: z.any(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
export const contentPageSidebarContent = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.Content),
|
||||
})
|
||||
.merge(sidebarContentSchema)
|
||||
|
||||
export const contentPageJoinLoyaltyContact = z.object({
|
||||
__typename: z.literal(
|
||||
SidebarTypenameEnum.ContentPageSidebarJoinLoyaltyContact
|
||||
),
|
||||
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.ContentPageSidebarJoinLoyaltyContactBlockContactContact
|
||||
),
|
||||
contact: z.object({
|
||||
display_text: z.string().nullable(),
|
||||
contact_field: z.string(),
|
||||
footnote: z.string().nullable(),
|
||||
}),
|
||||
})
|
||||
export const contentPageSidebarDynamicContent = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.DynamicContent),
|
||||
})
|
||||
.merge(sidebarDynamicContentSchema)
|
||||
|
||||
export const contentPageJoinLoyaltyContact = z
|
||||
.object({
|
||||
__typename: z.literal(
|
||||
ContentPageEnum.ContentStack.sidebar.JoinLoyaltyContact
|
||||
),
|
||||
}),
|
||||
})
|
||||
})
|
||||
.merge(joinLoyaltyContactSchema)
|
||||
|
||||
export const contentPageSidebarDynamicContent = z.object({
|
||||
__typename: z.literal(SidebarTypenameEnum.ContentPageSidebarDynamicContent),
|
||||
dynamic_content: z.object({
|
||||
component: z.nativeEnum(SidebarDynamicComponentEnum),
|
||||
}),
|
||||
})
|
||||
|
||||
const contentPageSidebarItem = z.discriminatedUnion("__typename", [
|
||||
contentPageSidebarTextContent,
|
||||
export const sidebarSchema = z.discriminatedUnion("__typename", [
|
||||
contentPageSidebarContent,
|
||||
contentPageSidebarDynamicContent,
|
||||
contentPageJoinLoyaltyContact,
|
||||
])
|
||||
|
||||
// Content Page Schema and types
|
||||
export const validateContentPageSchema = z.object({
|
||||
export const contentPageSchema = z.object({
|
||||
content_page: z.object({
|
||||
hero_image: tempImageVaultAssetSchema,
|
||||
blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
|
||||
sidebar: discriminatedUnionArray(sidebarSchema.options).nullable(),
|
||||
title: z.string(),
|
||||
header: z.object({
|
||||
heading: z.string(),
|
||||
preamble: z.string(),
|
||||
}),
|
||||
hero_image: imageVaultAssetSchema.nullable().optional(),
|
||||
blocks: z.array(contentPageBlockItem).nullable(),
|
||||
sidebar: z.array(contentPageSidebarItem).nullable(),
|
||||
system: z.object({
|
||||
uid: z.string(),
|
||||
locale: z.nativeEnum(Lang),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
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(CardsGridEnum.Card),
|
||||
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 loyaltyCardBlockRefs = z.object({
|
||||
__typename: z.literal(CardsGridEnum.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 contentPageBlockTextContentRefs = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: rteConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const contentPageCardsRefs = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid),
|
||||
cards_grid: z.object({
|
||||
cardConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: cardGridCardsRef,
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const contentPageShortcutsRefs = z.object({
|
||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksShortcuts),
|
||||
shortcuts: z.object({
|
||||
shortcuts: z.array(
|
||||
system: systemSchema.merge(
|
||||
z.object({
|
||||
linkConnection: rteConnectionRefs,
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const contentPageDynamicContentRefs = z.object({
|
||||
__typename: z.literal(
|
||||
ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent
|
||||
),
|
||||
dynamic_content: z.object({
|
||||
link: z.object({
|
||||
pageConnection: pageConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
/** REFS */
|
||||
const contentPageCardsRefs = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.CardsGrid),
|
||||
})
|
||||
.merge(cardGridRefsSchema)
|
||||
|
||||
const contentPageBlockContentRefs = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.Content),
|
||||
})
|
||||
.merge(blockContentRefsSchema)
|
||||
|
||||
const contentPageDynamicContentRefs = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.DynamicContent),
|
||||
})
|
||||
.merge(dynamicContentRefsSchema)
|
||||
|
||||
const contentPageShortcutsRefs = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.Shortcuts),
|
||||
})
|
||||
.merge(shortcutsRefsSchema)
|
||||
|
||||
const contentPageTextColsRefs = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.blocks.TextCols),
|
||||
})
|
||||
.merge(textColsRefsSchema)
|
||||
|
||||
const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [
|
||||
contentPageBlockTextContentRefs,
|
||||
contentPageBlockContentRefs,
|
||||
contentPageShortcutsRefs,
|
||||
contentPageCardsRefs,
|
||||
contentPageDynamicContentRefs,
|
||||
contentPageTextColsRefs,
|
||||
])
|
||||
|
||||
const contentPageSidebarTextContentRef = z.object({
|
||||
__typename: z.literal(SidebarTypenameEnum.ContentPageSidebarContent),
|
||||
content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: rteConnectionRefs,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
const contentPageSidebarContentRef = z
|
||||
.object({
|
||||
__typename: z.literal(ContentPageEnum.ContentStack.sidebar.Content),
|
||||
})
|
||||
.merge(sidebarContentRefsSchema)
|
||||
|
||||
const contentPageSidebarJoinLoyaltyContactRef = z.object({
|
||||
__typename: z.literal(
|
||||
SidebarTypenameEnum.ContentPageSidebarJoinLoyaltyContact
|
||||
),
|
||||
join_loyalty_contact: z.object({
|
||||
button: z
|
||||
.object({
|
||||
linkConnection: pageConnectionRefs,
|
||||
})
|
||||
.nullable(),
|
||||
}),
|
||||
})
|
||||
const contentPageSidebarJoinLoyaltyContactRef = z
|
||||
.object({
|
||||
__typename: z.literal(
|
||||
ContentPageEnum.ContentStack.sidebar.JoinLoyaltyContact
|
||||
),
|
||||
})
|
||||
.merge(joinLoyaltyContactRefsSchema)
|
||||
|
||||
const contentPageSidebarRefsItem = z.discriminatedUnion("__typename", [
|
||||
contentPageSidebarTextContentRef,
|
||||
contentPageSidebarContentRef,
|
||||
contentPageSidebarJoinLoyaltyContactRef,
|
||||
])
|
||||
|
||||
export const validateContentPageRefsSchema = z.object({
|
||||
export const contentPageRefsSchema = z.object({
|
||||
content_page: z.object({
|
||||
blocks: z.array(contentPageBlockRefsItem).nullable(),
|
||||
sidebar: z.array(contentPageSidebarRefsItem).nullable(),
|
||||
system: z.object({
|
||||
content_type_uid: z.string(),
|
||||
uid: z.string(),
|
||||
}),
|
||||
blocks: discriminatedUnionArray(
|
||||
contentPageBlockRefsItem.options
|
||||
).nullable(),
|
||||
sidebar: discriminatedUnionArray(
|
||||
contentPageSidebarRefsItem.options
|
||||
).nullable(),
|
||||
system: systemSchema,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,49 +1,36 @@
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { GetContentPage } from "@/lib/graphql/Query/ContentPage.graphql"
|
||||
import { GetContentPage } from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
||||
|
||||
import { makeImageVaultImage } from "@/utils/imageVault"
|
||||
import { removeMultipleSlashes } from "@/utils/url"
|
||||
|
||||
import { removeEmptyObjects } from "../../utils"
|
||||
import { validateContentPageSchema } from "./output"
|
||||
import { contentPageSchema } from "./output"
|
||||
import {
|
||||
fetchContentPageRefs,
|
||||
generatePageTags,
|
||||
getContentPageCounter,
|
||||
makeButtonObject,
|
||||
validateContentPageRefs,
|
||||
} from "./utils"
|
||||
|
||||
import {
|
||||
CardsGridEnum,
|
||||
ContentBlocksTypenameEnum,
|
||||
SidebarTypenameEnum,
|
||||
} from "@/types/components/content/enums"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
TrackingSDKPageData,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import {
|
||||
Block,
|
||||
ContentPage,
|
||||
ContentPageDataRaw,
|
||||
Sidebar,
|
||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||
import type { GetContentPageSchema } from "@/types/trpc/routers/contentstack/contentPage"
|
||||
|
||||
export const contentPageQueryRouter = router({
|
||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
||||
const { lang, uid } = ctx
|
||||
|
||||
const cleanedRefsData = await fetchContentPageRefs(lang, uid)
|
||||
const validatedRefsData = validateContentPageRefs(
|
||||
cleanedRefsData,
|
||||
const contentPageRefsData = await fetchContentPageRefs(lang, uid)
|
||||
const contentPageRefs = validateContentPageRefs(
|
||||
contentPageRefsData,
|
||||
lang,
|
||||
uid
|
||||
)
|
||||
const tags = generatePageTags(validatedRefsData, lang)
|
||||
if (!contentPageRefs) {
|
||||
return null
|
||||
}
|
||||
const tags = generatePageTags(contentPageRefs, lang)
|
||||
|
||||
getContentPageCounter.add(1, { lang, uid })
|
||||
console.info(
|
||||
@@ -53,7 +40,7 @@ export const contentPageQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
const response = await request<ContentPageDataRaw>(
|
||||
const response = await request<GetContentPageSchema>(
|
||||
GetContentPage,
|
||||
{ locale: lang, uid },
|
||||
{
|
||||
@@ -64,154 +51,27 @@ export const contentPageQueryRouter = router({
|
||||
}
|
||||
)
|
||||
|
||||
const { content_page } = removeEmptyObjects(response.data)
|
||||
if (!content_page) {
|
||||
throw notFound(response)
|
||||
}
|
||||
const contentPage = contentPageSchema.safeParse(response.data)
|
||||
|
||||
const processedBlocks = content_page.blocks
|
||||
? content_page.blocks.map((block: any) => {
|
||||
switch (block.__typename) {
|
||||
case ContentBlocksTypenameEnum.ContentPageBlocksContent:
|
||||
return block
|
||||
case ContentBlocksTypenameEnum.ContentPageBlocksShortcuts:
|
||||
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 ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid:
|
||||
return {
|
||||
...block,
|
||||
cards_grid: {
|
||||
...block.cards_grid,
|
||||
cards: block.cards_grid.cardConnection.edges.map(
|
||||
({ node: card }: { node: any }) => {
|
||||
switch (card.__typename) {
|
||||
case CardsGridEnum.Card:
|
||||
return {
|
||||
...card,
|
||||
isContentCard: !!card.is_content_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,
|
||||
sidePeekButton:
|
||||
card.has_sidepeek_button ||
|
||||
!!card.sidepeek_button?.call_to_action_text
|
||||
? {
|
||||
title:
|
||||
card.sidepeek_button.call_to_action_text,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
case CardsGridEnum.LoyaltyCard:
|
||||
return {
|
||||
...card,
|
||||
image: makeImageVaultImage(card.image),
|
||||
link: makeButtonObject(card.link),
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
}
|
||||
case ContentBlocksTypenameEnum.ContentPageBlocksDynamicContent:
|
||||
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,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return block
|
||||
}
|
||||
})
|
||||
: null
|
||||
|
||||
const sidebar = response.data.content_page.sidebar
|
||||
? response.data.content_page.sidebar.map((item: any) => {
|
||||
switch (item.__typename) {
|
||||
case SidebarTypenameEnum.ContentPageSidebarJoinLoyaltyContact:
|
||||
return {
|
||||
...item,
|
||||
join_loyalty_contact: {
|
||||
...item.join_loyalty_contact,
|
||||
button: makeButtonObject(item.join_loyalty_contact.button),
|
||||
},
|
||||
}
|
||||
default:
|
||||
return item
|
||||
}
|
||||
})
|
||||
: null
|
||||
|
||||
const heroImage = makeImageVaultImage(content_page.hero_image)
|
||||
const validatedContentPage = validateContentPageSchema.safeParse({
|
||||
content_page: {
|
||||
...content_page,
|
||||
blocks: processedBlocks,
|
||||
sidebar,
|
||||
hero_image: heroImage,
|
||||
},
|
||||
})
|
||||
|
||||
if (!validatedContentPage.success) {
|
||||
if (!contentPage.success) {
|
||||
console.error(
|
||||
`Failed to validate Contentpage Data - (lang: ${lang}, uid: ${uid})`
|
||||
)
|
||||
console.error(validatedContentPage.error?.format())
|
||||
console.error(contentPage.error?.format())
|
||||
return null
|
||||
}
|
||||
|
||||
const { hero_image, blocks, ...restContentPage } =
|
||||
validatedContentPage.data.content_page
|
||||
|
||||
const contentPage: ContentPage = {
|
||||
...restContentPage,
|
||||
heroImage,
|
||||
blocks: blocks as Block[],
|
||||
sidebar: sidebar as Sidebar[],
|
||||
}
|
||||
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: contentPage.system.uid,
|
||||
lang: contentPage.system.locale as Lang,
|
||||
publishedDate: contentPage.system.updated_at,
|
||||
createdDate: contentPage.system.created_at,
|
||||
pageId: contentPage.data.content_page.system.uid,
|
||||
lang: contentPage.data.content_page.system.locale as Lang,
|
||||
publishedDate: contentPage.data.content_page.system.updated_at,
|
||||
createdDate: contentPage.data.content_page.system.created_at,
|
||||
channel: TrackingChannelEnum["static-content-page"],
|
||||
pageType: "staticcontentpage",
|
||||
}
|
||||
|
||||
return {
|
||||
contentPage,
|
||||
contentPage: contentPage.data.content_page,
|
||||
tracking,
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { GetContentPageRefs } from "@/lib/graphql/Query/ContentPage.graphql"
|
||||
import { GetContentPageRefs } from "@/lib/graphql/Query/ContentPage/ContentPage.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
|
||||
import { generateTag, generateTags } from "@/utils/generateTag"
|
||||
import { removeMultipleSlashes } from "@/utils/url"
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import { removeEmptyObjects } from "../../utils"
|
||||
import { validateContentPageRefsSchema } from "./output"
|
||||
import { contentPageRefsSchema } from "./output"
|
||||
|
||||
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
||||
import { Edges } from "@/types/requests/utils/edges"
|
||||
import { NodeRefs } from "@/types/requests/utils/refs"
|
||||
import { ContentPageRefsDataRaw } from "@/types/trpc/routers/contentstack/contentPage"
|
||||
import { ContentPageEnum } from "@/types/enums/contentPage"
|
||||
import { System } from "@/types/requests/system"
|
||||
import {
|
||||
ContentPageRefs,
|
||||
GetContentPageRefsSchema,
|
||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||
|
||||
const meter = metrics.getMeter("trpc.contentPage")
|
||||
// OpenTelemetry metrics: ContentPage
|
||||
@@ -41,10 +41,15 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
const refsResponse = await request<ContentPageRefsDataRaw>(
|
||||
const refsResponse = await request<GetContentPageRefsSchema>(
|
||||
GetContentPageRefs,
|
||||
{ locale: lang, uid },
|
||||
{ cache: "force-cache", next: { tags: [generateTag(lang, uid)] } }
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
},
|
||||
}
|
||||
)
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
@@ -69,11 +74,15 @@ export async function fetchContentPageRefs(lang: Lang, uid: string) {
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
return removeEmptyObjects(refsResponse.data)
|
||||
return refsResponse.data
|
||||
}
|
||||
|
||||
export function validateContentPageRefs(data: any, lang: Lang, uid: string) {
|
||||
const validatedData = validateContentPageRefsSchema.safeParse(data)
|
||||
export function validateContentPageRefs(
|
||||
data: GetContentPageRefsSchema,
|
||||
lang: Lang,
|
||||
uid: string
|
||||
) {
|
||||
const validatedData = contentPageRefsSchema.safeParse(data)
|
||||
if (!validatedData.success) {
|
||||
getContentPageRefsFailCounter.add(1, {
|
||||
lang,
|
||||
@@ -97,64 +106,70 @@ export function validateContentPageRefs(data: any, lang: Lang, uid: string) {
|
||||
query: { lang, uid },
|
||||
})
|
||||
)
|
||||
|
||||
return validatedData.data
|
||||
}
|
||||
|
||||
export function generatePageTags(validatedData: any, lang: Lang): string[] {
|
||||
export function generatePageTags(
|
||||
validatedData: ContentPageRefs,
|
||||
lang: Lang
|
||||
): string[] {
|
||||
const connections = getConnections(validatedData)
|
||||
return [
|
||||
generateTags(lang, connections),
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedData.content_page.system.uid),
|
||||
].flat()
|
||||
}
|
||||
|
||||
export function getConnections(refs: ContentPageRefsDataRaw) {
|
||||
const connections: Edges<NodeRefs>[] = []
|
||||
if (refs.content_page.blocks) {
|
||||
refs.content_page.blocks.forEach((item) => {
|
||||
switch (item.__typename) {
|
||||
case ContentBlocksTypenameEnum.ContentPageBlocksContent: {
|
||||
if (item.content.content.embedded_itemsConnection.edges.length) {
|
||||
connections.push(item.content.content.embedded_itemsConnection)
|
||||
export function getConnections({ content_page }: ContentPageRefs) {
|
||||
const connections: System["system"][] = [content_page.system]
|
||||
if (content_page.blocks) {
|
||||
content_page.blocks.forEach((block) => {
|
||||
switch (block.__typename) {
|
||||
case ContentPageEnum.ContentStack.blocks.Content:
|
||||
{
|
||||
if (block.content.length) {
|
||||
// TS has trouble infering the filtered types
|
||||
// @ts-ignore
|
||||
connections.push(...block.content)
|
||||
}
|
||||
}
|
||||
break
|
||||
case ContentPageEnum.ContentStack.blocks.Shortcuts: {
|
||||
if (block.shortcuts.shortcuts.length) {
|
||||
connections.push(...block.shortcuts.shortcuts)
|
||||
}
|
||||
break
|
||||
}
|
||||
case ContentBlocksTypenameEnum.ContentPageBlocksShortcuts: {
|
||||
item.shortcuts.shortcuts.forEach((shortcut) => {
|
||||
if (shortcut.linkConnection.edges.length) {
|
||||
connections.push(shortcut.linkConnection)
|
||||
}
|
||||
})
|
||||
case ContentPageEnum.ContentStack.blocks.TextCols: {
|
||||
if (block.text_cols.length) {
|
||||
connections.push(...block.text_cols)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (content_page.sidebar) {
|
||||
content_page.sidebar.forEach((block) => {
|
||||
switch (block.__typename) {
|
||||
case ContentPageEnum.ContentStack.sidebar.Content:
|
||||
if (block.content.length) {
|
||||
connections.push(...block.content)
|
||||
}
|
||||
break
|
||||
|
||||
case ContentPageEnum.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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user