Merged in feat/revalidate-cache-my-pages-breadcrumbs (pull request #135)
Feat/revalidate cache my pages breadcrumbs Approved-by: Michael Zetterberg
This commit is contained in:
@@ -28,7 +28,7 @@ export default async function CurrentContentPage({
|
|||||||
url: searchParams.uri,
|
url: searchParams.uri,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tags: [`${searchParams.uri}-${params.lang}`],
|
next: { tags: [`${searchParams.uri}-${params.lang}`] },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export default async function CurrentContentPage({
|
|||||||
GetCurrentBlockPageTrackingData,
|
GetCurrentBlockPageTrackingData,
|
||||||
{ uid: response.data.all_current_blocks_page.items[0].system.uid },
|
{ uid: response.data.all_current_blocks_page.items[0].system.uid },
|
||||||
{
|
{
|
||||||
tags: [`${searchParams.uri}-en`],
|
next: { tags: [`${searchParams.uri}-en`] },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { z } from "zod"
|
|||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import { internalServerError } from "@/server/errors/next"
|
import { internalServerError } from "@/server/errors/next"
|
||||||
|
import { affix as breadcrumbsAffix } from "@/server/routers/contentstack/breadcrumbs/utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -14,14 +15,16 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
|
|
||||||
const validateJsonBody = z.object({
|
const validateJsonBody = z.object({
|
||||||
api_key: z.string(),
|
|
||||||
module: z.string(),
|
|
||||||
|
|
||||||
data: z.object({
|
data: z.object({
|
||||||
content_type: z.object({
|
content_type: z.object({
|
||||||
uid: z.string(),
|
uid: z.string(),
|
||||||
}),
|
}),
|
||||||
entry: z.object({
|
entry: z.object({
|
||||||
|
breadcrumbs: z
|
||||||
|
.object({
|
||||||
|
title: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
locale: z.nativeEnum(Lang),
|
locale: z.nativeEnum(Lang),
|
||||||
uid: z.string(),
|
uid: z.string(),
|
||||||
url: z.string().optional(),
|
url: z.string().optional(),
|
||||||
@@ -75,6 +78,25 @@ export async function POST(request: NextRequest) {
|
|||||||
console.info(`Revalidating tag: ${tag}`)
|
console.info(`Revalidating tag: ${tag}`)
|
||||||
revalidateTag(tag)
|
revalidateTag(tag)
|
||||||
|
|
||||||
|
if (entry.breadcrumbs) {
|
||||||
|
const breadcrumbsRefsTag = generateRefsResponseTag(
|
||||||
|
entry.locale,
|
||||||
|
identifier,
|
||||||
|
breadcrumbsAffix
|
||||||
|
)
|
||||||
|
const breadcrumbsTag = generateTag(
|
||||||
|
entry.locale,
|
||||||
|
entry.uid,
|
||||||
|
breadcrumbsAffix
|
||||||
|
)
|
||||||
|
|
||||||
|
console.info(`Revalidating breadcrumbsRefsTag: ${breadcrumbsRefsTag}`)
|
||||||
|
revalidateTag(breadcrumbsRefsTag)
|
||||||
|
|
||||||
|
console.info(`Revalidating breadcrumbsTag: ${breadcrumbsTag}`)
|
||||||
|
revalidateTag(breadcrumbsTag)
|
||||||
|
}
|
||||||
|
|
||||||
return Response.json({ revalidated: true, now: Date.now() })
|
return Response.json({ revalidated: true, now: Date.now() })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.info("Failed to revalidate tag(s)")
|
console.info("Failed to revalidate tag(s)")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default async function Footer({ lang }: LangParams) {
|
|||||||
locale: lang,
|
locale: lang,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tags: [`footer-${lang}`],
|
next: { tags: [`footer-${lang}`] },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default async function Header({ lang, uid }: LangParams & HeaderProps) {
|
|||||||
const { data } = await request<HeaderQueryData>(
|
const { data } = await request<HeaderQueryData>(
|
||||||
GetHeader,
|
GetHeader,
|
||||||
{ locale: lang },
|
{ locale: lang },
|
||||||
{ tags: [`header-${lang}`] }
|
{ next: { tags: [`header-${lang}`] } }
|
||||||
)
|
)
|
||||||
const { data: urls } = await batchRequest<LanguageSwitcherQueryData>([
|
const { data: urls } = await batchRequest<LanguageSwitcherQueryData>([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
"use client"
|
||||||
import { cva } from "class-variance-authority"
|
import { cva } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { trpc } from "@/lib/trpc/client"
|
||||||
|
|
||||||
import styles from "./maxWidth.module.css"
|
import styles from "./maxWidth.module.css"
|
||||||
|
|
||||||
import type { MaxWidthProps } from "@/types/components/max-width"
|
import type { MaxWidthProps } from "@/types/components/max-width"
|
||||||
@@ -10,8 +14,11 @@ export default function MaxWidth({
|
|||||||
className,
|
className,
|
||||||
tag = "section",
|
tag = "section",
|
||||||
...props
|
...props
|
||||||
}: MaxWidthProps
|
}: MaxWidthProps) {
|
||||||
) {
|
const d = trpc.contentstack.breadcrumbs.get.useQuery({
|
||||||
|
locale: Lang.en,
|
||||||
|
href: "/my-pages/overview",
|
||||||
|
})
|
||||||
const Cmp = tag
|
const Cmp = tag
|
||||||
return <Cmp className={maxWidthVariants({ className })} {...props} />
|
return <Cmp className={maxWidthVariants({ className })} {...props} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export default function Breadcrumbs({ breadcrumbs }: BreadcrumbsProps) {
|
|||||||
return (
|
return (
|
||||||
<nav className={styles.breadcrumbs}>
|
<nav className={styles.breadcrumbs}>
|
||||||
<ul className={styles.list}>
|
<ul className={styles.list}>
|
||||||
<BreadcrumbsWithLink href="#">{_("Home")}</BreadcrumbsWithLink>
|
|
||||||
{breadcrumbs.map((breadcrumb) => {
|
{breadcrumbs.map((breadcrumb) => {
|
||||||
if (breadcrumb.href) {
|
if (breadcrumb.href) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
2
env/client.ts
vendored
2
env/client.ts
vendored
@@ -3,10 +3,12 @@ import { z } from "zod"
|
|||||||
|
|
||||||
export const env = createEnv({
|
export const env = createEnv({
|
||||||
client: {
|
client: {
|
||||||
|
NEXT_PUBLIC_NODE_ENV: z.enum(["development", "test", "production"]),
|
||||||
NEXT_PUBLIC_PORT: z.string().default("3000"),
|
NEXT_PUBLIC_PORT: z.string().default("3000"),
|
||||||
},
|
},
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
runtimeEnv: {
|
runtimeEnv: {
|
||||||
|
NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV,
|
||||||
NEXT_PUBLIC_PORT: process.env.NEXT_PUBLIC_PORT,
|
NEXT_PUBLIC_PORT: process.env.NEXT_PUBLIC_PORT,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
fragment MyPagesBreadcrumbs on AccountPage {
|
fragment MyPagesBreadcrumbs on AccountPage {
|
||||||
breadcrumbs {
|
breadcrumbs {
|
||||||
title
|
title
|
||||||
parents: parentsConnection {
|
parentsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
... on AccountPage {
|
... on AccountPage {
|
||||||
@@ -9,6 +9,7 @@ fragment MyPagesBreadcrumbs on AccountPage {
|
|||||||
title
|
title
|
||||||
}
|
}
|
||||||
system {
|
system {
|
||||||
|
locale
|
||||||
uid
|
uid
|
||||||
}
|
}
|
||||||
url
|
url
|
||||||
|
|||||||
20
lib/graphql/Fragments/Refs/Breadcrumbs.graphql
Normal file
20
lib/graphql/Fragments/Refs/Breadcrumbs.graphql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#import "./System.graphql"
|
||||||
|
|
||||||
|
fragment MyPagesBreadcrumbsRefs on AccountPage {
|
||||||
|
breadcrumbs {
|
||||||
|
parentsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
... on AccountPage {
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#import "../Fragments/MyPages/Breadcrumbs.graphql"
|
#import "../Fragments/MyPages/Breadcrumbs.graphql"
|
||||||
|
#import "../Fragments/Refs/Breadcrumbs.graphql"
|
||||||
|
|
||||||
query GetMyPagesBreadcrumbs($locale: String!, $url: String!) {
|
query GetMyPagesBreadcrumbs($locale: String!, $url: String!) {
|
||||||
all_account_page(locale: $locale, where: { url: $url }) {
|
all_account_page(locale: $locale, where: { url: $url }) {
|
||||||
@@ -10,3 +11,11 @@ query GetMyPagesBreadcrumbs($locale: String!, $url: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query GetMyPagesBreadcrumbsRefs($locale: String!, $url: String!) {
|
||||||
|
all_account_page(locale: $locale, where: { url: $url }) {
|
||||||
|
items {
|
||||||
|
...MyPagesBreadcrumbsRefs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ export async function batchRequest<T>(
|
|||||||
try {
|
try {
|
||||||
const response = await Promise.allSettled(
|
const response = await Promise.allSettled(
|
||||||
queries.map((query) =>
|
queries.map((query) =>
|
||||||
request<T>(query.document, query.variables, { tags: query.tags })
|
request<T>(query.document, query.variables, {
|
||||||
|
next: { tags: query.tags },
|
||||||
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { DocumentNode } from "graphql"
|
|||||||
import type { Data } from "@/types/request"
|
import type { Data } from "@/types/request"
|
||||||
|
|
||||||
const client = new GraphQLClient(env.CMS_URL, {
|
const client = new GraphQLClient(env.CMS_URL, {
|
||||||
|
cache: "force-cache",
|
||||||
fetch: cache(async function (
|
fetch: cache(async function (
|
||||||
url: URL | RequestInfo,
|
url: URL | RequestInfo,
|
||||||
params: RequestInit | undefined
|
params: RequestInit | undefined
|
||||||
@@ -21,11 +22,14 @@ const client = new GraphQLClient(env.CMS_URL, {
|
|||||||
export async function request<T>(
|
export async function request<T>(
|
||||||
query: string | DocumentNode,
|
query: string | DocumentNode,
|
||||||
variables?: {},
|
variables?: {},
|
||||||
next?: NextFetchRequestConfig
|
options?: Pick<RequestInit, "cache" | "next">
|
||||||
): Promise<Data<T>> {
|
): Promise<Data<T>> {
|
||||||
try {
|
try {
|
||||||
if (next) {
|
if (options?.cache) {
|
||||||
client.requestConfig.next = next
|
client.requestConfig.cache = options.cache
|
||||||
|
}
|
||||||
|
if (options?.next) {
|
||||||
|
client.requestConfig.next = options.next
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.PRINT_QUERY) {
|
if (env.PRINT_QUERY) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||||
import { httpBatchLink } from "@trpc/client"
|
import { httpBatchLink, loggerLink } from "@trpc/client"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
import { env } from "@/env/client"
|
import { env } from "@/env/client"
|
||||||
@@ -14,7 +14,14 @@ function initializeTrpcClient() {
|
|||||||
// that trpc and next are running on the same port.
|
// that trpc and next are running on the same port.
|
||||||
return trpc.createClient({
|
return trpc.createClient({
|
||||||
links: [
|
links: [
|
||||||
|
loggerLink({
|
||||||
|
enabled: (opts) =>
|
||||||
|
(env.NEXT_PUBLIC_NODE_ENV === "development" &&
|
||||||
|
typeof window !== "undefined") ||
|
||||||
|
(opts.direction === "down" && opts.result instanceof Error),
|
||||||
|
}),
|
||||||
httpBatchLink({
|
httpBatchLink({
|
||||||
|
fetch,
|
||||||
transformer,
|
transformer,
|
||||||
/**
|
/**
|
||||||
* This is locally in Next.js
|
* This is locally in Next.js
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
overview,
|
overview,
|
||||||
profile,
|
profile,
|
||||||
profileEdit,
|
profileEdit,
|
||||||
|
stays,
|
||||||
} from "./constants/routes/myPages.js"
|
} from "./constants/routes/myPages.js"
|
||||||
|
|
||||||
const jiti = createJiti(new URL(import.meta.url).pathname)
|
const jiti = createJiti(new URL(import.meta.url).pathname)
|
||||||
@@ -124,6 +125,12 @@ const nextConfig = {
|
|||||||
{ source: profileEdit.fi, destination: "/fi/my-pages/profile/edit" },
|
{ source: profileEdit.fi, destination: "/fi/my-pages/profile/edit" },
|
||||||
{ source: profileEdit.no, destination: "/no/my-pages/profile/edit" },
|
{ source: profileEdit.no, destination: "/no/my-pages/profile/edit" },
|
||||||
{ source: profileEdit.sv, destination: "/sv/my-pages/profile/edit" },
|
{ source: profileEdit.sv, destination: "/sv/my-pages/profile/edit" },
|
||||||
|
|
||||||
|
{ source: stays.da, destination: "/da/my-pages/stays" },
|
||||||
|
{ source: stays.de, destination: "/de/my-pages/stays" },
|
||||||
|
{ source: stays.fi, destination: "/fi/my-pages/stays" },
|
||||||
|
{ source: stays.no, destination: "/no/my-pages/stays" },
|
||||||
|
{ source: stays.sv, destination: "/sv/my-pages/stays" },
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TRPCError } from "@trpc/server"
|
import { TRPCError } from "@trpc/server"
|
||||||
import {
|
import {
|
||||||
TRPC_ERROR_CODES_BY_NUMBER,
|
|
||||||
TRPC_ERROR_CODES_BY_KEY,
|
TRPC_ERROR_CODES_BY_KEY,
|
||||||
|
TRPC_ERROR_CODES_BY_NUMBER,
|
||||||
} from "@trpc/server/rpc"
|
} from "@trpc/server/rpc"
|
||||||
|
|
||||||
export function unauthorizedError() {
|
export function unauthorizedError() {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages";
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
const langs = Object.keys(Lang) as [keyof typeof Lang]
|
|
||||||
|
|
||||||
export const getBreadcrumbsInput = z.object({
|
export const getBreadcrumbsInput = z.object({
|
||||||
href: z.string().min(1, { message: "href is required" }),
|
href: z.string().min(1, { message: "href is required" }),
|
||||||
locale: z.enum(langs),
|
locale: z.nativeEnum(Lang),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,40 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
|
export const validateBreadcrumbsRefsConstenstackSchema = z.object({
|
||||||
|
all_account_page: z.object({
|
||||||
|
items: z.array(
|
||||||
|
z.object({
|
||||||
|
breadcrumbs: z.object({
|
||||||
|
parentsConnection: z.object({
|
||||||
|
edges: z.array(
|
||||||
|
z.object({
|
||||||
|
node: z.object({
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
export const validateBreadcrumbsConstenstackSchema = z.object({
|
export const validateBreadcrumbsConstenstackSchema = z.object({
|
||||||
all_account_page: z.object({
|
all_account_page: z.object({
|
||||||
items: z.array(
|
items: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
breadcrumbs: z.object({
|
breadcrumbs: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
parents: z.object({
|
parentsConnection: z.object({
|
||||||
edges: z.array(
|
edges: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
node: z.object({
|
node: z.object({
|
||||||
@@ -14,6 +42,7 @@ export const validateBreadcrumbsConstenstackSchema = z.object({
|
|||||||
title: z.string(),
|
title: z.string(),
|
||||||
}),
|
}),
|
||||||
system: z.object({
|
system: z.object({
|
||||||
|
locale: z.nativeEnum(Lang),
|
||||||
uid: z.string(),
|
uid: z.string(),
|
||||||
}),
|
}),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
|
|||||||
@@ -1,51 +1,120 @@
|
|||||||
import { GetMyPagesBreadcrumbs } from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql"
|
import {
|
||||||
|
GetMyPagesBreadcrumbs,
|
||||||
|
GetMyPagesBreadcrumbsRefs,
|
||||||
|
} from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { getBreadcrumbsInput } from "./input"
|
import {
|
||||||
import { validateBreadcrumbsConstenstackSchema } from "./output"
|
generateRefsResponseTag,
|
||||||
|
generateTag,
|
||||||
|
generateTags,
|
||||||
|
} from "@/utils/generateTag"
|
||||||
|
import { removeMultipleSlashes } from "@/utils/url"
|
||||||
|
|
||||||
import { GetMyPagesBreadcrumbsData } from "@/types/requests/myPages/breadcrumbs"
|
import { getBreadcrumbsInput } from "./input"
|
||||||
|
import {
|
||||||
|
getBreadcrumbsSchema,
|
||||||
|
validateBreadcrumbsConstenstackSchema,
|
||||||
|
validateBreadcrumbsRefsConstenstackSchema,
|
||||||
|
} from "./output"
|
||||||
|
import { affix, getConnections, homeBreadcrumbs } from "./utils"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
GetMyPagesBreadcrumbsData,
|
||||||
|
GetMyPagesBreadcrumbsRefsData,
|
||||||
|
} from "@/types/requests/myPages/breadcrumbs"
|
||||||
|
|
||||||
export const breadcrumbsQueryRouter = router({
|
export const breadcrumbsQueryRouter = router({
|
||||||
get: publicProcedure.input(getBreadcrumbsInput).query(async ({ input }) => {
|
get: publicProcedure.input(getBreadcrumbsInput).query(async ({ input }) => {
|
||||||
try {
|
try {
|
||||||
const response = await request<GetMyPagesBreadcrumbsData>(
|
const refsResponse = await request<GetMyPagesBreadcrumbsRefsData>(
|
||||||
GetMyPagesBreadcrumbs,
|
GetMyPagesBreadcrumbsRefs,
|
||||||
{ locale: input.locale, url: input.href }
|
{ locale: input.locale, url: input.href },
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
tags: [generateRefsResponseTag(input.locale, input.href, affix)],
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if (!response.data) {
|
|
||||||
|
if (!refsResponse.data) {
|
||||||
|
console.error("Bad response for `GetMyPagesBreadcrumbsRefs`")
|
||||||
|
console.error({ refsResponse })
|
||||||
|
throw internalServerError()
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedRefsData =
|
||||||
|
validateBreadcrumbsRefsConstenstackSchema.safeParse(refsResponse.data)
|
||||||
|
if (!validatedRefsData.success) {
|
||||||
|
console.info("Bad validation for `GetMyPagesBreadcrumbsRefs`")
|
||||||
|
console.error(validatedRefsData.error)
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedBreadcrumbs =
|
const connections = getConnections(validatedRefsData.data)
|
||||||
|
const tags = generateTags(input.locale, connections)
|
||||||
|
const page = validatedRefsData.data.all_account_page.items[0]
|
||||||
|
tags.push(generateTag(input.locale, page.system.uid, affix))
|
||||||
|
|
||||||
|
const response = await request<GetMyPagesBreadcrumbsData>(
|
||||||
|
GetMyPagesBreadcrumbs,
|
||||||
|
{ locale: input.locale, url: input.href },
|
||||||
|
{ next: { tags } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
console.error("Bad response for `GetMyPagesBreadcrumbs`")
|
||||||
|
console.error({ input })
|
||||||
|
console.error({ response })
|
||||||
|
throw internalServerError()
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedBreadcrumbsData =
|
||||||
validateBreadcrumbsConstenstackSchema.safeParse(response.data)
|
validateBreadcrumbsConstenstackSchema.safeParse(response.data)
|
||||||
if (!validatedBreadcrumbs.success) {
|
if (!validatedBreadcrumbsData.success) {
|
||||||
|
console.error("Bad validation for `GetMyPagesBreadcrumbs`")
|
||||||
|
console.error(validatedBreadcrumbsData.error)
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentBreadcrumbs =
|
const parentBreadcrumbs =
|
||||||
validatedBreadcrumbs.data.all_account_page.items[0].breadcrumbs.parents.edges.map(
|
validatedBreadcrumbsData.data.all_account_page.items[0].breadcrumbs.parentsConnection.edges.map(
|
||||||
(breadcrumb) => {
|
(breadcrumb) => {
|
||||||
return {
|
return {
|
||||||
href: breadcrumb.node.url,
|
href: removeMultipleSlashes(
|
||||||
|
`/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
|
||||||
|
),
|
||||||
title: breadcrumb.node.breadcrumbs.title,
|
title: breadcrumb.node.breadcrumbs.title,
|
||||||
uid: breadcrumb.node.system.uid,
|
uid: breadcrumb.node.system.uid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const pageBreadcrumb =
|
|
||||||
validatedBreadcrumbs.data.all_account_page.items.map((breadcrumb) => {
|
|
||||||
return {
|
|
||||||
href: "",
|
|
||||||
title: breadcrumb.breadcrumbs.title,
|
|
||||||
uid: breadcrumb.system.uid,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const breadcrumbs = [parentBreadcrumbs, pageBreadcrumb].flat()
|
|
||||||
|
|
||||||
return breadcrumbs
|
const pageBreadcrumb =
|
||||||
|
validatedBreadcrumbsData.data.all_account_page.items.map(
|
||||||
|
(breadcrumb) => {
|
||||||
|
return {
|
||||||
|
title: breadcrumb.breadcrumbs.title,
|
||||||
|
uid: breadcrumb.system.uid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const breadcrumbs = [
|
||||||
|
homeBreadcrumbs[input.locale],
|
||||||
|
parentBreadcrumbs,
|
||||||
|
pageBreadcrumb,
|
||||||
|
].flat()
|
||||||
|
const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
|
||||||
|
if (!validatedBreadcrumbs.success) {
|
||||||
|
console.info("Bad validation for `validatedBreadcrumbs`")
|
||||||
|
console.error(validatedBreadcrumbs.error)
|
||||||
|
throw badRequestError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return validatedBreadcrumbs.data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.info(`Get My Pages Breadcrumbs Error`)
|
console.info(`Get My Pages Breadcrumbs Error`)
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|||||||
50
server/routers/contentstack/breadcrumbs/utils.ts
Normal file
50
server/routers/contentstack/breadcrumbs/utils.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
import type { GetMyPagesBreadcrumbsRefsData } from "@/types/requests/myPages/breadcrumbs"
|
||||||
|
import type { Edges } from "@/types/requests/utils/edges"
|
||||||
|
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||||
|
|
||||||
|
export function getConnections(refs: GetMyPagesBreadcrumbsRefsData) {
|
||||||
|
const connections: Edges<NodeRefs>[] = []
|
||||||
|
refs.all_account_page.items.forEach((ref) => {
|
||||||
|
connections.push(ref.breadcrumbs.parentsConnection)
|
||||||
|
})
|
||||||
|
|
||||||
|
return connections
|
||||||
|
}
|
||||||
|
|
||||||
|
export const affix = "breadcrumbs"
|
||||||
|
|
||||||
|
// TODO: Make these editable in CMS?
|
||||||
|
export const homeBreadcrumbs = {
|
||||||
|
[Lang.da]: {
|
||||||
|
href: "/da",
|
||||||
|
title: "Hjem",
|
||||||
|
uid: "da",
|
||||||
|
},
|
||||||
|
[Lang.de]: {
|
||||||
|
href: "/de",
|
||||||
|
title: "Heim",
|
||||||
|
uid: "de",
|
||||||
|
},
|
||||||
|
[Lang.en]: {
|
||||||
|
href: "/en",
|
||||||
|
title: "Home",
|
||||||
|
uid: "en",
|
||||||
|
},
|
||||||
|
[Lang.fi]: {
|
||||||
|
href: "/fi",
|
||||||
|
title: "Koti",
|
||||||
|
uid: "fi",
|
||||||
|
},
|
||||||
|
[Lang.no]: {
|
||||||
|
href: "/no",
|
||||||
|
title: "Hjem",
|
||||||
|
uid: "no",
|
||||||
|
},
|
||||||
|
[Lang.sv]: {
|
||||||
|
href: "/sv",
|
||||||
|
title: "Hem",
|
||||||
|
uid: "sv",
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
generateTag,
|
generateTag,
|
||||||
generateTags,
|
generateTags,
|
||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
|
import { removeMultipleSlashes } from "@/utils/url"
|
||||||
|
|
||||||
import { getNavigationInputSchema } from "./input"
|
import { getNavigationInputSchema } from "./input"
|
||||||
import {
|
import {
|
||||||
@@ -35,7 +36,7 @@ export function mapMenuItems(navigationItems: NavigationItem[]) {
|
|||||||
lang: node.system.locale,
|
lang: node.system.locale,
|
||||||
linkText: item.link_text || node.title,
|
linkText: item.link_text || node.title,
|
||||||
uid: node.system.uid,
|
uid: node.system.uid,
|
||||||
url: `/${node.system.locale}/${node.url}`.replaceAll(/\/\/+/g, "/"),
|
url: removeMultipleSlashes(`/${node.system.locale}/${node.url}`),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("sub_items" in item) {
|
if ("sub_items" in item) {
|
||||||
@@ -54,12 +55,16 @@ export const navigationQueryRouter = router({
|
|||||||
const refsResponse = await request<GetNavigationMyPagesRefsData>(
|
const refsResponse = await request<GetNavigationMyPagesRefsData>(
|
||||||
GetNavigationMyPagesRefs,
|
GetNavigationMyPagesRefs,
|
||||||
{ locale: lang },
|
{ locale: lang },
|
||||||
{ tags: [generateRefsResponseTag(lang, "navigation_my_pages")] }
|
{
|
||||||
|
next: {
|
||||||
|
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
console.error("Bad response for `GetNavigationMyPagesRefs`")
|
console.error("Bad response for `GetNavigationMyPagesRefs`")
|
||||||
console.error(refsResponse)
|
console.error({ refsResponse })
|
||||||
throw internalServerError()
|
throw internalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +85,13 @@ export const navigationQueryRouter = router({
|
|||||||
const response = await request<GetNavigationMyPagesData>(
|
const response = await request<GetNavigationMyPagesData>(
|
||||||
GetNavigationMyPages,
|
GetNavigationMyPages,
|
||||||
{ locale: lang },
|
{ locale: lang },
|
||||||
{ tags }
|
{ next: { tags } }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
console.error("Bad response for `GetNavigationMyPages`")
|
console.error("Bad response for `GetNavigationMyPages`")
|
||||||
console.error(response)
|
console.error({ input: lang })
|
||||||
|
console.error({ response })
|
||||||
throw internalServerError()
|
throw internalServerError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export const userQueryRouter = router({
|
|||||||
get: protectedProcedure.query(async function ({ ctx }) {
|
get: protectedProcedure.query(async function ({ ctx }) {
|
||||||
try {
|
try {
|
||||||
const apiResponse = await api.get(api.endpoints.v0.profile, {
|
const apiResponse = await api.get(api.endpoints.v0.profile, {
|
||||||
cache: "no-store",
|
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
import type { AllRequestResponse } from "../utils/all"
|
import type { AllRequestResponse } from "../utils/all"
|
||||||
import type { EdgesWithTotalCount } from "../utils/edges"
|
import type { Edges } from "../utils/edges"
|
||||||
|
import type { System } from "../system"
|
||||||
|
|
||||||
interface AccountPageBreadcrumbs {
|
interface AccountPageBreadcrumbs {
|
||||||
breadcrumbs: {
|
breadcrumbs: {
|
||||||
title: string
|
title: string
|
||||||
parents: EdgesWithTotalCount<{
|
parentsConnection: Edges<{
|
||||||
breadcrumbs: {
|
breadcrumbs: {
|
||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
system: {
|
system: {
|
||||||
|
locale: Lang
|
||||||
uid: string
|
uid: string
|
||||||
}
|
}
|
||||||
url: string
|
url: string
|
||||||
@@ -25,3 +28,16 @@ interface AllAccountPageResponse
|
|||||||
export interface GetMyPagesBreadcrumbsData {
|
export interface GetMyPagesBreadcrumbsData {
|
||||||
all_account_page: AllAccountPageResponse
|
all_account_page: AllAccountPageResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AccountPageBreadcrumbRefs extends System {
|
||||||
|
breadcrumbs: {
|
||||||
|
parentsConnection: Edges<System>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AllAccountPageRefsResponse
|
||||||
|
extends AllRequestResponse<AccountPageBreadcrumbRefs> { }
|
||||||
|
|
||||||
|
export interface GetMyPagesBreadcrumbsRefsData {
|
||||||
|
all_account_page: AllAccountPageRefsResponse
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
import type { Edges } from "@/types/requests/utils/edges"
|
import type { Edges } from "@/types/requests/utils/edges"
|
||||||
import type { Lang } from "@/constants/languages"
|
|
||||||
import type { NodeRefs } from "@/types/requests/utils/refs"
|
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||||
|
import type { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to generate tag for initial refs request
|
* Function to generate tag for initial refs request
|
||||||
*
|
*
|
||||||
* @param lang
|
* @param lang Lang
|
||||||
* @param identifier Should be uri for all pages and content_type_uid for
|
* @param identifier Should be uri for all pages and content_type_uid for
|
||||||
* everything else
|
* everything else
|
||||||
|
* @param affix possible extra value to add to string, e.g lang:identifier:breadcrumbs:refs
|
||||||
|
* as it is the same entity as the actual page tag otherwise
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export function generateRefsResponseTag(lang: Lang, identifier: string) {
|
export function generateRefsResponseTag(
|
||||||
|
lang: Lang,
|
||||||
|
identifier: string,
|
||||||
|
affix?: string
|
||||||
|
) {
|
||||||
|
if (affix) {
|
||||||
|
return `${lang}:${identifier}:${affix}:refs`
|
||||||
|
}
|
||||||
return `${lang}:${identifier}:refs`
|
return `${lang}:${identifier}:refs`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +44,15 @@ export function generateRefTag(
|
|||||||
*
|
*
|
||||||
* @param lang Lang
|
* @param lang Lang
|
||||||
* @param uid system.uid of entity
|
* @param uid system.uid of entity
|
||||||
|
* @param affix possible extra value to add to string, e.g lang:uid:breadcrumbs
|
||||||
|
* as it is the same entity as the actual page tag otherwise
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export function generateTag(lang: Lang, uid: string) {
|
export function generateTag(lang: Lang, uid: string, affix?: string) {
|
||||||
|
if (affix) {
|
||||||
|
return `${lang}:${uid}:${affix}`
|
||||||
|
}
|
||||||
|
|
||||||
return `${lang}:${uid}`
|
return `${lang}:${uid}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
utils/url.ts
Normal file
3
utils/url.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function removeMultipleSlashes(str: string) {
|
||||||
|
return str.replaceAll(/\/\/+/g, "/")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user