feat: manage press fallback labels via payload

This commit is contained in:
syntaxbullet
2026-06-22 12:27:36 +02:00
parent 9ce55217a1
commit 6676610e5e
5 changed files with 142 additions and 29 deletions

View File

@@ -16,6 +16,8 @@ const FB = '"Inter", sans-serif';
type PressIndexCms = Partial<typeof pressIndexContent> & {
hero?: Partial<typeof pressIndexContent.hero> & { image?: unknown }
events?: Partial<typeof pressIndexContent.events> & { collectionFallbackImage?: unknown }
news?: Partial<typeof pressIndexContent.news> & { collectionFallbackImage?: unknown }
}
type PressEventItem = (typeof pressIndexContent.events.fallbackItems)[number]
@@ -24,34 +26,45 @@ type PressNewsItem = (typeof pressIndexContent.news.fallbackItems)[number]
const fallbackText = (value: unknown, fallback: string) => typeof value === 'string' && value.length > 0 ? value : fallback;
const fallbackArray = <T,>(value: unknown, fallback: T[]) => Array.isArray(value) && value.length ? value as T[] : fallback;
const statusLabels: Record<string, string> = {
geplant: 'In Planung',
'anmeldung-offen': 'Anmeldung offen',
intern: 'Intern',
abgeschlossen: 'Abgeschlossen',
type StatusLabel = {
value?: string | null
label?: string | null
}
const normalizeStatus = (status: unknown) => {
const value = fallbackText(status, 'Geplant')
return statusLabels[value] || value
const statusLabelMap = (items: unknown) =>
fallbackArray<StatusLabel>(items, pressIndexContent.events.statusLabels).reduce<Record<string, string>>((labels, item) => {
const value = fallbackText(item.value, '')
const label = fallbackText(item.label, '')
if (value && label) labels[value] = label
return labels
}, {})
const normalizeStatus = (status: unknown, labels: Record<string, string>, defaultStatusLabel: string) => {
const value = fallbackText(status, defaultStatusLabel)
return labels[value] || value
}
const eventFromCms = (event: CmsRouteDoc): PressEventItem => ({
title: String(event.title || 'Event'),
date: String(event.date || 'Termin folgt'),
location: String(event.location || event.venue || 'Bayern'),
cat: String(event.category || 'Event'),
status: normalizeStatus(event.status),
img: docImageUrl(event, '/images/buehne-moderatoren.jpg'),
const eventFromCms = (
event: CmsRouteDoc,
fallbackImage: string,
labels: Record<string, string>,
eventsSection: NonNullable<PressIndexCms['events']>,
): PressEventItem => ({
title: String(event.title || fallbackText(eventsSection.missingTitleLabel, pressIndexContent.events.missingTitleLabel)),
date: String(event.date || fallbackText(eventsSection.missingDateLabel, pressIndexContent.events.missingDateLabel)),
location: String(event.location || event.venue || fallbackText(eventsSection.missingLocationLabel, pressIndexContent.events.missingLocationLabel)),
cat: String(event.category || fallbackText(eventsSection.missingCategoryLabel, pressIndexContent.events.missingCategoryLabel)),
status: normalizeStatus(event.status, labels, fallbackText(eventsSection.defaultStatusLabel, pressIndexContent.events.defaultStatusLabel)),
img: docImageUrl(event, fallbackImage),
desc: String(event.description || event.meta?.description || ''),
slug: String(event.spaPath || `/presse/events/${event.slug}`),
})
const postFromCms = (post: CmsRouteDoc): PressNewsItem => ({
title: String(post.title || 'Presse'),
const postFromCms = (post: CmsRouteDoc, fallbackImage: string, newsSection: NonNullable<PressIndexCms['news']>): PressNewsItem => ({
title: String(post.title || fallbackText(newsSection.missingTitleLabel, pressIndexContent.news.missingTitleLabel)),
excerpt: String(post.excerpt || post.meta?.description || ''),
cat: String(post.cat || 'Presse'),
img: docImageUrl(post, '/images/gala-saal-overview.jpg', 'heroImage'),
cat: String(post.cat || fallbackText(newsSection.missingCategoryLabel, pressIndexContent.news.missingCategoryLabel)),
img: docImageUrl(post, fallbackImage, 'heroImage'),
slug: String(post.spaPath || `/presse/blog/${post.slug}`),
})
@@ -204,22 +217,34 @@ function DarkInput(props: React.InputHTMLAttributes<HTMLInputElement>) {
const Press: React.FC = () => {
const isMobile = useIsMobile();
const cms = (useCmsRoute()?.doc?.pressIndex || {}) as PressIndexCms;
const hero = { ...pressIndexContent.hero, ...(cms.hero || {}) };
const eventsSection = { ...pressIndexContent.events, ...(cms.events || {}) };
const newsSection = { ...pressIndexContent.news, ...(cms.news || {}) };
const downloads = { ...pressIndexContent.downloads, ...(cms.downloads || {}) };
const accreditation = { ...pressIndexContent.accreditation, ...(cms.accreditation || {}) };
const hero = React.useMemo(() => ({ ...pressIndexContent.hero, ...(cms.hero || {}) }), [cms.hero]);
const eventsSection = React.useMemo(() => ({ ...pressIndexContent.events, ...(cms.events || {}) }), [cms.events]);
const newsSection = React.useMemo(() => ({ ...pressIndexContent.news, ...(cms.news || {}) }), [cms.news]);
const downloads = React.useMemo(() => ({ ...pressIndexContent.downloads, ...(cms.downloads || {}) }), [cms.downloads]);
const accreditation = React.useMemo(() => ({ ...pressIndexContent.accreditation, ...(cms.accreditation || {}) }), [cms.accreditation]);
const contactLines = fallbackText(downloads.contactLines, pressIndexContent.downloads.contactLines).split('\n');
const cmsEvents = useCmsCollection('events');
const cmsPosts = useCmsCollection('posts');
const eventStatusLabels = React.useMemo(() => statusLabelMap(eventsSection.statusLabels), [eventsSection.statusLabels]);
const eventCollectionFallbackImage = mediaUrl(eventsSection.collectionFallbackImage, `/images/${pressIndexContent.events.collectionFallbackImageFilename}`);
const newsCollectionFallbackImage = mediaUrl(newsSection.collectionFallbackImage, `/images/${pressIndexContent.news.collectionFallbackImageFilename}`);
const combinedEvents = React.useMemo(() => {
if (cmsEvents.length) return cmsEvents.map(eventFromCms);
if (cmsEvents.length) {
return cmsEvents.map((event) =>
eventFromCms(
event,
eventCollectionFallbackImage,
eventStatusLabels,
eventsSection,
),
);
}
return fallbackArray<PressEventItem>(eventsSection.fallbackItems, pressIndexContent.events.fallbackItems);
}, [cmsEvents, eventsSection.fallbackItems]);
}, [cmsEvents, eventCollectionFallbackImage, eventStatusLabels, eventsSection]);
const combinedNews = React.useMemo(() => {
if (cmsPosts.length) return cmsPosts.map(postFromCms);
if (cmsPosts.length) return cmsPosts.map((post) => postFromCms(post, newsCollectionFallbackImage, newsSection));
return fallbackArray<PressNewsItem>(newsSection.fallbackItems, pressIndexContent.news.fallbackItems);
}, [cmsPosts, newsSection.fallbackItems]);
}, [cmsPosts, newsCollectionFallbackImage, newsSection]);
const [hoveredEvent, setHoveredEvent] = useState<number | null>(null);
const [dlHovered, setDlHovered] = useState(false);
const [submitHovered, setSubmitHovered] = useState(false);