feat: manage preistraeger fallback copy via payload

This commit is contained in:
syntaxbullet
2026-06-22 12:48:54 +02:00
parent 2eef1d8350
commit 856ab84252
7 changed files with 109 additions and 17 deletions

View File

@@ -100,6 +100,18 @@ export const preistraegerIndexFields: Field[] = [
fields: [
uploadField('fallbackImage', 'Fallback card image', `Current frontend fallback image: /images/${preistraegerIndexContent.card.fallbackImageFilename}`),
text('hoverLabel', 'Hover label', preistraegerIndexContent.card.hoverLabel),
{
name: 'fallbackWinner',
label: 'Missing winner field fallbacks',
type: 'group',
fields: [
text('name', 'Name', preistraegerIndexContent.card.fallbackWinner.name),
text('category', 'Category', preistraegerIndexContent.card.fallbackWinner.category),
text('awardType', 'Award type', preistraegerIndexContent.card.fallbackWinner.awardType),
text('location', 'Location', preistraegerIndexContent.card.fallbackWinner.location),
text('industry', 'Industry', preistraegerIndexContent.card.fallbackWinner.industry),
],
},
],
},
{

View File

@@ -138,6 +138,22 @@ export const Preistraeger: CollectionConfig = {
{ name: 'label', type: 'text', label: 'Label', defaultValue: preistraegerDetailContent.breadcrumb.label },
],
},
{
name: 'fallbackWinner',
label: 'Missing winner field fallbacks',
type: 'group',
fields: [
{ name: 'name', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.name },
{ name: 'category', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.category },
{ name: 'awardType', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.awardType },
{ name: 'longDesc', type: 'textarea', defaultValue: preistraegerDetailContent.fallbackWinner.longDesc },
{ name: 'quote', type: 'textarea', defaultValue: preistraegerDetailContent.fallbackWinner.quote },
{ name: 'quotePerson', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.quotePerson },
{ name: 'quoteRole', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.quoteRole },
{ name: 'location', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.location },
{ name: 'industry', type: 'text', defaultValue: preistraegerDetailContent.fallbackWinner.industry },
],
},
{
name: 'sections',
label: 'Section copy',

View File

@@ -1463,6 +1463,13 @@ export interface Page {
*/
fallbackImage?: (number | null) | Media;
hoverLabel?: string | null;
fallbackWinner?: {
name?: string | null;
category?: string | null;
awardType?: string | null;
location?: string | null;
industry?: string | null;
};
};
/**
* Bottom application and membership CTA.
@@ -2782,6 +2789,17 @@ export interface Preistraeger {
url?: string | null;
label?: string | null;
};
fallbackWinner?: {
name?: string | null;
category?: string | null;
awardType?: string | null;
longDesc?: string | null;
quote?: string | null;
quotePerson?: string | null;
quoteRole?: string | null;
location?: string | null;
industry?: string | null;
};
sections?: {
companyHeading?: string | null;
juryHeading?: string | null;
@@ -4336,6 +4354,15 @@ export interface PagesSelect<T extends boolean = true> {
| {
fallbackImage?: T;
hoverLabel?: T;
fallbackWinner?:
| T
| {
name?: T;
category?: T;
awardType?: T;
location?: T;
industry?: T;
};
};
cta?:
| T
@@ -5243,6 +5270,19 @@ export interface PreistraegerSelect<T extends boolean = true> {
url?: T;
label?: T;
};
fallbackWinner?:
| T
| {
name?: T;
category?: T;
awardType?: T;
longDesc?: T;
quote?: T;
quotePerson?: T;
quoteRole?: T;
location?: T;
industry?: T;
};
sections?:
| T
| {

View File

@@ -22,21 +22,25 @@ type PreistraegerIndexCms = Partial<typeof preistraegerIndexContent> & {
const fallbackText = (value: unknown, fallback: string) => typeof value === 'string' && value.length > 0 ? value : fallback;
const normalizeWinner = (doc: CmsRouteDoc, fallbackImage: string): Winner => ({
const normalizeWinner = (
doc: CmsRouteDoc,
fallbackImage: string,
fallbackWinner: typeof preistraegerIndexContent.card.fallbackWinner,
): Winner => ({
id: String(doc.id),
slug: String(doc.slug || ''),
name: String(doc.title || 'Preisträger'),
category: String(doc.category || 'Preisträger'),
name: String(doc.title || fallbackWinner.name || preistraegerIndexContent.card.fallbackWinner.name),
category: String(doc.category || fallbackWinner.category || preistraegerIndexContent.card.fallbackWinner.category),
year: Number(doc.year || new Date().getFullYear()),
type: String(doc.awardType || 'Auszeichnung'),
type: String(doc.awardType || fallbackWinner.awardType || preistraegerIndexContent.card.fallbackWinner.awardType),
img: docImageUrl(doc, fallbackImage),
shortDesc: String(doc.shortDesc || doc.description || doc.meta?.description || ''),
longDesc: String(doc.longDesc || doc.description || doc.meta?.description || ''),
quote: String(doc.quote || ''),
quotePerson: String(doc.quotePerson || ''),
quoteRole: String(doc.quoteRole || ''),
location: String(doc.location || 'Bayern'),
industry: String(doc.industry || 'Mittelstand'),
location: String(doc.location || fallbackWinner.location || preistraegerIndexContent.card.fallbackWinner.location),
industry: String(doc.industry || fallbackWinner.industry || preistraegerIndexContent.card.fallbackWinner.industry),
website: String(doc.website || '#'),
hasMedia: Boolean(doc.hasMedia),
});
@@ -60,9 +64,10 @@ export default function Preistraeger() {
[card],
);
const winners = useMemo(() => {
if (cmsPreistraeger.length) return cmsPreistraeger.map((doc) => normalizeWinner(doc, cardFallbackImage)).filter((winner) => winner.slug);
const fallbackWinner = card.fallbackWinner || preistraegerIndexContent.card.fallbackWinner;
if (cmsPreistraeger.length) return cmsPreistraeger.map((doc) => normalizeWinner(doc, cardFallbackImage, fallbackWinner)).filter((winner) => winner.slug);
return WINNERS;
}, [cardFallbackImage, cmsPreistraeger]);
}, [card, cardFallbackImage, cmsPreistraeger]);
const years = useMemo(() => [...new Set(winners.map((winner) => winner.year))].sort((a, b) => b - a), [winners]);
const categories = useMemo(() => [...new Set(winners.map((winner) => winner.category))].sort((a, b) => a.localeCompare(b, 'de')), [winners]);
const [activeYear, setActiveYear] = useState<number | undefined>(undefined);

View File

@@ -31,22 +31,23 @@ export default function PreistraegerDetail() {
...preistraegerDetailContent,
...((cmsWinner?.detailPage || {}) as PreistraegerDetailCms),
} as PreistraegerDetailCms;
const fallbackWinner = cmsDetail.fallbackWinner || preistraegerDetailContent.fallbackWinner;
const winnerFallbackImage = mediaUrl(cmsDetail.fallbackImage, `/images/${preistraegerDetailContent.fallbackImageFilename}`);
const winner = cmsWinner ? {
id: String(cmsWinner.id),
slug: String(cmsWinner.slug || slug),
name: String(cmsWinner.title || 'Preisträger'),
category: String(cmsWinner.category || 'Preisträger'),
name: String(cmsWinner.title || fallbackWinner.name || preistraegerDetailContent.fallbackWinner.name),
category: String(cmsWinner.category || fallbackWinner.category || preistraegerDetailContent.fallbackWinner.category),
year: Number(cmsWinner.year || new Date().getFullYear()),
type: String(cmsWinner.awardType || 'Auszeichnung'),
type: String(cmsWinner.awardType || fallbackWinner.awardType || preistraegerDetailContent.fallbackWinner.awardType),
img: docImageUrl(cmsWinner, winnerFallbackImage),
shortDesc: String(cmsWinner.shortDesc || cmsWinner.description || cmsWinner.meta?.description || ''),
longDesc: String(cmsWinner.longDesc || cmsWinner.description || cmsWinner.meta?.description || 'Dieser Preisträger wird aus Payload CMS geladen.'),
quote: String(cmsWinner.quote || 'Diese Erfolgsgeschichte wird aktuell in Payload CMS gepflegt.'),
quotePerson: String(cmsWinner.quotePerson || cmsWinner.title || 'BMP'),
quoteRole: String(cmsWinner.quoteRole || 'Preisträger'),
location: String(cmsWinner.location || 'Bayern'),
industry: String(cmsWinner.industry || 'Mittelstand'),
longDesc: String(cmsWinner.longDesc || cmsWinner.description || cmsWinner.meta?.description || fallbackWinner.longDesc || preistraegerDetailContent.fallbackWinner.longDesc),
quote: String(cmsWinner.quote || fallbackWinner.quote || preistraegerDetailContent.fallbackWinner.quote),
quotePerson: String(cmsWinner.quotePerson || cmsWinner.title || fallbackWinner.quotePerson || preistraegerDetailContent.fallbackWinner.quotePerson),
quoteRole: String(cmsWinner.quoteRole || fallbackWinner.quoteRole || preistraegerDetailContent.fallbackWinner.quoteRole),
location: String(cmsWinner.location || fallbackWinner.location || preistraegerDetailContent.fallbackWinner.location),
industry: String(cmsWinner.industry || fallbackWinner.industry || preistraegerDetailContent.fallbackWinner.industry),
website: String(cmsWinner.website || '#'),
hasMedia: Boolean(cmsWinner.hasMedia),
detailPage: (cmsWinner.detailPage || {}) as PreistraegerDetailCms,

View File

@@ -5,6 +5,17 @@ export const preistraegerDetailContent = {
url: '/preistraeger',
label: 'Preisträger:innen',
},
fallbackWinner: {
name: 'Preisträger',
category: 'Preisträger',
awardType: 'Auszeichnung',
longDesc: 'Dieser Preisträger wird aus Payload CMS geladen.',
quote: 'Diese Erfolgsgeschichte wird aktuell in Payload CMS gepflegt.',
quotePerson: 'BMP',
quoteRole: 'Preisträger',
location: 'Bayern',
industry: 'Mittelstand',
},
sections: {
companyHeading: 'Über das Unternehmen',
juryHeading: 'Warum ausgezeichnet?',

View File

@@ -23,6 +23,13 @@ export const preistraegerIndexContent = {
card: {
fallbackImageFilename: 'gala-saal-overview.jpg',
hoverLabel: 'Mehr erfahren',
fallbackWinner: {
name: 'Preisträger',
category: 'Preisträger',
awardType: 'Auszeichnung',
location: 'Bayern',
industry: 'Mittelstand',
},
},
cta: {
text: 'Werden Sie der nächste Preisträger.',