feat(SW-187): Footer data from contentstack
This commit is contained in:
@@ -8,7 +8,7 @@ import Navigation from "./Navigation"
|
|||||||
import styles from "./footer.module.css"
|
import styles from "./footer.module.css"
|
||||||
|
|
||||||
export default async function Footer() {
|
export default async function Footer() {
|
||||||
const footerData = await serverClient().contentstack.base.footer({
|
const footerData = await serverClient().contentstack.base.currentFooter({
|
||||||
lang: getLang(),
|
lang: getLang(),
|
||||||
})
|
})
|
||||||
if (!footerData) {
|
if (!footerData) {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ export default function FooterMainNav({ mainLinks }: FooterMainNavProps) {
|
|||||||
<nav className={styles.mainNavigation}>
|
<nav className={styles.mainNavigation}>
|
||||||
<ul className={styles.mainNavigationList}>
|
<ul className={styles.mainNavigationList}>
|
||||||
{mainLinks.map((link) => (
|
{mainLinks.map((link) => (
|
||||||
<li key={link.id} className={styles.mainNavigationItem}>
|
<li key={link.title} className={styles.mainNavigationItem}>
|
||||||
<Subtitle type="two" asChild>
|
<Subtitle type="two" asChild>
|
||||||
<Link
|
<Link
|
||||||
color="burgundy"
|
color="burgundy"
|
||||||
href={link.href}
|
href={link.url}
|
||||||
className={styles.mainNavigationLink}
|
className={styles.mainNavigationLink}
|
||||||
>
|
>
|
||||||
{link.title}
|
{link.title}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import FooterSecondaryNav from "./SecondaryNav"
|
|||||||
|
|
||||||
import styles from "./navigation.module.css"
|
import styles from "./navigation.module.css"
|
||||||
|
|
||||||
export default function FooterNavigation() {
|
export default function FooterNavigation({ ...props }) {
|
||||||
const { mainLinks, secondaryLinks, appDownloads } = footer
|
const { mainLinks } = props
|
||||||
|
const { secondaryLinks, appDownloads } = footer
|
||||||
return (
|
return (
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<div className={styles.maxWidth}>
|
<div className={styles.maxWidth}>
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import FooterDetails from "./Details"
|
import FooterDetails from "./Details"
|
||||||
import FooterNavigation from "./Navigation"
|
import FooterNavigation from "./Navigation"
|
||||||
|
|
||||||
export default function Footer() {
|
export default async function Footer() {
|
||||||
|
const footerData = await serverClient().contentstack.base.footer({
|
||||||
|
lang: getLang(),
|
||||||
|
})
|
||||||
|
if (!footerData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
console.log("footerData:", footerData)
|
||||||
return (
|
return (
|
||||||
<footer>
|
<footer>
|
||||||
<FooterNavigation />
|
<FooterNavigation {...footerData} />
|
||||||
<FooterDetails />
|
<FooterDetails />
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
|
|||||||
12
lib/graphql/Fragments/Footer/AppDownloads.graphql
Normal file
12
lib/graphql/Fragments/Footer/AppDownloads.graphql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fragment AppDownloads on Footer {
|
||||||
|
app_downloads {
|
||||||
|
title
|
||||||
|
links {
|
||||||
|
type
|
||||||
|
href {
|
||||||
|
href
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/graphql/Fragments/Footer/MainLinks.graphql
Normal file
29
lib/graphql/Fragments/Footer/MainLinks.graphql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
fragment MainLinks on Footer {
|
||||||
|
main_links {
|
||||||
|
title
|
||||||
|
open_in_new_tab
|
||||||
|
link {
|
||||||
|
href
|
||||||
|
title
|
||||||
|
}
|
||||||
|
pageConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
... on AccountPage {
|
||||||
|
title
|
||||||
|
url
|
||||||
|
}
|
||||||
|
... on LoyaltyPage {
|
||||||
|
title
|
||||||
|
url
|
||||||
|
}
|
||||||
|
... on ContentPage {
|
||||||
|
title
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
lib/graphql/Fragments/Footer/Refs/MainLinks.graphql
Normal file
18
lib/graphql/Fragments/Footer/Refs/MainLinks.graphql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
fragment MainLinksRef on Footer {
|
||||||
|
__typename
|
||||||
|
main_links {
|
||||||
|
pageConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...LoyaltyPageRef
|
||||||
|
...ContentPageRef
|
||||||
|
...AccountPageRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
20
lib/graphql/Fragments/Footer/Refs/SecondaryLinks.graphql
Normal file
20
lib/graphql/Fragments/Footer/Refs/SecondaryLinks.graphql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
fragment SecondaryLinksRef on Footer {
|
||||||
|
__typename
|
||||||
|
secondary_links {
|
||||||
|
links {
|
||||||
|
pageConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...LoyaltyPageRef
|
||||||
|
...ContentPageRef
|
||||||
|
...AccountPageRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
24
lib/graphql/Fragments/Footer/SecondaryLinks.graphql
Normal file
24
lib/graphql/Fragments/Footer/SecondaryLinks.graphql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#import "../Refs/MyPages/AccountPage.graphql"
|
||||||
|
#import "../Refs/ContentPage/ContentPage.graphql"
|
||||||
|
#import "../Refs/LoyaltyPage/LoyaltyPage.graphql"
|
||||||
|
|
||||||
|
fragment SecondaryLinks on Footer {
|
||||||
|
secondary_links {
|
||||||
|
title
|
||||||
|
links {
|
||||||
|
title
|
||||||
|
open_in_new_tab
|
||||||
|
pageConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
link {
|
||||||
|
href
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
lib/graphql/Query/Footer.graphql
Normal file
28
lib/graphql/Query/Footer.graphql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#import "../Fragments/Footer/AppDownloads.graphql"
|
||||||
|
#import "../Fragments/Footer/MainLinks.graphql"
|
||||||
|
#import "../Fragments/Footer/SecondaryLinks.graphql"
|
||||||
|
#import "../Fragments/Footer/Refs/MainLinks.graphql"
|
||||||
|
#import "../Fragments/Footer/Refs/SecondaryLinks.graphql"
|
||||||
|
#import "../Fragments/Refs/System.graphql"
|
||||||
|
|
||||||
|
query GetFooter($locale: String!) {
|
||||||
|
all_footer(limit: 1, locale: $locale) {
|
||||||
|
items {
|
||||||
|
...MainLinks
|
||||||
|
...SecondaryLinks
|
||||||
|
...AppDownloads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetFooterRef($locale: String!) {
|
||||||
|
all_footer(limit: 1, locale: $locale) {
|
||||||
|
items {
|
||||||
|
...MainLinksRef
|
||||||
|
...SecondaryLinksRef
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { Image } from "@/types/image"
|
import { Image } from "@/types/image"
|
||||||
|
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||||
|
|
||||||
// Help me write this zod schema based on the type ContactConfig
|
// Help me write this zod schema based on the type ContactConfig
|
||||||
export const validateContactConfigSchema = z.object({
|
export const validateContactConfigSchema = z.object({
|
||||||
@@ -165,7 +166,7 @@ const validateNavigationItem = z.object({
|
|||||||
|
|
||||||
export type NavigationItem = z.infer<typeof validateNavigationItem>
|
export type NavigationItem = z.infer<typeof validateNavigationItem>
|
||||||
|
|
||||||
export const validateFooterConfigSchema = z.object({
|
export const validateCurrentFooterConfigSchema = z.object({
|
||||||
all_current_footer: z.object({
|
all_current_footer: z.object({
|
||||||
items: z.array(
|
items: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -232,16 +233,18 @@ export const validateFooterConfigSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type FooterDataRaw = z.infer<typeof validateFooterConfigSchema>
|
export type CurrentFooterDataRaw = z.infer<
|
||||||
|
typeof validateCurrentFooterConfigSchema
|
||||||
|
>
|
||||||
|
|
||||||
export type FooterData = Omit<
|
export type CurrentFooterData = Omit<
|
||||||
FooterDataRaw["all_current_footer"]["items"][0],
|
CurrentFooterDataRaw["all_current_footer"]["items"][0],
|
||||||
"logoConnection"
|
"logoConnection"
|
||||||
> & {
|
> & {
|
||||||
logo: Image
|
logo: Image
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateFooterRefConfigSchema = z.object({
|
const validateCurrentFooterRefConfigSchema = z.object({
|
||||||
all_current_footer: z.object({
|
all_current_footer: z.object({
|
||||||
items: z.array(
|
items: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -254,4 +257,106 @@ const validateFooterRefConfigSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export type CurrentFooterRefDataRaw = z.infer<
|
||||||
|
typeof validateCurrentFooterRefConfigSchema
|
||||||
|
>
|
||||||
|
|
||||||
|
const validateExternalLink = z
|
||||||
|
.object({
|
||||||
|
href: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
|
||||||
|
const validateInternalLink = z
|
||||||
|
.object({
|
||||||
|
edges: z.array(
|
||||||
|
z.object({
|
||||||
|
node: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
|
||||||
|
const validateLinkItem = z.object({
|
||||||
|
title: z.string(),
|
||||||
|
open_in_new_tab: z.boolean(),
|
||||||
|
link: validateExternalLink,
|
||||||
|
pageConnection: validateInternalLink,
|
||||||
|
})
|
||||||
|
|
||||||
|
export type FooterLinkItem = z.infer<typeof validateLinkItem>
|
||||||
|
|
||||||
|
export const validateFooterConfigSchema = z.object({
|
||||||
|
all_footer: z.object({
|
||||||
|
items: z.array(
|
||||||
|
z.object({
|
||||||
|
main_links: z.array(validateLinkItem),
|
||||||
|
app_downloads: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
links: z.array(
|
||||||
|
z.object({
|
||||||
|
type: z.string(),
|
||||||
|
href: validateExternalLink,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
secondary_links: z.array(
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
links: z.array(validateLinkItem),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type FooterDataRaw = z.infer<typeof validateFooterConfigSchema>
|
||||||
|
export type FooterData = FooterDataRaw["all_footer"]["items"][0]
|
||||||
|
|
||||||
|
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 validateFooterRefConfigSchema = z.object({
|
||||||
|
all_footer: z.object({
|
||||||
|
items: z.array(
|
||||||
|
z.object({
|
||||||
|
main_links: z.array(
|
||||||
|
z.object({
|
||||||
|
pageConnection: pageConnectionRefs,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
secondary_links: z.array(
|
||||||
|
z.object({
|
||||||
|
links: z.array(
|
||||||
|
z.object({
|
||||||
|
pageConnection: pageConnectionRefs,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
export type FooterRefDataRaw = z.infer<typeof validateFooterRefConfigSchema>
|
export type FooterRefDataRaw = z.infer<typeof validateFooterRefConfigSchema>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
GetCurrentHeader,
|
GetCurrentHeader,
|
||||||
GetCurrentHeaderRef,
|
GetCurrentHeaderRef,
|
||||||
} from "@/lib/graphql/Query/CurrentHeader.graphql"
|
} from "@/lib/graphql/Query/CurrentHeader.graphql"
|
||||||
|
import { GetFooter, GetFooterRef } from "@/lib/graphql/Query/Footer.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||||
@@ -18,15 +19,19 @@ import { generateTag } from "@/utils/generateTag"
|
|||||||
import { langInput } from "./input"
|
import { langInput } from "./input"
|
||||||
import {
|
import {
|
||||||
type ContactConfigData,
|
type ContactConfigData,
|
||||||
|
CurrentFooterDataRaw,
|
||||||
|
CurrentFooterRefDataRaw,
|
||||||
FooterDataRaw,
|
FooterDataRaw,
|
||||||
FooterRefDataRaw,
|
FooterRefDataRaw,
|
||||||
HeaderData,
|
HeaderData,
|
||||||
HeaderDataRaw,
|
HeaderDataRaw,
|
||||||
HeaderRefDataRaw,
|
HeaderRefDataRaw,
|
||||||
validateContactConfigSchema,
|
validateContactConfigSchema,
|
||||||
|
validateCurrentFooterConfigSchema,
|
||||||
validateFooterConfigSchema,
|
validateFooterConfigSchema,
|
||||||
validateHeaderConfigSchema,
|
validateHeaderConfigSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
|
import { transformPageConnectionLinks } from "./utils"
|
||||||
|
|
||||||
const meter = metrics.getMeter("trpc.contentstack.base")
|
const meter = metrics.getMeter("trpc.contentstack.base")
|
||||||
// OpenTelemetry metrics: ContactConfig
|
// OpenTelemetry metrics: ContactConfig
|
||||||
@@ -218,6 +223,90 @@ export const baseQueryRouter = router({
|
|||||||
logo,
|
logo,
|
||||||
} as HeaderData
|
} as HeaderData
|
||||||
}),
|
}),
|
||||||
|
currentFooter: contentstackBaseProcedure
|
||||||
|
.input(langInput)
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
getFooterRefCounter.add(1, { lang: input.lang })
|
||||||
|
console.info(
|
||||||
|
"contentstack.footer.ref start",
|
||||||
|
JSON.stringify({ query: { lang: input.lang } })
|
||||||
|
)
|
||||||
|
const responseRef = await request<CurrentFooterRefDataRaw>(
|
||||||
|
GetCurrentFooterRef,
|
||||||
|
{
|
||||||
|
locale: input.lang,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// There's currently no error handling/validation for the responseRef, should it be added?
|
||||||
|
getFooterCounter.add(1, { lang: input.lang })
|
||||||
|
console.info(
|
||||||
|
"contentstack.footer start",
|
||||||
|
JSON.stringify({
|
||||||
|
query: {
|
||||||
|
lang: input.lang,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const response = await request<CurrentFooterDataRaw>(
|
||||||
|
GetCurrentFooter,
|
||||||
|
{
|
||||||
|
locale: input.lang,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tags: [
|
||||||
|
generateTag(
|
||||||
|
input.lang,
|
||||||
|
responseRef.data.all_current_footer.items[0].system.uid
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
const notFoundError = notFound(response)
|
||||||
|
getFooterFailCounter.add(1, {
|
||||||
|
lang: input.lang,
|
||||||
|
error_type: "not_found",
|
||||||
|
error: JSON.stringify({ code: notFoundError.code }),
|
||||||
|
})
|
||||||
|
console.error(
|
||||||
|
"contentstack.footer not found error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: {
|
||||||
|
lang: input.lang,
|
||||||
|
},
|
||||||
|
error: { code: notFoundError.code },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
throw notFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedFooterConfig = validateCurrentFooterConfigSchema.safeParse(
|
||||||
|
response.data
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!validatedFooterConfig.success) {
|
||||||
|
getFooterFailCounter.add(1, {
|
||||||
|
lang: input.lang,
|
||||||
|
error_type: "validation_error",
|
||||||
|
error: JSON.stringify(validatedFooterConfig.error),
|
||||||
|
})
|
||||||
|
console.error(
|
||||||
|
"contentstack.footer validation error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { lang: input.lang },
|
||||||
|
error: validatedFooterConfig.error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
getFooterSuccessCounter.add(1, { lang: input.lang })
|
||||||
|
console.info(
|
||||||
|
"contentstack.footer success",
|
||||||
|
JSON.stringify({ query: { lang: input.lang } })
|
||||||
|
)
|
||||||
|
return validatedFooterConfig.data.all_current_footer.items[0]
|
||||||
|
}),
|
||||||
footer: contentstackBaseProcedure
|
footer: contentstackBaseProcedure
|
||||||
.input(langInput)
|
.input(langInput)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
@@ -226,7 +315,7 @@ export const baseQueryRouter = router({
|
|||||||
"contentstack.footer.ref start",
|
"contentstack.footer.ref start",
|
||||||
JSON.stringify({ query: { lang: input.lang } })
|
JSON.stringify({ query: { lang: input.lang } })
|
||||||
)
|
)
|
||||||
const responseRef = await request<FooterRefDataRaw>(GetCurrentFooterRef, {
|
const responseRef = await request<FooterRefDataRaw>(GetFooterRef, {
|
||||||
locale: input.lang,
|
locale: input.lang,
|
||||||
})
|
})
|
||||||
// There's currently no error handling/validation for the responseRef, should it be added?
|
// There's currently no error handling/validation for the responseRef, should it be added?
|
||||||
@@ -240,7 +329,7 @@ export const baseQueryRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
const response = await request<FooterDataRaw>(
|
const response = await request<FooterDataRaw>(
|
||||||
GetCurrentFooter,
|
GetFooter,
|
||||||
{
|
{
|
||||||
locale: input.lang,
|
locale: input.lang,
|
||||||
},
|
},
|
||||||
@@ -248,7 +337,7 @@ export const baseQueryRouter = router({
|
|||||||
tags: [
|
tags: [
|
||||||
generateTag(
|
generateTag(
|
||||||
input.lang,
|
input.lang,
|
||||||
responseRef.data.all_current_footer.items[0].system.uid
|
responseRef.data.all_footer.items[0].system.uid
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -297,6 +386,15 @@ export const baseQueryRouter = router({
|
|||||||
"contentstack.footer success",
|
"contentstack.footer success",
|
||||||
JSON.stringify({ query: { lang: input.lang } })
|
JSON.stringify({ query: { lang: input.lang } })
|
||||||
)
|
)
|
||||||
return validatedFooterConfig.data.all_current_footer.items[0]
|
const validatedFooterData = validatedFooterConfig.data.all_footer.items[0]
|
||||||
|
const mainLinks = transformPageConnectionLinks(
|
||||||
|
validatedFooterData.main_links
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
mainLinks: mainLinks,
|
||||||
|
appDownloads: validatedFooterData.app_downloads,
|
||||||
|
secondaryLinks: validatedFooterData.secondary_links,
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
10
server/routers/contentstack/base/utils.ts
Normal file
10
server/routers/contentstack/base/utils.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { FooterLinkItem } from "./output"
|
||||||
|
|
||||||
|
export function transformPageConnectionLinks(links: FooterLinkItem[]) {
|
||||||
|
return links.flatMap((link) =>
|
||||||
|
link.pageConnection?.edges.map((edge) => ({
|
||||||
|
title: edge.node.title,
|
||||||
|
url: edge.node.url,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
export type FooterMainNav = {
|
export type FooterMainNav = {
|
||||||
id: string
|
url: string
|
||||||
href: string
|
|
||||||
title: string
|
title: string
|
||||||
openInNewTab: boolean
|
|
||||||
isExternal: boolean
|
|
||||||
}
|
}
|
||||||
export type FooterMainNavProps = {
|
export type FooterMainNavProps = {
|
||||||
mainLinks: FooterMainNav[]
|
mainLinks: FooterMainNav[]
|
||||||
|
|||||||
Reference in New Issue
Block a user