diff --git a/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx b/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx
index 8ffc3c82d..ec2cddb4b 100644
--- a/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx
+++ b/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx
@@ -1,3 +1,5 @@
+import { getBreadcrumbsListSchema } from "@/utils/getJsonSchemas"
+
import styles from "./layout.module.css"
import {
@@ -7,7 +9,9 @@ import {
UIDParams,
} from "@/types/params"
-export default function ContentTypeLayout({
+export { generateMetadata } from "@/utils/generateMetadata"
+
+export default async function ContentTypeLayout({
breadcrumbs,
preview,
children,
@@ -17,13 +21,25 @@ export default function ContentTypeLayout({
preview: React.ReactNode
}
>) {
+ const breadcrumbsListSchema = await getBreadcrumbsListSchema()
+
return (
-
-
- {preview}
- {breadcrumbs}
- {children}
-
-
+ <>
+ {breadcrumbsListSchema ? (
+
+ ) : null}
+
+
+ {preview}
+ {breadcrumbs}
+ {children}
+
+
+ >
)
}
diff --git a/app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx b/app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx
index 215e3aec8..3460f0774 100644
--- a/app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx
+++ b/app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx
@@ -17,8 +17,6 @@ import {
UIDParams,
} from "@/types/params"
-export { generateMetadata } from "@/utils/generateMetadata"
-
export default function ContentTypePage({
params,
}: PageArgs) {
diff --git a/lib/graphql/Fragments/Breadcrumbs/Breadcrumbs.graphql b/lib/graphql/Fragments/Breadcrumbs/Breadcrumbs.graphql
index 86b35eeef..26efd5704 100644
--- a/lib/graphql/Fragments/Breadcrumbs/Breadcrumbs.graphql
+++ b/lib/graphql/Fragments/Breadcrumbs/Breadcrumbs.graphql
@@ -1,4 +1,5 @@
#import "./AccountPage.graphql"
+#import "./CollectionPage.graphql"
#import "./ContentPage.graphql"
#import "./LoyaltyPage.graphql"
@@ -9,6 +10,7 @@ fragment Breadcrumbs on Breadcrumbs {
node {
__typename
...AccountPageBreadcrumb
+ ...CollectionPageBreadcrumb
...ContentPageBreadcrumb
...LoyaltyPageBreadcrumb
}
@@ -23,6 +25,7 @@ fragment BreadcrumbsRefs on Breadcrumbs {
node {
__typename
...AccountPageBreadcrumbRef
+ ...CollectionPageBreadcrumbRef
...ContentPageBreadcrumbRef
...LoyaltyPageBreadcrumbRef
}
diff --git a/lib/graphql/Fragments/Breadcrumbs/CollectionPage.graphql b/lib/graphql/Fragments/Breadcrumbs/CollectionPage.graphql
new file mode 100644
index 000000000..debb6b1e8
--- /dev/null
+++ b/lib/graphql/Fragments/Breadcrumbs/CollectionPage.graphql
@@ -0,0 +1,24 @@
+#import "../System.graphql"
+
+fragment CollectionPageBreadcrumb on CollectionPage {
+ web {
+ breadcrumbs {
+ title
+ }
+ }
+ system {
+ ...System
+ }
+ url
+}
+
+fragment CollectionPageBreadcrumbRef on CollectionPage {
+ web {
+ breadcrumbs {
+ title
+ }
+ }
+ system {
+ ...System
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index d1a2b5f1f..87aa057cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -81,6 +81,7 @@
"lint-staged": "^15.2.2",
"netlify-plugin-cypress": "^2.2.1",
"prettier": "^3.2.5",
+ "schema-dts": "^1.1.2",
"start-server-and-test": "^2.0.3",
"ts-node": "^10.9.2",
"typescript": "^5",
@@ -17973,6 +17974,15 @@
"loose-envify": "^1.1.0"
}
},
+ "node_modules/schema-dts": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.2.tgz",
+ "integrity": "sha512-MpNwH0dZJHinVxk9bT8XUdjKTxMYrA5bLtrrGmFA6PTLwlOKnhi67XoRd6/ty+Djt6ZC0slR57qFhZDNMI6DhQ==",
+ "dev": true,
+ "peerDependencies": {
+ "typescript": ">=4.1.0"
+ }
+ },
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
diff --git a/package.json b/package.json
index 7baa98fe1..effaf858e 100644
--- a/package.json
+++ b/package.json
@@ -96,6 +96,7 @@
"lint-staged": "^15.2.2",
"netlify-plugin-cypress": "^2.2.1",
"prettier": "^3.2.5",
+ "schema-dts": "^1.1.2",
"start-server-and-test": "^2.0.3",
"ts-node": "^10.9.2",
"typescript": "^5",
diff --git a/server/routers/contentstack/breadcrumbs/output.ts b/server/routers/contentstack/breadcrumbs/output.ts
index d9b8b2f95..ac08ff7bc 100644
--- a/server/routers/contentstack/breadcrumbs/output.ts
+++ b/server/routers/contentstack/breadcrumbs/output.ts
@@ -1,16 +1,11 @@
import { z } from "zod"
+import { removeMultipleSlashes } from "@/utils/url"
+
import { systemSchema } from "../schemas/system"
+import { homeBreadcrumbs } from "./utils"
-export const getBreadcrumbsSchema = z.array(
- z.object({
- href: z.string().optional(),
- title: z.string(),
- uid: z.string(),
- })
-)
-
-const breadcrumbsRefs = z.object({
+export const breadcrumbsRefsSchema = z.object({
web: z
.object({
breadcrumbs: z
@@ -32,43 +27,7 @@ const breadcrumbsRefs = z.object({
system: systemSchema,
})
-export type BreadcrumbsRefs = z.infer
-
-export const validateMyPagesBreadcrumbsRefsContentstackSchema = z.object({
- account_page: breadcrumbsRefs,
-})
-
-export type GetMyPagesBreadcrumbsRefsData = z.infer<
- typeof validateMyPagesBreadcrumbsRefsContentstackSchema
->
-
-export const validateLoyaltyPageBreadcrumbsRefsContentstackSchema = z.object({
- loyalty_page: breadcrumbsRefs,
-})
-
-export type GetLoyaltyPageBreadcrumbsRefsData = z.infer<
- typeof validateLoyaltyPageBreadcrumbsRefsContentstackSchema
->
-
-export const validateCollectionPageBreadcrumbsRefsContentstackSchema = z.object(
- {
- collection_page: breadcrumbsRefs,
- }
-)
-
-export type GetCollectionPageBreadcrumbsRefsData = z.infer<
- typeof validateCollectionPageBreadcrumbsRefsContentstackSchema
->
-
-export const validateContentPageBreadcrumbsRefsContentstackSchema = z.object({
- content_page: breadcrumbsRefs,
-})
-
-export type GetContentPageBreadcrumbsRefsData = z.infer<
- typeof validateContentPageBreadcrumbsRefsContentstackSchema
->
-
-const page = z.object({
+export const rawBreadcrumbsDataSchema = z.object({
web: z.object({
breadcrumbs: z.object({
title: z.string(),
@@ -92,36 +51,24 @@ const page = z.object({
system: systemSchema,
})
-export type Page = z.infer
+export const breadcrumbsSchema = rawBreadcrumbsDataSchema.transform((data) => {
+ const { parentsConnection, title } = data.web.breadcrumbs
+ const parentBreadcrumbs = parentsConnection.edges.map((breadcrumb) => {
+ return {
+ href: removeMultipleSlashes(
+ `/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
+ ),
+ title: breadcrumb.node.web.breadcrumbs.title,
+ uid: breadcrumb.node.system.uid,
+ }
+ })
-export const validateMyPagesBreadcrumbsContentstackSchema = z.object({
- account_page: page,
+ const pageBreadcrumb = {
+ title,
+ uid: data.system.uid,
+ href: undefined,
+ }
+ const homeBreadcrumb = homeBreadcrumbs[data.system.locale]
+
+ return [homeBreadcrumb, parentBreadcrumbs, pageBreadcrumb].flat()
})
-
-export type GetMyPagesBreadcrumbsData = z.infer<
- typeof validateMyPagesBreadcrumbsContentstackSchema
->
-
-export const validateLoyaltyPageBreadcrumbsContentstackSchema = z.object({
- loyalty_page: page,
-})
-
-export type GetLoyaltyPageBreadcrumbsData = z.infer<
- typeof validateLoyaltyPageBreadcrumbsContentstackSchema
->
-
-export const validateContentPageBreadcrumbsContentstackSchema = z.object({
- content_page: page,
-})
-
-export type GetContentPageBreadcrumbsData = z.infer<
- typeof validateContentPageBreadcrumbsContentstackSchema
->
-
-export const validateCollectionPageBreadcrumbsContentstackSchema = z.object({
- collection_page: page,
-})
-
-export type GetCollectionPageBreadcrumbsData = z.infer<
- typeof validateCollectionPageBreadcrumbsContentstackSchema
->
diff --git a/server/routers/contentstack/breadcrumbs/query.ts b/server/routers/contentstack/breadcrumbs/query.ts
index ff96fcc49..14f32280c 100644
--- a/server/routers/contentstack/breadcrumbs/query.ts
+++ b/server/routers/contentstack/breadcrumbs/query.ts
@@ -1,3 +1,7 @@
+import { metrics } from "@opentelemetry/api"
+import { cache } from "react"
+
+import { Lang } from "@/constants/languages"
import {
GetMyPagesBreadcrumbs,
GetMyPagesBreadcrumbsRefs,
@@ -14,237 +18,198 @@ import {
GetLoyaltyPageBreadcrumbs,
GetLoyaltyPageBreadcrumbsRefs,
} from "@/lib/graphql/Query/Breadcrumbs/LoyaltyPage.graphql"
+import { request } from "@/lib/graphql/request"
+import { notFound } from "@/server/errors/trpc"
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
-import {
- GetCollectionPageBreadcrumbsData,
- GetCollectionPageBreadcrumbsRefsData,
- type GetContentPageBreadcrumbsData,
- type GetContentPageBreadcrumbsRefsData,
- type GetLoyaltyPageBreadcrumbsData,
- type GetLoyaltyPageBreadcrumbsRefsData,
- type GetMyPagesBreadcrumbsData,
- type GetMyPagesBreadcrumbsRefsData,
- validateCollectionPageBreadcrumbsContentstackSchema,
- validateCollectionPageBreadcrumbsRefsContentstackSchema,
- validateContentPageBreadcrumbsContentstackSchema,
- validateContentPageBreadcrumbsRefsContentstackSchema,
- validateLoyaltyPageBreadcrumbsContentstackSchema,
- validateLoyaltyPageBreadcrumbsRefsContentstackSchema,
- validateMyPagesBreadcrumbsContentstackSchema,
- validateMyPagesBreadcrumbsRefsContentstackSchema,
-} from "./output"
-import {
- getBreadcrumbs,
- getRefsResponse,
- getResponse,
- getTags,
- Variables,
-} from "./utils"
+import { breadcrumbsRefsSchema, breadcrumbsSchema } from "./output"
+import { getTags } from "./utils"
import { PageTypeEnum } from "@/types/requests/pageType"
+import {
+ BreadcrumbsRefsSchema,
+ RawBreadcrumbsSchema,
+} from "@/types/trpc/routers/contentstack/breadcrumbs"
-async function getLoyaltyPageBreadcrumbs(variables: Variables) {
- const refsResponse = await getRefsResponse(
- GetLoyaltyPageBreadcrumbsRefs,
- variables
- )
+const meter = metrics.getMeter("trpc.breadcrumbs")
- const validatedRefsData =
- validateLoyaltyPageBreadcrumbsRefsContentstackSchema.safeParse(
- refsResponse.data
- )
+// OpenTelemetry metrics
+const getBreadcrumbsRefsCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.refs.get"
+)
+const getBreadcrumbsRefsSuccessCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.refs.get-success"
+)
+const getBreadcrumbsRefsFailCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.refs.get-fail"
+)
+const getBreadcrumbsCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.get"
+)
+const getBreadcrumbsSuccessCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.get-success"
+)
+const getBreadcrumbsFailCounter = meter.createCounter(
+ "trpc.contentstack.breadcrumbs.get-fail"
+)
- if (!validatedRefsData.success) {
- console.error(
- `Failed to validate Loyaltypage Breadcrumbs Refs - (uid: ${variables.uid})`
- )
- console.error(validatedRefsData.error)
- return null
- }
-
- const tags = getTags(validatedRefsData.data.loyalty_page, variables)
-
- const response = await getResponse(
- GetLoyaltyPageBreadcrumbs,
- variables,
- tags
- )
-
- if (!response.data.loyalty_page.web?.breadcrumbs?.title) {
- return null
- }
-
- const validatedBreadcrumbsData =
- validateLoyaltyPageBreadcrumbsContentstackSchema.safeParse(response.data)
-
- if (!validatedBreadcrumbsData.success) {
- console.error(
- `Failed to validate Loyaltypage Breadcrumbs Data - (uid: ${variables.uid})`
- )
- console.error(validatedBreadcrumbsData.error)
- return null
- }
-
- return getBreadcrumbs(
- validatedBreadcrumbsData.data.loyalty_page,
- variables.locale
- )
+interface BreadcrumbsPageData {
+ dataKey: keyof T
+ refQuery: string
+ query: string
}
-async function getCollectionPageBreadcrumbs(variables: Variables) {
- const refsResponse =
- await getRefsResponse(
- GetCollectionPageBreadcrumbsRefs,
- variables
- )
- const validatedRefsData =
- validateCollectionPageBreadcrumbsRefsContentstackSchema.safeParse(
- refsResponse.data
- )
+const getBreadcrumbs = cache(async function fetchMemoizedBreadcrumbs(
+ { dataKey, refQuery, query }: BreadcrumbsPageData,
+ { uid, lang }: { uid: string; lang: Lang }
+) {
+ getBreadcrumbsRefsCounter.add(1, { lang, uid })
+ console.info(
+ "contentstack.breadcrumbs refs get start",
+ JSON.stringify({ query: { lang, uid } })
+ )
+ const refsResponse = await request<{ [K in keyof T]: BreadcrumbsRefsSchema }>(
+ refQuery,
+ { locale: lang, uid }
+ )
+
+ const validatedRefsData = breadcrumbsRefsSchema.safeParse(
+ refsResponse.data[dataKey]
+ )
if (!validatedRefsData.success) {
+ getBreadcrumbsRefsFailCounter.add(1, {
+ error_type: "validation_error",
+ error: JSON.stringify(validatedRefsData.error),
+ })
console.error(
- `Failed to validate CollectionPpage Breadcrumbs Refs - (uid: ${variables.uid})`
+ "contentstack.breadcrumbs refs validation error",
+ JSON.stringify({
+ error: validatedRefsData.error,
+ })
)
- console.error(validatedRefsData.error)
- return null
- }
- const tags = getTags(validatedRefsData.data.collection_page, variables)
- const response = await getResponse(
- GetCollectionPageBreadcrumbs,
- variables,
- tags
- )
- if (!response.data.collection_page.web?.breadcrumbs?.title) {
- return null
- }
- const validatedBreadcrumbsData =
- validateCollectionPageBreadcrumbsContentstackSchema.safeParse(response.data)
- if (!validatedBreadcrumbsData.success) {
- console.error(
- `Failed to validate Collectionpage Breadcrumbs Data - (uid: ${variables.uid})`
- )
- console.error(validatedBreadcrumbsData.error)
- return null
- }
- return getBreadcrumbs(
- validatedBreadcrumbsData.data.collection_page,
- variables.locale
- )
-}
-
-async function getContentPageBreadcrumbs(variables: Variables) {
- const refsResponse = await getRefsResponse(
- GetContentPageBreadcrumbsRefs,
- variables
- )
-
- const validatedRefsData =
- validateContentPageBreadcrumbsRefsContentstackSchema.safeParse(
- refsResponse.data
- )
-
- if (!validatedRefsData.success) {
- console.error(
- `Failed to validate Contentpage Breadcrumbs Refs - (uid: ${variables.uid})`
- )
- console.error(validatedRefsData.error)
- return null
- }
-
- const tags = getTags(validatedRefsData.data.content_page, variables)
-
- const response = await getResponse(
- GetContentPageBreadcrumbs,
- variables,
- tags
- )
-
- if (!response.data.content_page.web?.breadcrumbs?.title) {
- return null
- }
-
- const validatedBreadcrumbsData =
- validateContentPageBreadcrumbsContentstackSchema.safeParse(response.data)
-
- if (!validatedBreadcrumbsData.success) {
- console.error(
- `Failed to validate Contentpage Breadcrumbs Data - (uid: ${variables.uid})`
- )
- console.error(validatedBreadcrumbsData.error)
- return null
- }
-
- return getBreadcrumbs(
- validatedBreadcrumbsData.data.content_page,
- variables.locale
- )
-}
-
-async function getMyPagesBreadcrumbs(variables: Variables) {
- const refsResponse = await getRefsResponse(
- GetMyPagesBreadcrumbsRefs,
- variables
- )
-
- const validatedRefsData =
- validateMyPagesBreadcrumbsRefsContentstackSchema.safeParse(
- refsResponse.data
- )
- if (!validatedRefsData.success) {
- console.error(
- `Failed to validate My Page Breadcrumbs Refs - (uid: ${variables.uid})`
- )
- console.error(validatedRefsData.error)
- return null
- }
-
- const tags = getTags(validatedRefsData.data.account_page, variables)
-
- const response = await getResponse(
- GetMyPagesBreadcrumbs,
- variables,
- tags
- )
-
- if (!response.data.account_page.web?.breadcrumbs?.title) {
return []
}
- const validatedBreadcrumbsData =
- validateMyPagesBreadcrumbsContentstackSchema.safeParse(response.data)
+ getBreadcrumbsRefsSuccessCounter.add(1, { lang, uid })
+ console.info(
+ "contentstack.breadcrumbs refs get success",
+ JSON.stringify({ query: { lang, uid } })
+ )
- if (!validatedBreadcrumbsData.success) {
+ const tags = getTags(validatedRefsData.data, lang)
+
+ getBreadcrumbsCounter.add(1, { lang, uid })
+ console.info(
+ "contentstack.breadcrumbs get start",
+ JSON.stringify({ query: { lang, uid } })
+ )
+ const response = await request(
+ query,
+ { locale: lang, uid },
+ {
+ cache: "force-cache",
+ next: { tags },
+ }
+ )
+
+ if (!response.data) {
+ const notFoundError = notFound(response)
+ getBreadcrumbsFailCounter.add(1, {
+ lang,
+ uid,
+ error_type: "not_found",
+ error: JSON.stringify({ code: notFoundError.code }),
+ })
console.error(
- `Failed to validate My Page Breadcrumbs Data - (uid: ${variables.uid})`
+ "contentstack.breadcrumbs get not found error",
+ JSON.stringify({
+ query: { lang, uid },
+ error: { code: notFoundError.code },
+ })
)
- console.error(validatedBreadcrumbsData.error)
- return null
+ throw notFoundError
}
- return getBreadcrumbs(
- validatedBreadcrumbsData.data.account_page,
- variables.locale
+ const validatedBreadcrumbs = breadcrumbsSchema.safeParse(
+ response.data[dataKey]
)
-}
+
+ if (!validatedBreadcrumbs.success) {
+ getBreadcrumbsFailCounter.add(1, {
+ error_type: "validation_error",
+ error: JSON.stringify(validatedBreadcrumbs.error),
+ })
+ console.error(
+ "contentstack.breadcrumbs validation error",
+ JSON.stringify({
+ error: validatedBreadcrumbs.error,
+ })
+ )
+ return []
+ }
+
+ getBreadcrumbsSuccessCounter.add(1, { lang, uid })
+ console.info(
+ "contentstack.breadcrumbs get success",
+ JSON.stringify({ query: { lang, uid } })
+ )
+
+ return validatedBreadcrumbs.data
+})
export const breadcrumbsQueryRouter = router({
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
const variables = {
- locale: ctx.lang,
+ lang: ctx.lang,
uid: ctx.uid,
}
switch (ctx.contentType) {
case PageTypeEnum.accountPage:
- return await getMyPagesBreadcrumbs(variables)
+ return await getBreadcrumbs<{
+ account_page: RawBreadcrumbsSchema
+ }>(
+ {
+ dataKey: "account_page",
+ refQuery: GetMyPagesBreadcrumbsRefs,
+ query: GetMyPagesBreadcrumbs,
+ },
+ variables
+ )
case PageTypeEnum.collectionPage:
- return await getCollectionPageBreadcrumbs(variables)
+ return await getBreadcrumbs<{
+ collection_page: RawBreadcrumbsSchema
+ }>(
+ {
+ dataKey: "collection_page",
+ refQuery: GetCollectionPageBreadcrumbsRefs,
+ query: GetCollectionPageBreadcrumbs,
+ },
+ variables
+ )
case PageTypeEnum.contentPage:
- return await getContentPageBreadcrumbs(variables)
+ return await getBreadcrumbs<{
+ content_page: RawBreadcrumbsSchema
+ }>(
+ {
+ dataKey: "content_page",
+ refQuery: GetContentPageBreadcrumbsRefs,
+ query: GetContentPageBreadcrumbs,
+ },
+ variables
+ )
case PageTypeEnum.loyaltyPage:
- return await getLoyaltyPageBreadcrumbs(variables)
+ return await getBreadcrumbs<{
+ loyalty_page: RawBreadcrumbsSchema
+ }>(
+ {
+ dataKey: "loyalty_page",
+ refQuery: GetLoyaltyPageBreadcrumbsRefs,
+ query: GetLoyaltyPageBreadcrumbs,
+ },
+ variables
+ )
default:
return []
}
diff --git a/server/routers/contentstack/breadcrumbs/utils.ts b/server/routers/contentstack/breadcrumbs/utils.ts
index acb908083..ec6a32c38 100644
--- a/server/routers/contentstack/breadcrumbs/utils.ts
+++ b/server/routers/contentstack/breadcrumbs/utils.ts
@@ -1,33 +1,22 @@
import { Lang } from "@/constants/languages"
-import { request } from "@/lib/graphql/request"
-import { internalServerError, notFound } from "@/server/errors/trpc"
-import {
- generateRefsResponseTag,
- generateTag,
- generateTags,
-} from "@/utils/generateTag"
-import { removeMultipleSlashes } from "@/utils/url"
-
-import { type BreadcrumbsRefs, getBreadcrumbsSchema, Page } from "./output"
+import { generateTag, generateTags } from "@/utils/generateTag"
import type { Edges } from "@/types/requests/utils/edges"
import type { NodeRefs } from "@/types/requests/utils/refs"
+import { BreadcrumbsRefsSchema } from "@/types/trpc/routers/contentstack/breadcrumbs"
-export function getConnections(refs: BreadcrumbsRefs) {
- const connections: Edges[] = []
-
- if (refs.web?.breadcrumbs) {
- connections.push(refs.web.breadcrumbs.parentsConnection)
- }
-
- return connections
+export type Variables = {
+ lang: Lang
+ uid: string
}
export const affix = "breadcrumbs"
// TODO: Make these editable in CMS?
-export const homeBreadcrumbs = {
+export const homeBreadcrumbs: {
+ [key in keyof typeof Lang]: { href: string; title: string; uid: string }
+} = {
[Lang.da]: {
href: "/da",
title: "Hjem",
@@ -60,76 +49,19 @@ export const homeBreadcrumbs = {
},
}
-export type Variables = {
- locale: Lang
- uid: string
-}
+export function getConnections(data: BreadcrumbsRefsSchema) {
+ const connections: Edges[] = []
-export async function getRefsResponse(query: string, variables: Variables) {
- const refsResponse = await request(query, variables, {
- cache: "force-cache",
- next: {
- tags: [generateRefsResponseTag(variables.locale, variables.uid, affix)],
- },
- })
- if (!refsResponse.data) {
- throw notFound(refsResponse)
+ if (data.web?.breadcrumbs) {
+ connections.push(data.web.breadcrumbs.parentsConnection)
}
- return refsResponse
+ return connections
}
-export function getTags(page: BreadcrumbsRefs, variables: Variables) {
- const connections = getConnections(page)
- const tags = generateTags(variables.locale, connections)
- tags.push(generateTag(variables.locale, page.system.uid, affix))
+export function getTags(data: BreadcrumbsRefsSchema, lang: Lang) {
+ const connections = getConnections(data)
+ const tags = generateTags(lang, connections)
+ tags.push(generateTag(lang, data.system.uid, affix))
return tags
}
-
-export async function getResponse(
- query: string,
- variables: Variables,
- tags: string[]
-) {
- const response = await request(query, variables, {
- cache: "force-cache",
- next: { tags },
- })
- if (!response.data) {
- throw notFound(response)
- }
-
- return response
-}
-
-export function getBreadcrumbs(page: Page, lang: Lang) {
- const parentBreadcrumbs = page.web.breadcrumbs.parentsConnection.edges.map(
- (breadcrumb) => {
- return {
- href: removeMultipleSlashes(
- `/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
- ),
- title: breadcrumb.node.web.breadcrumbs.title,
- uid: breadcrumb.node.system.uid,
- }
- }
- )
-
- const pageBreadcrumb = {
- title: page.web.breadcrumbs.title,
- uid: page.system.uid,
- }
-
- const breadcrumbs = [
- homeBreadcrumbs[lang],
- parentBreadcrumbs,
- pageBreadcrumb,
- ].flat()
-
- const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
- if (!validatedBreadcrumbs.success) {
- throw internalServerError(validatedBreadcrumbs.error)
- }
-
- return validatedBreadcrumbs.data
-}
diff --git a/types/breadcrumbs.ts b/types/breadcrumbs.ts
deleted file mode 100644
index b79abcbb4..000000000
--- a/types/breadcrumbs.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { z } from "zod"
-
-import { getBreadcrumbsSchema } from "@/server/routers/contentstack/breadcrumbs/output"
-
-export interface Breadcrumbs extends z.infer {}
diff --git a/types/trpc/routers/contentstack/breadcrumbs.ts b/types/trpc/routers/contentstack/breadcrumbs.ts
new file mode 100644
index 000000000..78aa29b05
--- /dev/null
+++ b/types/trpc/routers/contentstack/breadcrumbs.ts
@@ -0,0 +1,15 @@
+import { z } from "zod"
+
+import {
+ breadcrumbsRefsSchema,
+ breadcrumbsSchema,
+ rawBreadcrumbsDataSchema,
+} from "@/server/routers/contentstack/breadcrumbs/output"
+
+export interface BreadcrumbsRefsSchema
+ extends z.input {}
+
+export interface RawBreadcrumbsSchema
+ extends z.input {}
+
+export interface Breadcrumbs extends z.output {}
diff --git a/types/trpc/routers/contentstack/metadata.ts b/types/trpc/routers/contentstack/metadata.ts
index e9a71bec1..c152f2e21 100644
--- a/types/trpc/routers/contentstack/metadata.ts
+++ b/types/trpc/routers/contentstack/metadata.ts
@@ -1,10 +1,6 @@
import { z } from "zod"
-import {
- metaDataSchema,
- rawMetaDataDataSchema,
-} from "@/server/routers/contentstack/metadata/output"
+import { rawMetaDataDataSchema } from "@/server/routers/contentstack/metadata/output"
export interface RawMetaDataSchema
extends z.input {}
-export interface MetaDataSchema extends z.output {}
diff --git a/utils/getJsonSchemas.ts b/utils/getJsonSchemas.ts
new file mode 100644
index 000000000..c297cf69f
--- /dev/null
+++ b/utils/getJsonSchemas.ts
@@ -0,0 +1,37 @@
+import { env } from "@/env/server"
+import { serverClient } from "@/lib/trpc/server"
+
+import type { BreadcrumbList, ListItem, WithContext } from "schema-dts"
+
+import { Breadcrumbs } from "@/types/trpc/routers/contentstack/breadcrumbs"
+
+function generateBreadcrumbsSchema(breadcrumbs: Breadcrumbs) {
+ if (!breadcrumbs.length) {
+ return null
+ }
+
+ const itemListElement: ListItem[] = breadcrumbs.map((item, index) => ({
+ "@type": "ListItem",
+ position: index + 1,
+ name: item.title,
+ // Only include "item" if "href" exists; otherwise, omit it
+ ...(item.href ? { item: `${env.PUBLIC_URL}${item.href}` } : {}),
+ }))
+
+ const jsonLd: WithContext = {
+ "@context": "https://schema.org",
+ "@type": "BreadcrumbList",
+ itemListElement,
+ }
+
+ return {
+ key: "breadcrumbs",
+ type: "application/ld+json",
+ jsonLd,
+ }
+}
+
+export async function getBreadcrumbsListSchema() {
+ const breadcrumbs = await serverClient().contentstack.breadcrumbs.get()
+ return generateBreadcrumbsSchema(breadcrumbs)
+}