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
+55 -165
View File
@@ -1,197 +1,87 @@
import { z } from "zod"
import { Lang } from "@/constants/languages"
import { discriminatedUnionArray } from "@/lib/discriminatedUnion"
import {
ContentEntries,
DynamicContentComponents,
} from "@/types/components/myPages/myPage/enums"
import { Embeds } from "@/types/requests/embeds"
import { PageLinkEnum } from "@/types/requests/pageLinks"
import { Edges } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node"
dynamicContentRefsSchema,
dynamicContentSchema,
} from "../schemas/blocks/dynamicContent"
import {
shortcutsRefsSchema,
shortcutsSchema,
} from "../schemas/blocks/shortcuts"
import { textContentSchema } from "../schemas/blocks/textContent"
import { page } from "../schemas/metadata"
import { systemSchema } from "../schemas/system"
const accountPageShortcuts = z.object({
__typename: z.literal(ContentEntries.AccountPageContentShortcuts),
shortcuts: z.object({
title: z.string().nullable(),
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),
}),
original_url: z.string().nullable().optional(),
url: z.string(),
title: z.string(),
}),
})
),
totalCount: z.number(),
}),
text: z.string().nullable(),
open_in_new_tab: z.boolean(),
})
),
}),
})
import { AccountPageEnum } from "@/types/enums/accountPage"
const accountPageDynamicContent = z.object({
__typename: z.literal(ContentEntries.AccountPageContentDynamicContent),
dynamic_content: z.object({
title: z.string().nullable(),
preamble: z.string().nullable(),
component: z.nativeEnum(DynamicContentComponents),
link: 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(),
original_url: z.string().nullable().optional(),
title: z.string(),
}),
})
),
totalCount: z.number(),
}),
link_text: z.string(),
}),
}),
})
const accountPageDynamicContent = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentSchema)
const accountPageTextContent = z.object({
__typename: z.literal(ContentEntries.AccountPageContentTextContent),
text_content: z.object({
content: z.object({
json: z.any(),
embedded_itemsConnection: z.object({
edges: z.array(z.any()),
totalCount: z.number(),
}),
}),
}),
})
const accountPageShortcuts = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.ShortCuts),
})
.merge(shortcutsSchema)
type TextContentRaw = z.infer<typeof accountPageTextContent>
const accountPageTextContent = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.TextContent),
})
.merge(textContentSchema)
type DynamicContentRaw = z.infer<typeof accountPageDynamicContent>
type ShortcutsRaw = z.infer<typeof accountPageShortcuts>
export type Shortcuts = Omit<ShortcutsRaw, "shortcuts"> & {
shortcuts: Omit<ShortcutsRaw["shortcuts"], "shortcuts"> & {
shortcuts: {
text?: string
openInNewTab: boolean
url: string
title: string
}[]
}
}
export type RteTextContent = Omit<TextContentRaw, "text_content"> & {
text_content: {
content: {
json: RTEDocument
embedded_itemsConnection: Edges<Embeds>
}
}
}
export type AccountPageContentItem =
| DynamicContentRaw
| Shortcuts
| RteTextContent
const accountPageContentItem = z.discriminatedUnion("__typename", [
accountPageShortcuts,
export const blocksSchema = z.discriminatedUnion("__typename", [
accountPageDynamicContent,
accountPageShortcuts,
accountPageTextContent,
])
export const validateAccountPageSchema = z.object({
export const accountPageSchema = z.object({
account_page: z.object({
content: discriminatedUnionArray(blocksSchema.options),
heading: z.string().nullable(),
url: z.string(),
title: z.string(),
content: z.array(accountPageContentItem),
system: z.object({
uid: z.string(),
locale: z.nativeEnum(Lang),
created_at: z.string(),
updated_at: z.string(),
}),
}),
})
export type AccountPageDataRaw = z.infer<typeof validateAccountPageSchema>
type AccountPageRaw = AccountPageDataRaw["account_page"]
export type AccountPage = Omit<AccountPageRaw, "content"> & {
content: AccountPageContentItem[]
}
// 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 accountPageShortcutsRefs = z.object({
__typename: z.literal(ContentEntries.AccountPageContentShortcuts),
shortcuts: z.object({
shortcuts: z.array(
url: z.string(),
system: systemSchema.merge(
z.object({
linkConnection: pageConnectionRefs,
created_at: z.string(),
updated_at: z.string(),
})
),
}),
})
const accountPageDynamicContentRefs = z.object({
__typename: z.literal(ContentEntries.AccountPageContentDynamicContent),
dynamic_content: z.object({
link: z.object({
linkConnection: pageConnectionRefs,
}),
}),
})
const accountPageDynamicContentRefs = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.DynamicContent),
})
.merge(dynamicContentRefsSchema)
const accountPageShortcutsRefs = z
.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.ShortCuts),
})
.merge(shortcutsRefsSchema)
const accountPageContentItemRefs = z.discriminatedUnion("__typename", [
z.object({
__typename: z.literal(AccountPageEnum.ContentStack.blocks.TextContent),
}),
accountPageDynamicContentRefs,
accountPageShortcutsRefs,
])
export const validateAccountPageRefsSchema = z.object({
export const accountPageRefsSchema = z.object({
account_page: z.object({
content: z.array(accountPageContentItemRefs),
system: z.object({
content_type_uid: z.string(),
uid: z.string(),
}),
content: discriminatedUnionArray(accountPageContentItemRefs.options),
system: systemSchema,
}),
})
export type AccountPageRefsDataRaw = z.infer<
typeof validateAccountPageRefsSchema
>
export const accountPageMetadataSchema = z.object({
account_page: page,
})
@@ -4,7 +4,8 @@ import { Lang } from "@/constants/languages"
import {
GetAccountPage,
GetAccountPageRefs,
} from "@/lib/graphql/Query/AccountPage.graphql"
} from "@/lib/graphql/Query/AccountPage/AccountPage.graphql"
import { GetMyPagesMetaData } from "@/lib/graphql/Query/AccountPage/MetaData.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
@@ -12,27 +13,27 @@ import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
import {
generateRefsResponseTag,
generateTag,
generateTags,
generateTagsFromSystem,
} from "@/utils/generateTag"
import { removeEmptyObjects } from "../../utils"
import { getMetaData, getResponse } from "../metadata/utils"
import {
type AccountPage,
AccountPageDataRaw,
AccountPageRefsDataRaw,
validateAccountPageRefsSchema,
validateAccountPageSchema,
accountPageMetadataSchema,
accountPageRefsSchema,
accountPageSchema,
} from "./output"
import { getConnections } from "./utils"
import { ContentEntries } from "@/types/components/myPages/myPage/enums"
import {
TrackingChannelEnum,
TrackingSDKPageData,
type TrackingSDKPageData,
} from "@/types/components/tracking"
import { Embeds } from "@/types/requests/embeds"
import { Edges } from "@/types/requests/utils/edges"
import { RTEDocument } from "@/types/rte/node"
import type {
GetAccountpageMetadata,
GetAccountPageRefsSchema,
GetAccountPageSchema,
} from "@/types/trpc/routers/contentstack/accountPage"
const meter = metrics.getMeter("trpc.accountPage")
@@ -64,7 +65,7 @@ export const accountPageQueryRouter = router({
"contentstack.accountPage.refs start",
JSON.stringify({ query: { lang, uid } })
)
const refsResponse = await request<AccountPageRefsDataRaw>(
const refsResponse = await request<GetAccountPageRefsSchema>(
GetAccountPageRefs,
{
locale: lang,
@@ -96,10 +97,9 @@ export const accountPageQueryRouter = router({
throw notFoundError
}
const cleanedData = removeEmptyObjects(refsResponse.data)
const validatedAccountPageRefs =
validateAccountPageRefsSchema.safeParse(cleanedData)
const validatedAccountPageRefs = accountPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedAccountPageRefs.success) {
getAccountPageRefsFailCounter.add(1, {
lang,
@@ -120,7 +120,7 @@ export const accountPageQueryRouter = router({
const connections = getConnections(validatedAccountPageRefs.data)
const tags = [
generateTags(lang, connections),
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
].flat()
getAccountPageRefsSuccessCounter.add(1, { lang, uid, tags })
@@ -129,7 +129,7 @@ export const accountPageQueryRouter = router({
"contentstack.accountPage start",
JSON.stringify({ query: { lang, uid } })
)
const response = await request<AccountPageDataRaw>(
const response = await request<GetAccountPageSchema>(
GetAccountPage,
{
locale: lang,
@@ -161,9 +161,7 @@ export const accountPageQueryRouter = router({
throw notFoundError
}
const validatedAccountPage = validateAccountPageSchema.safeParse(
response.data
)
const validatedAccountPage = accountPageSchema.safeParse(response.data)
if (!validatedAccountPage.success) {
getAccountPageFailCounter.add(1, {
@@ -186,48 +184,6 @@ export const accountPageQueryRouter = router({
"contentstack.accountPage success",
JSON.stringify({ query: { lang, uid } })
)
// TODO: Make returned data nicer
const content = validatedAccountPage.data.account_page.content.map(
(block) => {
switch (block.__typename) {
case ContentEntries.AccountPageContentDynamicContent:
return block
case ContentEntries.AccountPageContentShortcuts:
return {
...block,
shortcuts: {
...block.shortcuts,
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({
text: shortcut.text,
openInNewTab: shortcut.open_in_new_tab,
...shortcut.linkConnection.edges[0].node,
url:
shortcut.linkConnection.edges[0].node.original_url ||
`/${shortcut.linkConnection.edges[0].node.system.locale}${shortcut.linkConnection.edges[0].node.url}`,
})),
},
}
case ContentEntries.AccountPageContentTextContent:
return {
...block,
text_content: {
content: {
json: block.text_content.content.json as RTEDocument,
embedded_itemsConnection: block.text_content.content
.embedded_itemsConnection as Edges<Embeds>,
},
},
}
default:
return null
}
}
)
const accountPage = {
...validatedAccountPage.data.account_page,
content,
} as AccountPage
const parsedtitle = response.data.account_page.title
.replaceAll(" ", "")
@@ -243,8 +199,34 @@ export const accountPageQueryRouter = router({
}
return {
accountPage,
accountPage: validatedAccountPage.data.account_page,
tracking,
}
}),
metadata: router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const variables = {
locale: ctx.lang,
uid: ctx.uid,
}
const response = await getResponse<GetAccountpageMetadata>(
GetMyPagesMetaData,
variables
)
const validatedMetadata = accountPageMetadataSchema.safeParse(
response.data
)
if (!validatedMetadata.success) {
console.error(
`Failed to validate My Page MetaData Data - (uid: ${variables.uid})`
)
console.error(validatedMetadata.error)
return null
}
return getMetaData(validatedMetadata.data.account_page)
}),
}),
})
@@ -1,25 +1,22 @@
import { AccountPageRefsDataRaw } from "./output"
import { AccountPageEnum } from "@/types/enums/accountPage"
import type { System } from "@/types/requests/system"
import type { AccountPageRefs } from "@/types/trpc/routers/contentstack/accountPage"
import { ContentEntries } from "@/types/components/myPages/myPage/enums"
import type { Edges } from "@/types/requests/utils/edges"
import type { NodeRefs } from "@/types/requests/utils/refs"
export function getConnections({ account_page }: AccountPageRefs) {
const connections: System["system"][] = [account_page.system]
export function getConnections(refs: AccountPageRefsDataRaw) {
const connections: Edges<NodeRefs>[] = []
if (refs.account_page.content) {
refs.account_page.content.forEach((item) => {
switch (item.__typename) {
case ContentEntries.AccountPageContentShortcuts: {
item.shortcuts.shortcuts.forEach((shortcut) => {
if (shortcut.linkConnection.edges.length) {
connections.push(shortcut.linkConnection)
}
})
if (account_page.content) {
account_page.content.forEach((block) => {
switch (block.__typename) {
case AccountPageEnum.ContentStack.blocks.ShortCuts: {
if (block.shortcuts.shortcuts.length) {
connections.push(...block.shortcuts.shortcuts)
}
break
}
case ContentEntries.AccountPageContentDynamicContent: {
if (item.dynamic_content.link.linkConnection.edges.length) {
connections.push(item.dynamic_content.link.linkConnection)
case AccountPageEnum.ContentStack.blocks.DynamicContent: {
if (block.dynamic_content.link) {
connections.push(block.dynamic_content.link)
}
break
}