feat: manage participation fallback labels via payload

This commit is contained in:
syntaxbullet
2026-06-22 12:30:34 +02:00
parent 6676610e5e
commit 53fc63b486
5 changed files with 32 additions and 9 deletions

View File

@@ -109,6 +109,7 @@ export const participationFields: Field[] = [
text('eyebrow', 'Eyebrow', participationContent.process.eyebrow),
textarea('heading', 'Heading', participationContent.process.heading),
textarea('description', 'Intro paragraph', participationContent.process.description),
text('stepPrefix', 'Step label prefix', participationContent.process.stepPrefix),
text('scrollHint', 'Desktop scroll hint', participationContent.process.scrollHint),
text('progressLabel', 'Desktop progress label', participationContent.process.progressLabel),
{
@@ -184,6 +185,12 @@ export const participationFields: Field[] = [
textarea('heading', 'Heading', participationContent.applicationWays.heading),
textarea('description', 'Intro paragraph', participationContent.applicationWays.description),
text('recommendedLabel', 'Featured card label', participationContent.applicationWays.recommendedLabel),
ctaGroup(
'fallbackCta',
'Fallback CTA for path cards',
participationContent.applicationWays.fallbackCta.label,
participationContent.applicationWays.fallbackCta.url,
),
{
name: 'institutions',
label: 'Proposal institutions',
@@ -222,7 +229,7 @@ export const participationFields: Field[] = [
dbName: 'way_facts',
fields: factFields,
},
ctaGroup('cta', 'CTA', 'Zum Formular', '#bewerben'),
ctaGroup('cta', 'CTA', participationContent.applicationWays.fallbackCta.label, participationContent.applicationWays.fallbackCta.url),
],
},
],

View File

@@ -732,6 +732,7 @@ export interface Page {
eyebrow?: string | null;
heading?: string | null;
description?: string | null;
stepPrefix?: string | null;
scrollHint?: string | null;
progressLabel?: string | null;
steps?:
@@ -872,6 +873,10 @@ export interface Page {
heading?: string | null;
description?: string | null;
recommendedLabel?: string | null;
fallbackCta?: {
label?: string | null;
url?: string | null;
};
institutions?:
| {
text?: string | null;
@@ -3711,6 +3716,7 @@ export interface PagesSelect<T extends boolean = true> {
eyebrow?: T;
heading?: T;
description?: T;
stepPrefix?: T;
scrollHint?: T;
progressLabel?: T;
steps?:
@@ -3796,6 +3802,12 @@ export interface PagesSelect<T extends boolean = true> {
heading?: T;
description?: T;
recommendedLabel?: T;
fallbackCta?:
| T
| {
label?: T;
url?: T;
};
institutions?:
| T
| {

View File

@@ -38,6 +38,7 @@ export default function WegZurAuszeichnungSection({ content }: { content?: Proce
const isMobile = useIsMobile();
const section = { ...participationContent.process, ...(content || {}) };
const steps = fallbackArray(section.steps, participationContent.process.steps);
const stepPrefix = fallbackText(section.stepPrefix, participationContent.process.stepPrefix);
// ── MOBILE: stacked vertical list (no scroll-jack, no horizontal overflow) ──
if (isMobile) {
@@ -66,7 +67,7 @@ export default function WegZurAuszeichnungSection({ content }: { content?: Proce
</div>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: GOLD, letterSpacing: '0.18em', textTransform: 'uppercase' }}>Schritt {fallbackText(item.step, '')}</span>
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: GOLD, letterSpacing: '0.18em', textTransform: 'uppercase' }}>{stepPrefix} {fallbackText(item.step, '')}</span>
<span style={{ width: 1, height: 11, background: 'rgba(255,255,255,0.2)' }} />
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(255,255,255,0.62)', letterSpacing: '0.06em' }}>{fallbackText(item.date, '')}</span>
</div>
@@ -160,7 +161,7 @@ export default function WegZurAuszeichnungSection({ content }: { content?: Proce
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24 }}>
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: GOLD, letterSpacing: '0.22em', textTransform: 'uppercase' }}>
Schritt {fallbackText(item.step, '')}
{stepPrefix} {fallbackText(item.step, '')}
</span>
<span style={{ width: 1, height: 12, background: 'rgba(255,255,255,0.2)' }} />
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(255,255,255,0.62)', letterSpacing: '0.08em' }}>{fallbackText(item.date, '')}</span>

View File

@@ -67,7 +67,7 @@ const Participation: React.FC = () => {
{/* ── HERO ─────────────────────────────────────────────────────────────── */}
<section style={{ position: 'relative', minHeight: '65vh', display: 'flex', alignItems: 'flex-end', overflow: 'hidden', background: NAVY }}>
<Image unoptimized
src={mediaUrl(hero.backgroundImage, '/images/preisuebergabe.jpg')}
src={mediaUrl(hero.backgroundImage, `/images/${participationContent.hero.backgroundImageFilename}`)}
alt={mediaAlt(hero.backgroundImage, fallbackText(hero.imageAlt, participationContent.hero.imageAlt))}
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
/>
@@ -183,7 +183,7 @@ const Participation: React.FC = () => {
{/* Left full-bleed image with gradient blend into navy on the right (+ heading on mobile) */}
<div style={{ position: 'relative', overflow: 'hidden', minHeight: isMobile ? 280 : undefined }}>
<Image unoptimized
src={mediaUrl(evaluation.image, '/images/buehne-gewinner.jpg')}
src={mediaUrl(evaluation.image, `/images/${participationContent.evaluation.imageFilename}`)}
alt={mediaAlt(evaluation.image, fallbackText(evaluation.imageAlt, participationContent.evaluation.imageAlt))}
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center', display: 'block' }}
/>
@@ -329,7 +329,7 @@ const Participation: React.FC = () => {
{!isMobile && (
<div style={{ height: 220, position: 'relative', overflow: 'hidden', flexShrink: 0, zIndex: 1 }}>
<Image unoptimized src={mediaUrl(applicationForm.image, '/images/preistraeger-jubel.jpg')} alt={mediaAlt(applicationForm.image, fallbackText(applicationForm.imageAlt, participationContent.applicationForm.imageAlt))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', filter: 'sepia(0.18) brightness(0.92)' }} />
<Image unoptimized src={mediaUrl(applicationForm.image, `/images/${participationContent.applicationForm.imageFilename}`)} alt={mediaAlt(applicationForm.image, fallbackText(applicationForm.imageAlt, participationContent.applicationForm.imageAlt))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', filter: 'sepia(0.18) brightness(0.92)' }} />
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, rgba(168,120,0,0.1) 0%, rgba(168,120,0,0.25) 50%, rgba(168,120,0,0.92) 88%, #A87800 100%)' }} />
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 2, background: 'rgba(17,29,85,0.35)' }} />
<div style={{ position: 'absolute', bottom: 16, left: 40, display: 'flex', alignItems: 'center', gap: 8 }}>
@@ -430,6 +430,7 @@ function BewerbungswegeSection({ content }: { content?: ApplicationWaysContent }
const section = { ...participationContent.applicationWays, ...(content || {}) };
const institutions = fallbackArray(section.institutions, participationContent.applicationWays.institutions);
const ways = fallbackArray(section.ways, participationContent.applicationWays.ways);
const fallbackCta = { ...participationContent.applicationWays.fallbackCta, ...(section.fallbackCta || {}) };
return (
<section style={{ background: CREAM, position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
<MunichSkylineBg />
@@ -488,7 +489,7 @@ function BewerbungswegeSection({ content }: { content?: ApplicationWaysContent }
</div>
{/* CTA */}
<div style={{ padding: '18px 22px 22px' }}>
<a href={fallbackText(w.cta?.url, '#bewerben')} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, minHeight: 52, background: GOLD, color: '#101828', fontFamily: FF, fontSize: 14, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', textDecoration: 'none', borderRadius: 10 }}>{fallbackText(w.cta?.label, 'Zum Formular')} <ArrowRight size={14} /></a>
<a href={fallbackText(w.cta?.url, fallbackCta.url)} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, minHeight: 52, background: GOLD, color: '#101828', fontFamily: FF, fontSize: 14, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', textDecoration: 'none', borderRadius: 10 }}>{fallbackText(w.cta?.label, fallbackCta.label)} <ArrowRight size={14} /></a>
</div>
</div>
);
@@ -532,12 +533,12 @@ function BewerbungswegeSection({ content }: { content?: ApplicationWaysContent }
</div>
<a
href={fallbackText(w.cta?.url, '#bewerben')}
href={fallbackText(w.cta?.url, fallbackCta.url)}
style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#101828', background: GOLD, padding: '14px 24px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, marginTop: 36, alignSelf: 'flex-start', transition: 'opacity 0.15s' }}
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '0.85')}
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}
>
{fallbackText(w.cta?.label, 'Zum Formular')} <ArrowRight size={13} />
{fallbackText(w.cta?.label, fallbackCta.label)} <ArrowRight size={13} />
</a>
</div>
);

View File

@@ -11,6 +11,7 @@ export const participationContent = {
heading: 'IHR WEG ZUM\nPREIS.',
description:
'Von der Einreichung bis zur Gala vier klar definierte Schritte auf dem Weg zur höchsten Auszeichnung des bayerischen Mittelstands.',
stepPrefix: 'Schritt',
scrollHint: 'Scrollen zum Erkunden',
progressLabel: '01 / 04',
steps: [
@@ -105,6 +106,7 @@ export const participationContent = {
'Mitglieder des Wirtschaftsbeirates Bayern',
'Ehemalige Preisträger des Bayerischen Mittelstandspreises',
].map((text) => ({ text })),
fallbackCta: { label: 'Zum Formular', url: '#bewerben' },
ways: [
{
label: 'Weg 01',