From e12d814040be0f9cfeef6cd12dd78850a44fa521 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Mon, 22 Jun 2026 12:43:34 +0200 Subject: [PATCH] feat: manage blog detail fallback copy via payload --- src/collections/Posts/index.ts | 15 +++++++++++++++ src/payload-types.ts | 22 ++++++++++++++++++++++ src/spa/blogDetailContent.ts | 10 ++++++++++ src/spa/pages/BlogDetail.tsx | 20 +++++++++++--------- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/collections/Posts/index.ts b/src/collections/Posts/index.ts index 094e604..43dc170 100644 --- a/src/collections/Posts/index.ts +++ b/src/collections/Posts/index.ts @@ -166,6 +166,21 @@ export const Posts: CollectionConfig<'posts'> = { { name: 'url', type: 'text', defaultValue: blogDetailContent.heroBackLink.url }, ], }, + { + name: 'fallbackArticle', + label: 'Missing article field fallbacks', + type: 'group', + fields: [ + { name: 'title', type: 'text', defaultValue: blogDetailContent.fallbackArticle.title }, + { name: 'category', type: 'text', defaultValue: blogDetailContent.fallbackArticle.category }, + { name: 'date', type: 'text', defaultValue: blogDetailContent.fallbackArticle.date }, + { name: 'authorName', type: 'text', defaultValue: blogDetailContent.fallbackArticle.authorName }, + { name: 'authorRole', type: 'text', defaultValue: blogDetailContent.fallbackArticle.authorRole }, + { name: 'readTime', type: 'text', defaultValue: blogDetailContent.fallbackArticle.readTime }, + { name: 'excerpt', type: 'textarea', defaultValue: blogDetailContent.fallbackArticle.excerpt }, + { name: 'tag', type: 'text', defaultValue: blogDetailContent.fallbackArticle.tag }, + ], + }, { name: 'categoryColors', label: 'Category colors JSON', diff --git a/src/payload-types.ts b/src/payload-types.ts index 7c911b2..cd9a86a 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -2156,6 +2156,16 @@ export interface Post { label?: string | null; url?: string | null; }; + fallbackArticle?: { + title?: string | null; + category?: string | null; + date?: string | null; + authorName?: string | null; + authorRole?: string | null; + readTime?: string | null; + excerpt?: string | null; + tag?: string | null; + }; categoryColors?: | { [k: string]: unknown; @@ -5025,6 +5035,18 @@ export interface PostsSelect { label?: T; url?: T; }; + fallbackArticle?: + | T + | { + title?: T; + category?: T; + date?: T; + authorName?: T; + authorRole?: T; + readTime?: T; + excerpt?: T; + tag?: T; + }; categoryColors?: T; metaCopy?: | T diff --git a/src/spa/blogDetailContent.ts b/src/spa/blogDetailContent.ts index dfec19a..1d9bed3 100644 --- a/src/spa/blogDetailContent.ts +++ b/src/spa/blogDetailContent.ts @@ -9,6 +9,16 @@ export const blogDetailContent = { label: 'Newsroom', url: '/presse', }, + fallbackArticle: { + title: 'Presse', + category: 'Presse', + date: 'Aktuell', + authorName: 'BMP Redaktion', + authorRole: 'Presse', + readTime: '1 min', + excerpt: 'Dieser Pressebeitrag wird aus Payload CMS geladen.', + tag: 'Presse', + }, categoryColors: { Innovation: '#2563EB', Engagement: '#16A34A', diff --git a/src/spa/pages/BlogDetail.tsx b/src/spa/pages/BlogDetail.tsx index 336b1ec..8b28865 100644 --- a/src/spa/pages/BlogDetail.tsx +++ b/src/spa/pages/BlogDetail.tsx @@ -35,6 +35,7 @@ const getDetailPage = (value: unknown): BlogDetailCms => { ...detail, notFound: { ...blogDetailContent.notFound, ...detail.notFound }, heroBackLink: { ...blogDetailContent.heroBackLink, ...detail.heroBackLink }, + fallbackArticle: { ...blogDetailContent.fallbackArticle, ...detail.fallbackArticle }, categoryColors: colorMap(detail.categoryColors), metaCopy: { ...blogDetailContent.meta, ...(detail.metaCopy || detail.meta) }, sidebarCta: { ...blogDetailContent.sidebarCta, ...detail.sidebarCta }, @@ -48,23 +49,24 @@ export default function BlogDetail() { const route = useCmsRoute(); const cmsArticle = route?.collection === 'posts' ? route.doc : undefined; const cmsDetail = getDetailPage(cmsArticle?.detailPage); + const fallbackArticle = cmsDetail.fallbackArticle || blogDetailContent.fallbackArticle; const articleFallbackImage = mediaUrl(cmsDetail.fallbackImage, `/images/${blogDetailContent.fallbackImageFilename}`); const article = (slug ? getArticleBySlug(slug) : undefined) || (cmsArticle ? { slug: String(cmsArticle.slug || slug), - title: String(cmsArticle.title || 'Presse'), - cat: String(cmsArticle.cat || 'Presse'), - date: String(cmsArticle.date || 'Aktuell'), - author: String(cmsArticle.authorName || 'BMP Redaktion'), - authorRole: String(cmsArticle.authorRole || 'Presse'), - readTime: String(cmsArticle.readTime || '1 min'), + title: String(cmsArticle.title || fallbackArticle.title || blogDetailContent.fallbackArticle.title), + cat: String(cmsArticle.cat || fallbackArticle.category || blogDetailContent.fallbackArticle.category), + date: String(cmsArticle.date || fallbackArticle.date || blogDetailContent.fallbackArticle.date), + author: String(cmsArticle.authorName || fallbackArticle.authorName || blogDetailContent.fallbackArticle.authorName), + authorRole: String(cmsArticle.authorRole || fallbackArticle.authorRole || blogDetailContent.fallbackArticle.authorRole), + readTime: String(cmsArticle.readTime || fallbackArticle.readTime || blogDetailContent.fallbackArticle.readTime), img: docImageUrl(cmsArticle, articleFallbackImage, 'heroImage'), - excerpt: String(cmsArticle.excerpt || cmsArticle.meta?.description || 'Dieser Pressebeitrag wird aus Payload CMS geladen.'), - sections: Array.isArray(cmsArticle.sections) && cmsArticle.sections.length > 0 ? cmsArticle.sections as BlogSection[] : [{ body: String(cmsArticle.excerpt || cmsArticle.meta?.description || 'Dieser Pressebeitrag wird aus Payload CMS geladen.') }], + excerpt: String(cmsArticle.excerpt || cmsArticle.meta?.description || fallbackArticle.excerpt || blogDetailContent.fallbackArticle.excerpt), + sections: Array.isArray(cmsArticle.sections) && cmsArticle.sections.length > 0 ? cmsArticle.sections as BlogSection[] : [{ body: String(cmsArticle.excerpt || cmsArticle.meta?.description || fallbackArticle.excerpt || blogDetailContent.fallbackArticle.excerpt) }], tags: Array.isArray(cmsArticle.tags) ? cmsArticle.tags .map((item: { tag?: string }) => item.tag) .filter((tag): tag is string => Boolean(tag)) - : ['Presse'], + : [String(fallbackArticle.tag || blogDetailContent.fallbackArticle.tag)], relatedSlugs: Array.isArray(cmsArticle.relatedSlugs) ? cmsArticle.relatedSlugs .map((item: { slug?: string }) => item.slug)