diff --git a/apps/cms/src/app/(frontend)/[locale]/preview/global-block/[id]/page.tsx b/apps/cms/src/app/(frontend)/[locale]/preview/global-block/[id]/page.tsx new file mode 100644 index 000000000..3889593dc --- /dev/null +++ b/apps/cms/src/app/(frontend)/[locale]/preview/global-block/[id]/page.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; + +import { GlobalBlockRenderer } from "@/blocks/GlobalSectionSlot/GlobalBlockRenderer"; +import type { Locale } from "@/lib/types"; + +export const metadata: Metadata = { + robots: { follow: false, index: false }, + title: "Global Block Preview", +}; + +interface Args { + params: Promise<{ + id: string; + locale: Locale; + }>; +} + +export default async function GlobalBlockPreviewPage({ params }: Args) { + const { id, locale } = await params; + const referenceId = Number(id); + + if (!Number.isInteger(referenceId)) { + notFound(); + } + + return ( +
+ +
+ ); +} diff --git a/apps/cms/src/blocks/GlobalSectionSlot/Component.tsx b/apps/cms/src/blocks/GlobalSectionSlot/Component.tsx index 265e1b869..94ef91d77 100644 --- a/apps/cms/src/blocks/GlobalSectionSlot/Component.tsx +++ b/apps/cms/src/blocks/GlobalSectionSlot/Component.tsx @@ -1,17 +1,10 @@ -import { draftMode } from "next/headers"; - -import { renderContentBlock } from "@/blocks/contentBlockComponents"; -import { filterHiddenBlocks } from "@/lib/fields/section/filterHiddenBlocks"; import type { GlobalSectionSlotBlock } from "@/payload-types"; -export const GlobalSectionSlotBlockComponent = async ({ reference }: GlobalSectionSlotBlock) => { - if (!reference || typeof reference === "number") return null; - - const { isEnabled: draft } = await draftMode(); - if (!draft && reference._status !== "published") return null; +import { GlobalBlockRenderer } from "./GlobalBlockRenderer"; - const [inner] = filterHiddenBlocks(reference.block); - if (!inner) return null; +export const GlobalSectionSlotBlockComponent = ({ reference }: GlobalSectionSlotBlock) => { + const referenceId = typeof reference === "object" && reference ? reference.id : reference; + if (referenceId == null) return null; - return renderContentBlock(inner, inner.id ?? "global-section-inner"); + return ; }; diff --git a/apps/cms/src/blocks/GlobalSectionSlot/GlobalBlockRenderer.tsx b/apps/cms/src/blocks/GlobalSectionSlot/GlobalBlockRenderer.tsx new file mode 100644 index 000000000..8673985ea --- /dev/null +++ b/apps/cms/src/blocks/GlobalSectionSlot/GlobalBlockRenderer.tsx @@ -0,0 +1,39 @@ +import { draftMode } from "next/headers"; + +import { renderContentBlock } from "@/blocks/contentBlockComponents"; +import { isPublishedGlobalBlock } from "@/collections/GlobalBlock/slots"; +import { getPayloadClient, PAGE_RENDER_DEPTH } from "@/dal"; +import { filterHiddenBlocks } from "@/lib/fields/section/filterHiddenBlocks"; +import type { Locale } from "@/lib/types"; +import { resolveLocale } from "@/lib/utils/resolveLocale"; + +// Re-fetched because the page query spends one depth level on the slot, and inner blocks need the full depth. +export const GlobalBlockRenderer = async ({ + locale, + referenceId, +}: { + locale?: Locale; + referenceId: number; +}) => { + const { isEnabled: draft } = await draftMode(); + const resolvedLocale = await resolveLocale(locale); + const payload = await getPayloadClient(); + + const globalBlock = await payload.findByID({ + collection: "globalBlock", + depth: PAGE_RENDER_DEPTH, + disableErrors: true, + draft, + id: referenceId, + locale: resolvedLocale, + overrideAccess: true, + }); + + if (!globalBlock) return null; + if (!draft && !isPublishedGlobalBlock(globalBlock)) return null; + + const [inner] = filterHiddenBlocks(globalBlock.block); + if (!inner) return null; + + return renderContentBlock(inner, inner.id ?? "global-section-inner"); +}; diff --git a/apps/cms/src/blocks/RawHtml/extractText.ts b/apps/cms/src/blocks/RawHtml/extractText.ts new file mode 100644 index 000000000..ecd188bb5 --- /dev/null +++ b/apps/cms/src/blocks/RawHtml/extractText.ts @@ -0,0 +1,35 @@ +import type { RawHtmlBlock } from "@/payload-types"; + +const TAG_BODY = String.raw`(?:"[^"]*"|'[^']*'|[^>"'])*`; +const SCRIPT_OR_STYLE = new RegExp( + String.raw`<(script|style)\b${TAG_BODY}>[\s\S]*?<\/\1\s*>`, + "giu" +); +const HTML_COMMENT = //gu; +const HTML_TAG = new RegExp(`<[a-zA-Z/!?]${TAG_BODY}>`, "gu"); +const NAMED_ENTITIES: Record = { + "&": "&", + ">": ">", + "<": "<", + " ": " ", + """: '"', + "'": "'", + "'": "'", +}; + +function decodeEntities(input: string): string { + return input.replace( + /&(?:amp|gt|lt|nbsp|quot|apos|#39);/gu, + (match) => NAMED_ENTITIES[match] ?? match + ); +} + +export function stripHtml(html: string): string { + const withoutCode = html.replace(SCRIPT_OR_STYLE, " ").replace(HTML_COMMENT, " "); + const withoutTags = withoutCode.replace(HTML_TAG, " "); + return decodeEntities(withoutTags).replace(/\s+/gu, " ").trim(); +} + +export function extractRawHtmlText(block: RawHtmlBlock): string { + return stripHtml(block.html ?? ""); +} diff --git a/apps/cms/src/collections/GlobalBlock/config.ts b/apps/cms/src/collections/GlobalBlock/config.ts index 1e2e2ad18..bcbd40559 100644 --- a/apps/cms/src/collections/GlobalBlock/config.ts +++ b/apps/cms/src/collections/GlobalBlock/config.ts @@ -3,8 +3,21 @@ import type { CollectionConfig } from "payload"; import { contentBlocks } from "@/blocks/contentBlocks"; import { anyone, author, or, superAdmin, user } from "@/lib/access"; import { createLocalizedDefault } from "@/lib/utils/createLocalizedDefault"; +import { generatePreviewPath } from "@/lib/utils/generatePreviewPath"; +import { shouldIncludeLocalePrefix } from "@/lib/utils/localePrefix"; import { preventDeleteIfReferenced } from "./hooks/preventDeleteIfReferenced"; +import { revalidatePagesUsingGlobalBlock } from "./hooks/revalidatePagesUsingGlobalBlock"; + +const globalBlockPreviewPath = (id: unknown, locale: string): string => { + const prefix = shouldIncludeLocalePrefix(locale) ? `/${locale}` : ""; + + return generatePreviewPath({ + collection: "globalBlock", + path: `${prefix}/preview/global-block/${id}`, + slug: String(id ?? ""), + }); +}; export const GlobalBlock: CollectionConfig<"globalBlock"> = { access: { @@ -16,6 +29,11 @@ export const GlobalBlock: CollectionConfig<"globalBlock"> = { admin: { defaultColumns: ["title", "block", "updatedAt"], group: "Global Components", + livePreview: { + url: ({ data, locale }) => + globalBlockPreviewPath(data?.id, locale.code ?? locale.fallbackLocale), + }, + preview: (data, { locale }) => globalBlockPreviewPath(data?.id, locale), useAsTitle: "title", }, dbName: "gsec", @@ -54,6 +72,7 @@ export const GlobalBlock: CollectionConfig<"globalBlock"> = { }, ], hooks: { + afterChange: [revalidatePagesUsingGlobalBlock], beforeDelete: [preventDeleteIfReferenced], }, labels: { diff --git a/apps/cms/src/collections/GlobalBlock/findPagesUsingGlobalBlock.ts b/apps/cms/src/collections/GlobalBlock/findPagesUsingGlobalBlock.ts new file mode 100644 index 000000000..bbc084481 --- /dev/null +++ b/apps/cms/src/collections/GlobalBlock/findPagesUsingGlobalBlock.ts @@ -0,0 +1,44 @@ +import type { PayloadRequest } from "payload"; + +import { I18N_CONFIG } from "@/lib/config/i18n"; + +export interface PageUsingGlobalBlock { + id: number; + title: string; +} + +export async function findPagesUsingGlobalBlock({ + id, + includeDrafts = false, + req, +}: { + id: number | string; + includeDrafts?: boolean; + req: PayloadRequest; +}): Promise { + const pages = new Map(); + + for (const { code: locale } of I18N_CONFIG.locales) { + for (const draft of includeDrafts ? [false, true] : [false]) { + const { docs } = await req.payload.find({ + collection: "page", + depth: 0, + draft, + locale, + overrideAccess: true, + pagination: false, + req, + select: { title: true }, + where: { "blocks.reference": { equals: id } }, + }); + + for (const doc of docs) { + if (!pages.has(doc.id)) { + pages.set(doc.id, { id: doc.id, title: doc.title }); + } + } + } + } + + return [...pages.values()]; +} diff --git a/apps/cms/src/collections/GlobalBlock/hooks/preventDeleteIfReferenced.ts b/apps/cms/src/collections/GlobalBlock/hooks/preventDeleteIfReferenced.ts index a3ba5dfc4..c60181117 100644 --- a/apps/cms/src/collections/GlobalBlock/hooks/preventDeleteIfReferenced.ts +++ b/apps/cms/src/collections/GlobalBlock/hooks/preventDeleteIfReferenced.ts @@ -1,26 +1,16 @@ import { APIError } from "payload"; import type { CollectionBeforeDeleteHook } from "payload"; +import { findPagesUsingGlobalBlock } from "../findPagesUsingGlobalBlock"; + export const preventDeleteIfReferenced: CollectionBeforeDeleteHook = async ({ id, req }) => { - const pages = await req.payload.find({ - collection: "page", - depth: 0, - limit: 100, - overrideAccess: true, - pagination: false, - req, - where: { - "blocks.reference": { equals: id }, - }, - }); + const pages = await findPagesUsingGlobalBlock({ id, includeDrafts: true, req }); - if (pages.totalDocs > 0) { - const titles = pages.docs - .map((p) => (typeof p.title === "string" ? p.title : `#${p.id}`)) - .join(", "); + if (pages.length > 0) { + const titles = pages.map((p) => p.title || `#${p.id}`).join(", "); throw new APIError( - `Cannot delete this global block: it is still used by ${pages.totalDocs} page(s): ${titles}. Remove those references first.`, + `Cannot delete this global block: it is still used by ${pages.length} page(s): ${titles}. Remove those references first.`, 400, undefined, true diff --git a/apps/cms/src/collections/GlobalBlock/hooks/revalidatePagesUsingGlobalBlock.ts b/apps/cms/src/collections/GlobalBlock/hooks/revalidatePagesUsingGlobalBlock.ts new file mode 100644 index 000000000..dc3c9a23c --- /dev/null +++ b/apps/cms/src/collections/GlobalBlock/hooks/revalidatePagesUsingGlobalBlock.ts @@ -0,0 +1,68 @@ +import type { CollectionAfterChangeHook, PayloadRequest } from "payload"; + +import { I18N_CONFIG } from "@/lib/config/i18n"; +import { revalidatePageCache } from "@/lib/utils/revalidatePageCache"; +import type { GlobalBlock } from "@/payload-types"; + +import { findPagesUsingGlobalBlock } from "../findPagesUsingGlobalBlock"; +import { isPublishedGlobalBlock } from "../slots"; + +async function changesPublishedContent({ + doc, + previousDoc, + req, +}: { + doc: GlobalBlock; + previousDoc?: GlobalBlock; + req: PayloadRequest; +}): Promise { + if (isPublishedGlobalBlock(doc)) return true; + if (!isPublishedGlobalBlock(previousDoc)) return false; + + const live = await req.payload.findByID({ + collection: "globalBlock", + depth: 0, + id: doc.id, + overrideAccess: true, + req, + select: { _status: true }, + }); + + return !isPublishedGlobalBlock(live); +} + +export const revalidatePagesUsingGlobalBlock: CollectionAfterChangeHook = async ({ + doc, + previousDoc, + req, +}) => { + const { context, payload } = req; + + if (context.disableRevalidate || !(await changesPublishedContent({ doc, previousDoc, req }))) { + return doc; + } + + const pageIds = (await findPagesUsingGlobalBlock({ id: doc.id, req })).map((p) => p.id); + if (pageIds.length === 0) { + return doc; + } + + for (const { code: locale } of I18N_CONFIG.locales) { + const { docs: publishedPages } = await payload.find({ + collection: "page", + depth: 0, + locale, + overrideAccess: true, + pagination: false, + req, + select: { breadcrumbs: true, slug: true }, + where: { _status: { equals: "published" }, id: { in: pageIds } }, + }); + + for (const page of publishedPages) { + revalidatePageCache({ doc: page, locale, payload }); + } + } + + return doc; +}; diff --git a/apps/cms/src/collections/GlobalBlock/slots.ts b/apps/cms/src/collections/GlobalBlock/slots.ts new file mode 100644 index 000000000..496ded718 --- /dev/null +++ b/apps/cms/src/collections/GlobalBlock/slots.ts @@ -0,0 +1,21 @@ +import { relationId } from "@/lib/contentExtraction"; +import type { GlobalBlock } from "@/payload-types"; + +interface BlockLike { + blockType: string; + reference?: unknown; +} + +export function collectGlobalBlockIds(blocks: readonly BlockLike[]): (string | number)[] { + const ids = new Set(); + + for (const block of blocks) { + const id = block.blockType === "globalSectionSlot" ? relationId(block.reference) : null; + if (id != null) ids.add(id); + } + + return [...ids]; +} + +export const isPublishedGlobalBlock = (doc: Pick | null | undefined) => + doc?._status === "published"; diff --git a/apps/cms/src/collections/Page/extractPageContent.ts b/apps/cms/src/collections/Page/extractPageContent.ts index 8a3a879f4..9d8562017 100644 --- a/apps/cms/src/collections/Page/extractPageContent.ts +++ b/apps/cms/src/collections/Page/extractPageContent.ts @@ -6,6 +6,7 @@ import type { DocStore, } from "@focus-reactive/payload-plugin-seo/content"; +import { collectGlobalBlockIds, isPublishedGlobalBlock } from "@/collections/GlobalBlock/slots"; import { I18N_CONFIG } from "@/lib/config/i18n"; import { actionLinks, @@ -21,7 +22,6 @@ import type { ImageGroup, LinkResolveCtx, LinkValue, Upload } from "@/lib/conten import type { GlobalBlock, Page } from "@/payload-types"; type Block = Page["blocks"][number]; -type GlobalBlockContent = NonNullable[number]; export function extractPageBlockContent( block: Block, @@ -153,9 +153,9 @@ export function extractPageBlockContent( const gid = relationId(b.reference); const resolved = gid != null - ? (docs.get("globalBlock", gid) as { block?: GlobalBlockContent[] } | undefined) + ? (docs.get("globalBlock", gid) as Pick | undefined) : undefined; - const inner = resolved?.block?.[0]; + const inner = isPublishedGlobalBlock(resolved) ? resolved?.block?.[0] : undefined; return inner ? extractPageBlockContent(inner as unknown as Block, ctx, docs, helpers) : []; } @@ -164,19 +164,6 @@ export function extractPageBlockContent( } } -function collectGlobalBlockIds(blocks: Block[]): (string | number)[] { - const ids = new Set(); - - for (const block of blocks) { - if (block.blockType === "globalSectionSlot") { - const id = relationId((block as { reference?: unknown }).reference); - if (id != null) ids.add(id); - } - } - - return [...ids]; -} - function mergeStores(...stores: (DocStore | null)[]): DocStore { return { get: (collection, id) => { @@ -199,16 +186,15 @@ const extractPageContent: ContentExtractor = async (values, ctx, { resolveDocs, phase1Queries.push({ collection: "globalBlock", ids: globalIds, - select: ["block"], + select: ["block", "_status"], depth: 0, }); } const docs1: DocStore = await resolveDocs(phase1Queries); - const globalDocs = globalIds.map((id) => docs1.get("globalBlock", id)).filter(Boolean) as Record< - string, - unknown - >[]; + const globalDocs = globalIds + .map((id) => docs1.get("globalBlock", id) as Pick | undefined) + .filter(isPublishedGlobalBlock) as Record[]; const phase2Queries: DocQuery[] = globalDocs.flatMap((doc) => buildRefQueries(doc)); const docs2: DocStore | null = phase2Queries.length > 0 ? await resolveDocs(phase2Queries) : null; diff --git a/apps/cms/src/collections/Page/extractPageText.ts b/apps/cms/src/collections/Page/extractPageText.ts index 37448ee25..33b7024c2 100644 --- a/apps/cms/src/collections/Page/extractPageText.ts +++ b/apps/cms/src/collections/Page/extractPageText.ts @@ -3,6 +3,7 @@ import { extractCarouselText } from "@/blocks/Carousel/extractText"; import { extractChartText } from "@/blocks/Chart/extractText"; import { extractCtaBandText } from "@/blocks/CtaBand/extractText"; import { extractNewsletterText } from "@/blocks/Newsletter/extractText"; +import { extractRawHtmlText } from "@/blocks/RawHtml/extractText"; import { extractStatsText } from "@/blocks/Stats/extractText"; import { extractFaqText } from "@/blocks/Faq/extractText"; import { extractHeroText } from "@/blocks/Hero/extractText"; @@ -51,6 +52,13 @@ export function extractPageBlockText(block: Page["blocks"][number]): string { case "stats": { return extractStatsText(block); } + case "rawHtml": { + return extractRawHtmlText(block); + } + case "globalSectionSlot": { + // Shared text stays out of page vectors, so one global block edit does not re-embed every page using it. + return ""; + } default: { return ""; } diff --git a/apps/cms/src/collections/Page/hooks/indexEmbedding.ts b/apps/cms/src/collections/Page/hooks/indexEmbedding.ts index 3e6830724..25e14f959 100644 --- a/apps/cms/src/collections/Page/hooks/indexEmbedding.ts +++ b/apps/cms/src/collections/Page/hooks/indexEmbedding.ts @@ -1,29 +1,31 @@ -import type { CollectionAfterChangeHook, CollectionAfterDeleteHook } from "payload"; +import type { CollectionAfterChangeHook, CollectionAfterDeleteHook, PayloadRequest } from "payload"; import type { Pool } from "pg"; import { extractPageText } from "@/collections/Page/extractPageText"; -import { getLocaleFromRequest } from "@/lib/utils/getLocaleFromRequest"; +import { I18N_CONFIG } from "@/lib/config/i18n"; +import type { Locale } from "@/lib/types"; import type { Page } from "@/payload-types"; import { upsertEmbedding, deleteEmbedding } from "@/lib/search/dbOperations"; import { generateEmbedding } from "@/lib/search/generateEmbedding"; -export const indexPageEmbedding: CollectionAfterChangeHook = async ({ doc, req }) => { - if (doc._status !== "published") { - return doc; - } - if (req.context?.skipEmbedding) { - return doc; - } +const DEPTH_FOR_RELATED_TEXT = 1; +async function upsertPageEmbedding({ + locale, + page, + req, +}: { + locale: Locale; + page: Page; + req: PayloadRequest; +}): Promise { try { - const locale = getLocaleFromRequest(req); - const text = extractPageText(doc); - const embedding = await generateEmbedding(text); + const embedding = await generateEmbedding(extractPageText(page)); const { pool } = req.payload.db as unknown as { pool: Pool }; await upsertEmbedding({ collection: "page", - documentId: String(doc.id), + documentId: String(page.id), embedding, locale, pool, @@ -31,6 +33,44 @@ export const indexPageEmbedding: CollectionAfterChangeHook = async ({ doc, } catch (error) { req.payload.logger.error({ error }, "Failed to index page embedding"); } +} + +// Locale fallback lets an edit in one locale change the text of the other locales. +async function reindexPageInEveryLocale({ id, req }: { id: number; req: PayloadRequest }) { + for (const { code: locale } of I18N_CONFIG.locales) { + const page = await req.payload.findByID({ + collection: "page", + depth: DEPTH_FOR_RELATED_TEXT, + disableErrors: true, + id, + locale, + overrideAccess: true, + req, + }); + + if (page?._status === "published") { + await upsertPageEmbedding({ locale, page, req }); + } + } +} + +export const indexPageEmbedding: CollectionAfterChangeHook = async ({ + doc, + operation, + req, +}) => { + // nested-docs re-saves every new page as an update (resaveSelfAfterCreate), and that update indexes it. + if (operation === "create") { + return doc; + } + if (doc._status !== "published") { + return doc; + } + if (req.context?.skipEmbedding) { + return doc; + } + + await reindexPageInEveryLocale({ id: doc.id, req }); return doc; }; diff --git a/apps/cms/src/lib/dal/getPageBySlug.ts b/apps/cms/src/lib/dal/getPageBySlug.ts index 1d50e010b..f7598c377 100644 --- a/apps/cms/src/lib/dal/getPageBySlug.ts +++ b/apps/cms/src/lib/dal/getPageBySlug.ts @@ -10,6 +10,8 @@ import { getPayloadClient } from "@/dal/payload-client"; import { getAllDocuments } from "./getAllDocuments"; +export const PAGE_RENDER_DEPTH = 3; + export async function getPageBySlugQuery( payload: Payload, pathSegmentsNorm: string[], @@ -20,7 +22,7 @@ export async function getPageBySlugQuery( const lastSegment = pathSegmentsNorm.at(-1)!; const docs = await getAllDocuments(payload, "page", { - depth: 3, + depth: PAGE_RENDER_DEPTH, draft, locale: resolvedLocale, overrideAccess: true, diff --git a/apps/cms/src/lib/dal/index.ts b/apps/cms/src/lib/dal/index.ts index 2352a91f3..2b9ccd7fc 100644 --- a/apps/cms/src/lib/dal/index.ts +++ b/apps/cms/src/lib/dal/index.ts @@ -8,7 +8,7 @@ export { getPayloadClient } from "./payload-client"; // Pages -export { getPageBySlug } from "./getPageBySlug"; +export { getPageBySlug, PAGE_RENDER_DEPTH } from "./getPageBySlug"; // Posts export { getPostBySlug } from "./getPostBySlug"; diff --git a/apps/cms/src/lib/plugins/index.ts b/apps/cms/src/lib/plugins/index.ts index f63d03e65..a6d5d9594 100644 --- a/apps/cms/src/lib/plugins/index.ts +++ b/apps/cms/src/lib/plugins/index.ts @@ -18,6 +18,7 @@ import type { Field, PayloadRequest, Plugin } from "payload"; import { Authors } from "@/collections/Authors"; import { Categories } from "@/collections/Categories"; import { Footer } from "@/collections/Footer/config"; +import { GlobalBlock } from "@/collections/GlobalBlock/config"; import { Header } from "@/collections/Header/config"; import { Page as PageCollection } from "@/collections/Page/Page"; import serverExtractPageContent from "@/collections/Page/serverExtractPageContent"; @@ -299,6 +300,7 @@ export const plugins: Plugin[] = [ { slug: "testimonials", titleField: "author" }, { slug: "header", titleField: "name" }, { slug: "footer", titleField: "name" }, + { slug: "globalBlock", titleField: "title" }, ], translations: { @@ -333,7 +335,7 @@ export const plugins: Plugin[] = [ }), schedulePublicationPlugin({ - collections: ["page", "posts"], + collections: ["page", "posts", "globalBlock"], globals: ["site-settings"], schedulePublish: { timeIntervals: 60, @@ -342,8 +344,17 @@ export const plugins: Plugin[] = [ }), translatorPlugin({ - collections: [PageCollection, Posts, Categories, Authors, Testimonials, Header, Footer].map( - (col) => JSON.parse(JSON.stringify(col, (_, v) => (typeof v === "function" ? undefined : v))) + collections: [ + PageCollection, + Posts, + Categories, + Authors, + Testimonials, + Header, + Footer, + GlobalBlock, + ].map((col) => + JSON.parse(JSON.stringify(col, (_, v) => (typeof v === "function" ? undefined : v))) ), access: { check: ({ req }) => Boolean(req.user) }, runner: createSyncRunner(), diff --git a/apps/cms/src/lib/utils/generatePreviewPath.ts b/apps/cms/src/lib/utils/generatePreviewPath.ts index a0c6a20b2..ee15db173 100644 --- a/apps/cms/src/lib/utils/generatePreviewPath.ts +++ b/apps/cms/src/lib/utils/generatePreviewPath.ts @@ -1,7 +1,7 @@ import type { BLOG_CONFIG } from "@/lib/config/blog"; interface Props { - collection: "page" | typeof BLOG_CONFIG.collection; + collection: "page" | "globalBlock" | typeof BLOG_CONFIG.collection; slug: string; path: string; } diff --git a/apps/cms/src/lib/utils/revalidatePageCache.ts b/apps/cms/src/lib/utils/revalidatePageCache.ts index 4007fe782..62a095e45 100644 --- a/apps/cms/src/lib/utils/revalidatePageCache.ts +++ b/apps/cms/src/lib/utils/revalidatePageCache.ts @@ -7,7 +7,11 @@ import { getPathFromBreadcrumbs } from "../utils/path/getPathFromBreadcrumbs"; import { cacheTag } from "./cacheTags"; import { revalidateScopedTag } from "./scopedCache"; -export function revalidatePageCache(params: { doc: Page; locale: Locale; payload: Payload }): void { +export function revalidatePageCache(params: { + doc: Pick; + locale: Locale; + payload: Payload; +}): void { const path = getPathFromBreadcrumbs(params.doc.breadcrumbs) ?? "home"; params.payload.logger?.info?.(`Revalidating page with slug: ${params.doc.slug}`); revalidateScopedTag(cacheTag({ locale: params.locale, path, type: "page" }), "max");