diff --git a/src/collections/Pages/homeFields.ts b/src/collections/Pages/homeFields.ts index e611f83..a0b935f 100644 --- a/src/collections/Pages/homeFields.ts +++ b/src/collections/Pages/homeFields.ts @@ -1,4 +1,5 @@ import type { Field } from 'payload' +import { homeContent } from '@/spa/homeContent' const uploadField = (name: string, label: string, description?: string): Field => ({ name, @@ -48,37 +49,25 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Top full-screen gala image, badge, headline, intro text, primary buttons, video trigger, and partner ticker.'), fields: [ - uploadField('backgroundImage', 'Background image', 'Current frontend image: /images/gala-saal-overview.jpg'), - text('imageAlt', 'Image alt text', 'Awards Gala'), - text('badge', 'Badge text', 'Bewerbungsphase 2026 geschlossen'), - text('headingLine1', 'Headline line 1', 'BAYERNS BESTE'), - text('headingAccent', 'Headline accent line', 'MITTELSTÄNDLER.'), + uploadField('backgroundImage', 'Background image', `Current frontend image: /images/${homeContent.hero.backgroundImageFilename}`), + text('imageAlt', 'Image alt text', homeContent.hero.imageAlt), + text('badge', 'Badge text', homeContent.hero.badge), + text('headingLine1', 'Headline line 1', homeContent.hero.headingLine1), + text('headingAccent', 'Headline accent line', homeContent.hero.headingAccent), textarea( 'description', 'Intro paragraph', - 'Der Bayerische Mittelstandspreis würdigt herausragende Leistungen – von Innovation über Nachhaltigkeit bis hin zur Exzellenz.', + homeContent.hero.description, ), - { name: 'primaryCta', label: 'Primary CTA', type: 'group', fields: linkGroup('Jetzt kostenlos bewerben', '/teilnahme') }, - { name: 'secondaryCta', label: 'Secondary CTA', type: 'group', fields: linkGroup('Mitglied werden', '/mitglied-werden') }, - text('videoLabel', 'Video button label', 'Film abspielen'), - text('partnersLabel', 'Partner ticker label', 'Unsere Partner'), + { name: 'primaryCta', label: 'Primary CTA', type: 'group', fields: linkGroup(homeContent.hero.primaryCta.label, homeContent.hero.primaryCta.url) }, + { name: 'secondaryCta', label: 'Secondary CTA', type: 'group', fields: linkGroup(homeContent.hero.secondaryCta.label, homeContent.hero.secondaryCta.url) }, + text('videoLabel', 'Video button label', homeContent.hero.videoLabel), + text('partnersLabel', 'Partner ticker label', homeContent.hero.partnersLabel), { name: 'partners', label: 'Partner ticker names', type: 'array', - defaultValue: [ - { name: 'RSM Ebner Stolz' }, - { name: 'WWK' }, - { name: 'Deutsche Bank' }, - { name: 'Bionorica' }, - { name: 'Berchtesgadener Land' }, - { name: 'Metzler' }, - { name: 'vbw' }, - { name: 'Wirtschaftsbeirat Bayern' }, - { name: 'Radio Gong 96.3' }, - { name: 'münchen.tv' }, - { name: 'EWS' }, - ], + defaultValue: homeContent.hero.partners, fields: [text('name', 'Name')], }, ], @@ -89,34 +78,28 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Cream text panel plus right-side stage image and five eligibility rows.'), fields: [ - text('eyebrow', 'Eyebrow', 'Schnell-Check'), - textarea('heading', 'Heading', 'SIND SIE\nBERECHTIGT?'), - uploadField('image', 'Image', 'Current frontend image: /images/buehne-gewinner.jpg'), - text('imageAlt', 'Image alt text', 'Preisverleihung Bühne'), + text('eyebrow', 'Eyebrow', homeContent.quickCheck.eyebrow), + textarea('heading', 'Heading', homeContent.quickCheck.heading), + uploadField('image', 'Image', `Current frontend image: /images/${homeContent.quickCheck.imageFilename}`), + text('imageAlt', 'Image alt text', homeContent.quickCheck.imageAlt), textarea( 'body', 'Body copy', - 'Der Bayerische Mittelstandspreis richtet sich an inhabergeführte KMU im Freistaat. Fünf Kriterien entscheiden, erfüllen Sie alle fünf, steht Ihrer Bewerbung nichts im Weg.', + homeContent.quickCheck.body, ), { name: 'criteria', label: 'Criteria rows', type: 'array', - defaultValue: [ - { label: 'Standort Bayern', desc: 'Sitz oder Betriebsstätte im Freistaat' }, - { label: '50–1.500 Mitarbeiter', desc: 'Klassischer Mittelstand' }, - { label: 'Min. 3 Geschäftsjahre', desc: 'Markterfahrung und Stabilität' }, - { label: 'Privatwirtschaft', desc: 'Kein staatlicher Träger' }, - { label: 'Familien- bzw. eigentümergeführt', desc: 'Ihr Unternehmen ist inhabergeführt' }, - ], + defaultValue: homeContent.quickCheck.criteria, fields: [text('label', 'Criterion'), text('desc', 'Description')], }, textarea( 'note', 'Small note below criteria', - 'Auch ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen mit Schwerpunkt in Bayern kann teilnehmen.', + homeContent.quickCheck.note, ), - { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup('Ihr Weg zum Preis', '/teilnahme') }, + { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.quickCheck.cta.label, homeContent.quickCheck.cta.url) }, ], }, { @@ -125,28 +108,24 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Jubilation image, stats, editorial quote, body copy, and CTA.'), fields: [ - text('eyebrow', 'Eyebrow', 'Ein Gewinn für alle'), - textarea('heading', 'Heading', 'AUSZEICHNUNG\nFÜR DIE BESTEN.'), - uploadField('image', 'Image', 'Current frontend image: /images/preistraeger-jubel.jpg'), - text('imageAlt', 'Image alt text', 'BMP Auszeichnung'), + text('eyebrow', 'Eyebrow', homeContent.intro.eyebrow), + textarea('heading', 'Heading', homeContent.intro.heading), + uploadField('image', 'Image', `Current frontend image: /images/${homeContent.intro.imageFilename}`), + text('imageAlt', 'Image alt text', homeContent.intro.imageAlt), { name: 'stats', label: 'Stats', type: 'array', - defaultValue: [ - { value: '20+', label: 'Jahre Exzellenz' }, - { value: '0 €', label: 'Teilnahmegebühr' }, - { value: '100%', label: 'Unabhängig' }, - ], + defaultValue: homeContent.intro.stats, fields: [text('value', 'Value'), text('label', 'Label')], }, - textarea('quote', 'Quote', '„Ein Unternehmen zu gründen erfordert nicht allein spezielle Fähigkeiten. Es erfordert Persönlichkeit!“'), + textarea('quote', 'Quote', homeContent.intro.quote), textarea( 'body', 'Body copy', - 'Wir suchen Vorbilder, die sich trotz aller Risiken nicht scheuen, Visionen in Businesspläne und Ideen in Unternehmungen zu verwandeln, und das mit großem persönlichen Einsatz und Verantwortung für Ihre Mitarbeiter. Seit vielen Jahren würdigen wir herausragende unternehmerische Leistungen im Freistaat.', + homeContent.intro.body, ), - { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup('Mehr über den Preis', '/der-bmp') }, + { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.intro.cta.label, homeContent.intro.cta.url) }, ], }, { @@ -159,7 +138,7 @@ export const homeFields: Field[] = [ name: 'activePhase', label: 'Current phase shown on the homepage', type: 'select', - defaultValue: '0', + defaultValue: homeContent.status.activePhase, options: [ { label: '01 · Bewerbungsphase offen', value: '0' }, { label: '02 · Auswertung / Jury bewertet', value: '1' }, @@ -172,29 +151,15 @@ export const homeFields: Field[] = [ label: 'Slides', type: 'array', maxRows: 3, - defaultValue: [ - { - num: '01', tag: 'JETZT OFFEN', pulse: true, phase: 'Bewerbungsphase 2026', headline: 'BEWERBEN SIE SICH JETZT.', - body: 'Die Bewerbungsphase 2026 ist eröffnet – es ist noch nicht zu spät. Reichen Sie Ihre Unterlagen bis zum 30. Juni kostenlos ein und zeigen Sie, was den bayerischen Mittelstand ausmacht.', - cta: { label: 'Jetzt kostenlos bewerben', url: '/teilnahme' }, accent: '#EFBF04', bg: '#020A1E', glow: 'rgba(239,191,4,0.14)', meta: 'Deadline: 30. Juni 2026', membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, - }, - { - num: '02', tag: 'IN AUSWERTUNG', pulse: true, phase: 'Auswertungsphase 2026', headline: 'DIE JURY BEWERTET GERADE.', - body: 'Die Anmeldephase 2026 ist geschlossen. Unsere unabhängige Expertenjury aus Wissenschaft und Praxis sichtet und bewertet derzeit alle Einreichungen.', - accent: '#4A8FC9', bg: '#060D1C', glow: 'rgba(74,143,201,0.12)', meta: 'Ergebnis: August 2026', membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, - }, - { - num: '03', tag: 'ABGESCHLOSSEN', pulse: false, phase: 'Preisverleihung · Oktober 2026', headline: 'DER BMP 2026 WAR EIN ERFOLG.', - body: 'Wir danken allen 247 Bewerberinnen und Bewerbern für Mut und Exzellenz. Drei außergewöhnliche Unternehmen wurden in München ausgezeichnet – ein unvergesslicher Abend.', - cta: { label: 'Alle Preisträger ansehen', url: '/preistraeger' }, accent: '#EFBF04', bg: '#06090F', glow: 'rgba(239,191,4,0.08)', meta: 'Gala · Oktober 2026 · München', successApplications: '247', successWinners: '3', - }, - ], + defaultValue: homeContent.status.phases, fields: [ text('num', 'Slide number'), text('tag', 'Badge'), { name: 'pulse', label: 'Show pulse dot', type: 'checkbox', defaultValue: true }, text('phase', 'Phase label'), text('headline', 'Headline'), textarea('body', 'Body copy'), { name: 'cta', label: 'Slide CTA', type: 'group', fields: linkGroup('', '') }, text('accent', 'Accent color'), text('bg', 'Background color'), text('glow', 'Glow color'), text('meta', 'Meta label'), text('successApplications', 'Success applications number'), text('successWinners', 'Success winners number'), { name: 'membershipCta', label: 'Membership CTA', type: 'group', fields: linkGroup('Mitglied werden', '/mitglied-werden') }, ], }, - text('noActionLabel', 'No CTA fallback label', 'Kein Handlungsbedarf'), + text('noActionLabel', 'No CTA fallback label', homeContent.status.noActionLabel), + text('successApplicationsLabel', 'Success applications label', homeContent.status.successApplicationsLabel), + text('successWinnersLabel', 'Success winners label', homeContent.status.successWinnersLabel), ], }, { @@ -203,9 +168,12 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Blue card grid with featured winner relationships.'), fields: [ - text('eyebrow', 'Eyebrow', 'Success Stories'), - textarea('heading', 'Heading', 'DIE BESTEN UNTERNEHMEN IN BAYERN'), - { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup('Alle Preisträger', '/preistraeger') }, + text('eyebrow', 'Eyebrow', homeContent.winners.eyebrow), + textarea('heading', 'Heading', homeContent.winners.heading), + { name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.winners.cta.label, homeContent.winners.cta.url) }, + uploadField('fallbackImage', 'Fallback card image', `Current frontend image: /images/${homeContent.winners.fallbackImageFilename}`), + text('cardFallbackTitle', 'Fallback card title', homeContent.winners.cardFallbackTitle), + text('hoverLabel', 'Card hover label', homeContent.winners.hoverLabel), { name: 'featured', label: 'Featured winner cards', @@ -222,16 +190,13 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Portrait carousel and quote copy.'), fields: [ - text('eyebrow', 'Eyebrow', 'Stimmen der Preisträger'), - textarea('heading', 'Heading', 'DAS SAGEN UNSERE\nPREISTRÄGER.'), + text('eyebrow', 'Eyebrow', homeContent.testimonials.eyebrow), + textarea('heading', 'Heading', homeContent.testimonials.heading), { name: 'items', label: 'Testimonials', type: 'array', - defaultValue: [ - { imageUrl: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=600', name: 'Dr. Maria Huber', role: 'Geschäftsführerin', company: 'TechVision GmbH', year: '2025', quote: 'Der BMP hat uns neue Türen geöffnet. Die Auszeichnung ist ein wichtiges Qualitätssiegel für unser gesamtes Team.' }, - { imageUrl: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&q=80&w=600', name: 'Hannes Weber', role: 'Inhaber & CEO', company: 'Weber Präzision GmbH', year: '2023', quote: 'Der Gewinn war ein Moment des Stolzes für die gesamte Belegschaft. Die höchste Anerkennung für unsere tägliche Arbeit.' }, - ], + defaultValue: homeContent.testimonials.items, fields: [uploadField('image', 'Portrait image'), text('imageUrl', 'External image URL'), text('name', 'Name'), text('role', 'Role'), text('company', 'Company'), text('year', 'Year'), textarea('quote', 'Quote')], }, ], @@ -242,17 +207,13 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Cream left panel and networking image.'), fields: [ - text('eyebrow', 'Eyebrow', 'Mehr als nur ein Preis'), - textarea('heading', 'Heading', 'WARUM SICH DIE\nTEILNAHME LOHNT.'), - uploadField('image', 'Image', 'Current frontend image: /images/networking-innenhof.jpg'), - text('imageAlt', 'Image alt text', 'BMP Gala Netzwerk'), - text('imageLabel', 'Desktop image label', 'BMP Gala-Netzwerk'), + text('eyebrow', 'Eyebrow', homeContent.benefits.eyebrow), + textarea('heading', 'Heading', homeContent.benefits.heading), + uploadField('image', 'Image', `Current frontend image: /images/${homeContent.benefits.imageFilename}`), + text('imageAlt', 'Image alt text', homeContent.benefits.imageAlt), + text('imageLabel', 'Desktop image label', homeContent.benefits.imageLabel), { - name: 'items', label: 'Benefit rows', type: 'array', defaultValue: [ - { num: '01', title: 'Netzwerk', desc: 'Direkter Zugang zu Entscheidern aus Staatsregierung, Wirtschaft und Wissenschaft.' }, - { num: '02', title: 'Sichtbarkeit', desc: 'Breite mediale Begleitung durch TV-, Radio- und Print-Partner bayernweit.' }, - { num: '03', title: 'Bestätigung', desc: 'Ein unabhängiger Qualitätsnachweis – das stärkste Siegel für Ihren Marktauftritt.' }, - ], fields: [text('num', 'Number'), text('title', 'Title'), textarea('desc', 'Description')], + name: 'items', label: 'Benefit rows', type: 'array', defaultValue: homeContent.benefits.items, fields: [text('num', 'Number'), text('title', 'Title'), textarea('desc', 'Description')], }, ], }, @@ -262,22 +223,17 @@ export const homeFields: Field[] = [ type: 'group', admin: sectionAdmin('Final application pitch, award image, embedded form heading, and footnote.'), fields: [ - text('eyebrow', 'Eyebrow', 'Chance ergreifen'), - textarea('heading', 'Heading', 'SCHREIBEN\nAUCH SIE\nGESCHICHTE.'), - textarea('body', 'Body copy', 'Werden Sie Teil der Wall of Fame des bayerischen Mittelstands – vollständig kostenfrei.'), - { name: 'benefits', label: 'Benefit rows', type: 'array', defaultValue: [ - { num: '01', label: 'Kostenfrei', desc: 'Ohne Teilnahmegebühr' }, - { num: '02', label: 'Jury', desc: 'Aus Wissenschaft und Praxis' }, - { num: '03', label: 'Sichtbarkeit', desc: 'Medial bayernweit' }, - { num: '04', label: 'Gala', desc: 'Verleihung München' }, - ], fields: [text('num', 'Number'), text('label', 'Label'), text('desc', 'Description')] }, - uploadField('awardImage', 'Award image', 'Current frontend image: /images/preisuebergabe.jpg'), - text('awardImageAlt', 'Award image alt text', 'Preisverleihung Bayerischer Mittelstandspreis'), - text('awardCaption', 'Award image caption', 'Preisverleihung 2025'), - text('formEyebrow', 'Form eyebrow', 'Direktbewerbung 2026'), - text('formTitle', 'Form title', 'Kostenlos bewerben'), - text('footnote', 'Footnote normal text', 'Kostenlos und unverbindlich –'), - text('footnoteStrong', 'Footnote highlighted text', 'keine Teilnahmegebühr'), + text('eyebrow', 'Eyebrow', homeContent.application.eyebrow), + textarea('heading', 'Heading', homeContent.application.heading), + textarea('body', 'Body copy', homeContent.application.body), + { name: 'benefits', label: 'Benefit rows', type: 'array', defaultValue: homeContent.application.benefits, fields: [text('num', 'Number'), text('label', 'Label'), text('desc', 'Description')] }, + uploadField('awardImage', 'Award image', `Current frontend image: /images/${homeContent.application.awardImageFilename}`), + text('awardImageAlt', 'Award image alt text', homeContent.application.awardImageAlt), + text('awardCaption', 'Award image caption', homeContent.application.awardCaption), + text('formEyebrow', 'Form eyebrow', homeContent.application.formEyebrow), + text('formTitle', 'Form title', homeContent.application.formTitle), + text('footnote', 'Footnote normal text', homeContent.application.footnote), + text('footnoteStrong', 'Footnote highlighted text', homeContent.application.footnoteStrong), ], }, { @@ -285,7 +241,7 @@ export const homeFields: Field[] = [ label: '09 · Video modal placeholder', type: 'group', admin: sectionAdmin('Copy shown when the hero video button is clicked.'), - fields: [text('title', 'Title', 'Video-Player Platzhalter'), text('description', 'Description', 'Hier würde das Image-Video des BMP geladen werden.'), text('closeLabel', 'Close button label', 'Schließen')], + fields: [text('title', 'Title', homeContent.videoModal.title), text('description', 'Description', homeContent.videoModal.description), text('closeLabel', 'Close button label', homeContent.videoModal.closeLabel)], }, ], }, diff --git a/src/payload-types.ts b/src/payload-types.ts index 0830ad9..ac6c75c 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -324,6 +324,8 @@ export interface Page { }[] | null; noActionLabel?: string | null; + successApplicationsLabel?: string | null; + successWinnersLabel?: string | null; }; /** * Blue card grid with featured winner relationships. @@ -335,6 +337,12 @@ export interface Page { label?: string | null; url?: string | null; }; + /** + * Current frontend image: /images/gala-saal-overview.jpg + */ + fallbackImage?: (number | null) | Media; + cardFallbackTitle?: string | null; + hoverLabel?: string | null; /** * Choose the winner cards shown on the home page. If empty, the latest 2025 winners are used. */ @@ -3148,6 +3156,8 @@ export interface PagesSelect { id?: T; }; noActionLabel?: T; + successApplicationsLabel?: T; + successWinnersLabel?: T; }; winners?: | T @@ -3160,6 +3170,9 @@ export interface PagesSelect { label?: T; url?: T; }; + fallbackImage?: T; + cardFallbackTitle?: T; + hoverLabel?: T; featured?: T; }; testimonials?: diff --git a/src/scripts/preload-home-page-cms.ts b/src/scripts/preload-home-page-cms.ts index fb05922..adc5a7a 100644 --- a/src/scripts/preload-home-page-cms.ts +++ b/src/scripts/preload-home-page-cms.ts @@ -3,6 +3,8 @@ import 'dotenv/config' import config from '@payload-config' import { getPayload, type Payload } from 'payload' +import { homeContent } from '@/spa/homeContent' + const mediaByFilename = async (payload: Payload, filename: string) => { const result = await payload.find({ collection: 'media', @@ -31,12 +33,19 @@ async function main() { if (!page) throw new Error('Home page not found. Expected spaPath=/ or slug=startseite/home.') const [heroImage, quickImage, introImage, benefitsImage, awardImage] = await Promise.all([ - mediaByFilename(payload, 'gala-saal-overview.jpg'), - mediaByFilename(payload, 'buehne-gewinner.jpg'), - mediaByFilename(payload, 'preistraeger-jubel.jpg'), - mediaByFilename(payload, 'networking-innenhof.jpg'), - mediaByFilename(payload, 'preisuebergabe.jpg'), + mediaByFilename(payload, homeContent.hero.backgroundImageFilename), + mediaByFilename(payload, homeContent.quickCheck.imageFilename), + mediaByFilename(payload, homeContent.intro.imageFilename), + mediaByFilename(payload, homeContent.benefits.imageFilename), + mediaByFilename(payload, homeContent.application.awardImageFilename), ]) + const fallbackWinnerImage = await mediaByFilename(payload, homeContent.winners.fallbackImageFilename) + const { backgroundImageFilename: _heroFilename, ...heroContent } = homeContent.hero + const { imageFilename: _quickFilename, ...quickCheckContent } = homeContent.quickCheck + const { imageFilename: _introFilename, ...introContent } = homeContent.intro + const { fallbackImageFilename: _winnerFallbackFilename, ...winnersContent } = homeContent.winners + const { imageFilename: _benefitsFilename, ...benefitsContent } = homeContent.benefits + const { awardImageFilename: _awardFilename, ...applicationContent } = homeContent.application const winners = await payload.find({ collection: 'preistraeger', @@ -54,78 +63,35 @@ async function main() { data: { home: { hero: { + ...heroContent, backgroundImage: heroImage, - imageAlt: 'Awards Gala', - badge: 'Bewerbungsphase 2026 geschlossen', - headingLine1: 'BAYERNS BESTE', - headingAccent: 'MITTELSTÄNDLER.', - description: 'Der Bayerische Mittelstandspreis würdigt herausragende Leistungen – von Innovation über Nachhaltigkeit bis hin zur Exzellenz.', - primaryCta: { label: 'Jetzt kostenlos bewerben', url: '/teilnahme' }, - secondaryCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, - videoLabel: 'Film abspielen', - partnersLabel: 'Unsere Partner', - partners: [ - { name: 'RSM Ebner Stolz' }, { name: 'WWK' }, { name: 'Deutsche Bank' }, { name: 'Bionorica' }, - { name: 'Berchtesgadener Land' }, { name: 'Metzler' }, { name: 'vbw' }, { name: 'Wirtschaftsbeirat Bayern' }, - { name: 'Radio Gong 96.3' }, { name: 'münchen.tv' }, { name: 'EWS' }, - ], }, quickCheck: { - eyebrow: 'Schnell-Check', heading: 'SIND SIE\nBERECHTIGT?', image: quickImage, imageAlt: 'Preisverleihung Bühne', - body: 'Der Bayerische Mittelstandspreis richtet sich an inhabergeführte KMU im Freistaat. Fünf Kriterien entscheiden, erfüllen Sie alle fünf, steht Ihrer Bewerbung nichts im Weg.', - criteria: [ - { label: 'Standort Bayern', desc: 'Sitz oder Betriebsstätte im Freistaat' }, - { label: '50–1.500 Mitarbeiter', desc: 'Klassischer Mittelstand' }, - { label: 'Min. 3 Geschäftsjahre', desc: 'Markterfahrung und Stabilität' }, - { label: 'Privatwirtschaft', desc: 'Kein staatlicher Träger' }, - { label: 'Familien- bzw. eigentümergeführt', desc: 'Ihr Unternehmen ist inhabergeführt' }, - ], - note: 'Auch ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen mit Schwerpunkt in Bayern kann teilnehmen.', - cta: { label: 'Ihr Weg zum Preis', url: '/teilnahme' }, + ...quickCheckContent, + image: quickImage, }, intro: { - eyebrow: 'Ein Gewinn für alle', heading: 'AUSZEICHNUNG\nFÜR DIE BESTEN.', image: introImage, imageAlt: 'BMP Auszeichnung', - stats: [{ value: '20+', label: 'Jahre Exzellenz' }, { value: '0 €', label: 'Teilnahmegebühr' }, { value: '100%', label: 'Unabhängig' }], - quote: '„Ein Unternehmen zu gründen erfordert nicht allein spezielle Fähigkeiten. Es erfordert Persönlichkeit!“', - body: 'Wir suchen Vorbilder, die sich trotz aller Risiken nicht scheuen, Visionen in Businesspläne und Ideen in Unternehmungen zu verwandeln, und das mit großem persönlichen Einsatz und Verantwortung für Ihre Mitarbeiter. Seit vielen Jahren würdigen wir herausragende unternehmerische Leistungen im Freistaat.', - cta: { label: 'Mehr über den Preis', url: '/der-bmp' }, + ...introContent, + image: introImage, }, - status: { - activePhase: '0', - noActionLabel: 'Kein Handlungsbedarf', - phases: [ - { num: '01', tag: 'JETZT OFFEN', pulse: true, phase: 'Bewerbungsphase 2026', headline: 'BEWERBEN SIE SICH JETZT.', body: 'Die Bewerbungsphase 2026 ist eröffnet – es ist noch nicht zu spät. Reichen Sie Ihre Unterlagen bis zum 30. Juni kostenlos ein und zeigen Sie, was den bayerischen Mittelstand ausmacht.', cta: { label: 'Jetzt kostenlos bewerben', url: '/teilnahme' }, accent: '#EFBF04', bg: '#020A1E', glow: 'rgba(239,191,4,0.14)', meta: 'Deadline: 30. Juni 2026', membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' } }, - { num: '02', tag: 'IN AUSWERTUNG', pulse: true, phase: 'Auswertungsphase 2026', headline: 'DIE JURY BEWERTET GERADE.', body: 'Die Anmeldephase 2026 ist geschlossen. Unsere unabhängige Expertenjury aus Wissenschaft und Praxis sichtet und bewertet derzeit alle Einreichungen.', accent: '#4A8FC9', bg: '#060D1C', glow: 'rgba(74,143,201,0.12)', meta: 'Ergebnis: August 2026', membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' } }, - { num: '03', tag: 'ABGESCHLOSSEN', pulse: false, phase: 'Preisverleihung · Oktober 2026', headline: 'DER BMP 2026 WAR EIN ERFOLG.', body: 'Wir danken allen 247 Bewerberinnen und Bewerbern für Mut und Exzellenz. Drei außergewöhnliche Unternehmen wurden in München ausgezeichnet – ein unvergesslicher Abend.', cta: { label: 'Alle Preisträger ansehen', url: '/preistraeger' }, accent: '#EFBF04', bg: '#06090F', glow: 'rgba(239,191,4,0.08)', meta: 'Gala · Oktober 2026 · München', successApplications: '247', successWinners: '3' }, - ], - }, - winners: { eyebrow: 'Success Stories', heading: 'DIE BESTEN UNTERNEHMEN IN BAYERN', cta: { label: 'Alle Preisträger', url: '/preistraeger' }, featured: winners.docs.map((doc) => doc.id) }, - testimonials: { - eyebrow: 'Stimmen der Preisträger', heading: 'DAS SAGEN UNSERE\nPREISTRÄGER.', - items: [ - { imageUrl: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=600', name: 'Dr. Maria Huber', role: 'Geschäftsführerin', company: 'TechVision GmbH', year: '2025', quote: 'Der BMP hat uns neue Türen geöffnet. Die Auszeichnung ist ein wichtiges Qualitätssiegel für unser gesamtes Team.' }, - { imageUrl: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&q=80&w=600', name: 'Hannes Weber', role: 'Inhaber & CEO', company: 'Weber Präzision GmbH', year: '2023', quote: 'Der Gewinn war ein Moment des Stolzes für die gesamte Belegschaft. Die höchste Anerkennung für unsere tägliche Arbeit.' }, - { imageUrl: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&q=80&w=600', name: 'Lisa Berger', role: 'Gründerin & CEO', company: 'EcoTech Bavaria', year: '2024', quote: 'Das Netzwerk, das wir durch den BMP gewonnen haben, ist unbezahlbar. Wir fanden Partner, die unsere Werte teilen.' }, - { imageUrl: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=600', name: 'Dr. Michael Schuh', role: 'Vorstand', company: 'Bayerischer Maschinenbau', year: '2024', quote: 'Der Preis hat unsere Sichtbarkeit weit über die bayerischen Grenzen hinaus gestärkt. Ein echtes Premium-Siegel.' }, - { imageUrl: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&q=80&w=600', name: 'Anna Kellner', role: 'Mitgründerin', company: 'AlpinTech Solutions', year: '2024', quote: 'Der BMP hat unsere Employer Brand transformiert. Wir gewinnen heute die besten Talente, weil wir Preisträger sind.' }, - ], + status: homeContent.status, + winners: { + ...winnersContent, + fallbackImage: fallbackWinnerImage, + featured: winners.docs.map((doc) => doc.id), }, + testimonials: homeContent.testimonials, benefits: { - eyebrow: 'Mehr als nur ein Preis', heading: 'WARUM SICH DIE\nTEILNAHME LOHNT.', image: benefitsImage, imageAlt: 'BMP Gala Netzwerk', imageLabel: 'BMP Gala-Netzwerk', - items: [ - { num: '01', title: 'Netzwerk', desc: 'Direkter Zugang zu Entscheidern aus Staatsregierung, Wirtschaft und Wissenschaft.' }, - { num: '02', title: 'Sichtbarkeit', desc: 'Breite mediale Begleitung durch TV-, Radio- und Print-Partner bayernweit.' }, - { num: '03', title: 'Bestätigung', desc: 'Ein unabhängiger Qualitätsnachweis – das stärkste Siegel für Ihren Marktauftritt.' }, - ], + ...benefitsContent, + image: benefitsImage, }, application: { - eyebrow: 'Chance ergreifen', heading: 'SCHREIBEN\nAUCH SIE\nGESCHICHTE.', body: 'Werden Sie Teil der Wall of Fame des bayerischen Mittelstands – vollständig kostenfrei.', - benefits: [{ num: '01', label: 'Kostenfrei', desc: 'Ohne Teilnahmegebühr' }, { num: '02', label: 'Jury', desc: 'Aus Wissenschaft und Praxis' }, { num: '03', label: 'Sichtbarkeit', desc: 'Medial bayernweit' }, { num: '04', label: 'Gala', desc: 'Verleihung München' }], - awardImage, awardImageAlt: 'Preisverleihung Bayerischer Mittelstandspreis', awardCaption: 'Preisverleihung 2025', formEyebrow: 'Direktbewerbung 2026', formTitle: 'Kostenlos bewerben', footnote: 'Kostenlos und unverbindlich –', footnoteStrong: 'keine Teilnahmegebühr', + ...applicationContent, + awardImage, }, - videoModal: { title: 'Video-Player Platzhalter', description: 'Hier würde das Image-Video des BMP geladen werden.', closeLabel: 'Schließen' }, + videoModal: homeContent.videoModal, }, - }, + } as never, }) payload.logger.info(`Preloaded home CMS fields for page ${page.id}`) diff --git a/src/spa/homeContent.ts b/src/spa/homeContent.ts new file mode 100644 index 0000000..ff96826 --- /dev/null +++ b/src/spa/homeContent.ts @@ -0,0 +1,217 @@ +export const homeContent = { + hero: { + backgroundImageFilename: 'gala-saal-overview.jpg', + imageAlt: 'Awards Gala', + badge: 'Bewerbungsphase 2026 geschlossen', + headingLine1: 'BAYERNS BESTE', + headingAccent: 'MITTELSTÄNDLER.', + description: + 'Der Bayerische Mittelstandspreis würdigt herausragende Leistungen – von Innovation über Nachhaltigkeit bis hin zur Exzellenz.', + primaryCta: { label: 'Jetzt kostenlos bewerben', url: '/teilnahme' }, + secondaryCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, + videoLabel: 'Film abspielen', + partnersLabel: 'Unsere Partner', + partners: [ + { name: 'RSM Ebner Stolz' }, + { name: 'WWK' }, + { name: 'Deutsche Bank' }, + { name: 'Bionorica' }, + { name: 'Berchtesgadener Land' }, + { name: 'Metzler' }, + { name: 'vbw' }, + { name: 'Wirtschaftsbeirat Bayern' }, + { name: 'Radio Gong 96.3' }, + { name: 'münchen.tv' }, + { name: 'EWS' }, + ], + }, + quickCheck: { + eyebrow: 'Schnell-Check', + heading: 'SIND SIE\nBERECHTIGT?', + imageFilename: 'buehne-gewinner.jpg', + imageAlt: 'Preisverleihung Bühne', + body: + 'Der Bayerische Mittelstandspreis richtet sich an inhabergeführte KMU im Freistaat. Fünf Kriterien entscheiden, erfüllen Sie alle fünf, steht Ihrer Bewerbung nichts im Weg.', + criteria: [ + { label: 'Standort Bayern', desc: 'Sitz oder Betriebsstätte im Freistaat' }, + { label: '50–1.500 Mitarbeiter', desc: 'Klassischer Mittelstand' }, + { label: 'Min. 3 Geschäftsjahre', desc: 'Markterfahrung und Stabilität' }, + { label: 'Privatwirtschaft', desc: 'Kein staatlicher Träger' }, + { label: 'Familien- bzw. eigentümergeführt', desc: 'Ihr Unternehmen ist inhabergeführt' }, + ], + note: + 'Auch ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen mit Schwerpunkt in Bayern kann teilnehmen.', + cta: { label: 'Ihr Weg zum Preis', url: '/teilnahme' }, + }, + intro: { + eyebrow: 'Ein Gewinn für alle', + heading: 'AUSZEICHNUNG\nFÜR DIE BESTEN.', + imageFilename: 'preistraeger-jubel.jpg', + imageAlt: 'BMP Auszeichnung', + stats: [ + { value: '20+', label: 'Jahre Exzellenz' }, + { value: '0 €', label: 'Teilnahmegebühr' }, + { value: '100%', label: 'Unabhängig' }, + ], + quote: '„Ein Unternehmen zu gründen erfordert nicht allein spezielle Fähigkeiten. Es erfordert Persönlichkeit!“', + body: + 'Wir suchen Vorbilder, die sich trotz aller Risiken nicht scheuen, Visionen in Businesspläne und Ideen in Unternehmungen zu verwandeln, und das mit großem persönlichen Einsatz und Verantwortung für Ihre Mitarbeiter. Seit vielen Jahren würdigen wir herausragende unternehmerische Leistungen im Freistaat.', + cta: { label: 'Mehr über den Preis', url: '/der-bmp' }, + }, + status: { + activePhase: '0', + noActionLabel: 'Kein Handlungsbedarf', + successApplicationsLabel: 'Bewerbungen', + successWinnersLabel: 'Preisträger', + phases: [ + { + num: '01', + tag: 'JETZT OFFEN', + pulse: true, + phase: 'Bewerbungsphase 2026', + headline: 'BEWERBEN SIE SICH JETZT.', + body: + 'Die Bewerbungsphase 2026 ist eröffnet – es ist noch nicht zu spät. Reichen Sie Ihre Unterlagen bis zum 30. Juni kostenlos ein und zeigen Sie, was den bayerischen Mittelstand ausmacht.', + cta: { label: 'Jetzt kostenlos bewerben', url: '/teilnahme' }, + accent: '#EFBF04', + bg: '#020A1E', + glow: 'rgba(239,191,4,0.14)', + meta: 'Deadline: 30. Juni 2026', + membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, + }, + { + num: '02', + tag: 'IN AUSWERTUNG', + pulse: true, + phase: 'Auswertungsphase 2026', + headline: 'DIE JURY BEWERTET GERADE.', + body: + 'Die Anmeldephase 2026 ist geschlossen. Unsere unabhängige Expertenjury aus Wissenschaft und Praxis sichtet und bewertet derzeit alle Einreichungen.', + cta: null, + accent: '#4A8FC9', + bg: '#060D1C', + glow: 'rgba(74,143,201,0.12)', + meta: 'Ergebnis: August 2026', + membershipCta: { label: 'Mitglied werden', url: '/mitglied-werden' }, + }, + { + num: '03', + tag: 'ABGESCHLOSSEN', + pulse: false, + phase: 'Preisverleihung · Oktober 2026', + headline: 'DER BMP 2026 WAR EIN ERFOLG.', + body: + 'Wir danken allen 247 Bewerberinnen und Bewerbern für Mut und Exzellenz. Drei außergewöhnliche Unternehmen wurden in München ausgezeichnet – ein unvergesslicher Abend.', + cta: { label: 'Alle Preisträger ansehen', url: '/preistraeger' }, + accent: '#EFBF04', + bg: '#06090F', + glow: 'rgba(239,191,4,0.08)', + meta: 'Gala · Oktober 2026 · München', + successApplications: '247', + successWinners: '3', + }, + ], + }, + winners: { + eyebrow: 'Success Stories', + heading: 'DIE BESTEN UNTERNEHMEN IN BAYERN', + cta: { label: 'Alle Preisträger', url: '/preistraeger' }, + fallbackImageFilename: 'gala-saal-overview.jpg', + cardFallbackTitle: 'Preisträger', + hoverLabel: 'Detail →', + }, + testimonials: { + eyebrow: 'Stimmen der Preisträger', + heading: 'DAS SAGEN UNSERE\nPREISTRÄGER.', + items: [ + { + imageUrl: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=600', + name: 'Dr. Maria Huber', + role: 'Geschäftsführerin', + company: 'TechVision GmbH', + year: '2025', + quote: 'Der BMP hat uns neue Türen geöffnet. Die Auszeichnung ist ein wichtiges Qualitätssiegel für unser gesamtes Team.', + }, + { + imageUrl: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&q=80&w=600', + name: 'Hannes Weber', + role: 'Inhaber & CEO', + company: 'Weber Präzision GmbH', + year: '2023', + quote: 'Der Gewinn war ein Moment des Stolzes für die gesamte Belegschaft. Die höchste Anerkennung für unsere tägliche Arbeit.', + }, + { + imageUrl: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&q=80&w=600', + name: 'Lisa Berger', + role: 'Gründerin & CEO', + company: 'EcoTech Bavaria', + year: '2024', + quote: 'Das Netzwerk, das wir durch den BMP gewonnen haben, ist unbezahlbar. Wir fanden Partner, die unsere Werte teilen.', + }, + { + imageUrl: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=600', + name: 'Dr. Michael Schuh', + role: 'Vorstand', + company: 'Bayerischer Maschinenbau', + year: '2024', + quote: 'Der Preis hat unsere Sichtbarkeit weit über die bayerischen Grenzen hinaus gestärkt. Ein echtes Premium-Siegel.', + }, + { + imageUrl: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&q=80&w=600', + name: 'Anna Kellner', + role: 'Mitgründerin', + company: 'AlpinTech Solutions', + year: '2024', + quote: + 'Der BMP hat unsere Employer Brand transformiert. Wir gewinnen heute die besten Talente, weil wir Preisträger sind.', + }, + ], + }, + benefits: { + eyebrow: 'Mehr als nur ein Preis', + heading: 'WARUM SICH DIE\nTEILNAHME LOHNT.', + imageFilename: 'networking-innenhof.jpg', + imageAlt: 'BMP Gala Netzwerk', + imageLabel: 'BMP Gala-Netzwerk', + items: [ + { + num: '01', + title: 'Netzwerk', + desc: 'Direkter Zugang zu Entscheidern aus Staatsregierung, Wirtschaft und Wissenschaft.', + }, + { + num: '02', + title: 'Sichtbarkeit', + desc: 'Breite mediale Begleitung durch TV-, Radio- und Print-Partner bayernweit.', + }, + { + num: '03', + title: 'Bestätigung', + desc: 'Ein unabhängiger Qualitätsnachweis – das stärkste Siegel für Ihren Marktauftritt.', + }, + ], + }, + application: { + eyebrow: 'Chance ergreifen', + heading: 'SCHREIBEN\nAUCH SIE\nGESCHICHTE.', + body: 'Werden Sie Teil der Wall of Fame des bayerischen Mittelstands – vollständig kostenfrei.', + benefits: [ + { num: '01', label: 'Kostenfrei', desc: 'Ohne Teilnahmegebühr' }, + { num: '02', label: 'Jury', desc: 'Aus Wissenschaft und Praxis' }, + { num: '03', label: 'Sichtbarkeit', desc: 'Medial bayernweit' }, + { num: '04', label: 'Gala', desc: 'Verleihung München' }, + ], + awardImageFilename: 'preisuebergabe.jpg', + awardImageAlt: 'Preisverleihung Bayerischer Mittelstandspreis', + awardCaption: 'Preisverleihung 2025', + formEyebrow: 'Direktbewerbung 2026', + formTitle: 'Kostenlos bewerben', + footnote: 'Kostenlos und unverbindlich –', + footnoteStrong: 'keine Teilnahmegebühr', + }, + videoModal: { + title: 'Video-Player Platzhalter', + description: 'Hier würde das Image-Video des BMP geladen werden.', + closeLabel: 'Schließen', + }, +} diff --git a/src/spa/pages/Home.tsx b/src/spa/pages/Home.tsx index 323070a..8fbbf2a 100644 --- a/src/spa/pages/Home.tsx +++ b/src/spa/pages/Home.tsx @@ -11,125 +11,63 @@ import { useIsMobile } from '@/spa/hooks/useIsMobile'; import Image from '@/spa/components/ui/UnoptimizedImage' import { useCmsCollection, useCmsRoute } from '@/spa/cmsRoute' import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField' - -const PARTNERS = [ - { id: 'rsm', label: <>RSM
Ebner Stolz }, - { id: 'wwk', label: WWK }, - { id: 'db', label: Deutsche Bank }, - { id: 'bio', label: Bionorica }, - { id: 'bcl', label: Berchtesgadener
Land
}, - { id: 'mtz', label: Metzler }, - { id: 'vbw', label: vbw }, - { id: 'wby', label: Wirtschafts-
beirat Bayern
}, - { id: 'rgong', label: Radio Gong 96.3 }, - { id: 'mtvr', label: münchen.tv }, - { id: 'ews', label: EWS }, -]; +import { homeContent } from '@/spa/homeContent' const FF = '"IBM Plex Sans", sans-serif'; +type CmsRecord = Record; type HomeCms = Record; +type StatusPhase = HomeCms & { + cta?: { label?: string; to?: string; url?: string } | null; + membershipCta?: { label?: string; to?: string; url?: string } | null; +}; +type WinnerCardData = CmsRecord & { + id?: string | number; + slug?: string; + title?: string; + name?: string; + img?: string; + image?: unknown; + spaPath?: string; + year?: string | number; + awardType?: string; + type?: string; + category?: string; +}; const fallbackText = (value: unknown, fallback: string) => typeof value === 'string' && value.length > 0 ? value : fallback; -const fallbackArray = (value: unknown, fallback: T[]) => +const fallbackArray = (value: unknown, fallback: readonly T[]) => Array.isArray(value) && value.length > 0 ? (value as T[]) : fallback; function Lines({ text }: { text: string }) { return <>{text.split('\n').map((line, i) => {i > 0 &&
}{line}
)}; } -const DEFAULT_QUICK_CRITERIA = [ - { label: 'Standort Bayern', desc: 'Sitz oder Betriebsstätte im Freistaat' }, - { label: '50–1.500 Mitarbeiter', desc: 'Klassischer Mittelstand' }, - { label: 'Min. 3 Geschäftsjahre', desc: 'Markterfahrung und Stabilität' }, - { label: 'Privatwirtschaft', desc: 'Kein staatlicher Träger' }, - { label: 'Familien- bzw. eigentümergeführt', desc: 'Ihr Unternehmen ist inhabergeführt' }, -]; - -const DEFAULT_INTRO_STATS = [['20+', 'Jahre Exzellenz'], ['0 €', 'Teilnahmegebühr'], ['100%', 'Unabhängig']]; - -const DEFAULT_BENEFITS = [ - { num: '01', title: 'Netzwerk', desc: 'Direkter Zugang zu Entscheidern aus Staatsregierung, Wirtschaft und Wissenschaft.' }, - { num: '02', title: 'Sichtbarkeit', desc: 'Breite mediale Begleitung durch TV-, Radio- und Print-Partner bayernweit.' }, - { num: '03', title: 'Bestätigung', desc: 'Ein unabhängiger Qualitätsnachweis – das stärkste Siegel für Ihren Marktauftritt.' }, -]; - -const DEFAULT_APPLICATION_BENEFITS = [ - { num: '01', label: 'Kostenfrei', desc: 'Ohne Teilnahmegebühr' }, - { num: '02', label: 'Jury', desc: 'Aus Wissenschaft und Praxis' }, - { num: '03', label: 'Sichtbarkeit', desc: 'Medial bayernweit' }, - { num: '04', label: 'Gala', desc: 'Verleihung München' }, -]; - -// ─── Status Phase Slider ──────────────────────────────────────────────────── -/** - * 🔧 DEPLOYMENT CONFIG - * Setze ACTIVE_PHASE je nach aktuellem Stand des BMP-Zyklus: - * 0 = Bewerbungsphase offen - * 1 = Auswertungsphase (Jury bewertet) - * 2 = Nach der Preisverleihung (Erfolg) - */ -const ACTIVE_PHASE: 0 | 1 | 2 = 0; - -interface StatusPhase { - num: string; - tag: string; - pulse: boolean; - phase: string; - headline: string; - body: string; - cta: { label: string; to: string } | null; - accent: string; - bg: string; - glow: string; - meta: string; +function partnerLogos(partners: unknown) { + return fallbackArray(partners, homeContent.hero.partners).map((partner) => { + const item = partner as { id?: string; name?: string; label?: React.ReactNode }; + return item.label + ? { id: item.id || String(item.name || ''), label: item.label } + : { + id: item.id || String(item.name || ''), + label: {item.name}, + }; + }); } -const STATUS_PHASES: StatusPhase[] = [ - { - num: '01', - tag: 'JETZT OFFEN', - pulse: true, - phase: 'Bewerbungsphase 2026', - headline: 'BEWERBEN SIE SICH JETZT.', - body: 'Die Bewerbungsphase 2026 ist eröffnet – es ist noch nicht zu spät. Reichen Sie Ihre Unterlagen bis zum 30. Juni kostenlos ein und zeigen Sie, was den bayerischen Mittelstand ausmacht.', - cta: { label: 'Jetzt kostenlos bewerben', to: '/teilnahme' }, - accent: '#EFBF04', - bg: '#020A1E', - glow: 'rgba(239,191,4,0.14)', - meta: 'Deadline: 30. Juni 2026', - }, - { - num: '02', - tag: 'IN AUSWERTUNG', - pulse: true, - phase: 'Auswertungsphase 2026', - headline: 'DIE JURY BEWERTET GERADE.', - body: 'Die Anmeldephase 2026 ist geschlossen. Unsere unabhängige Expertenjury aus Wissenschaft und Praxis sichtet und bewertet derzeit alle Einreichungen.', - cta: null, - accent: '#4A8FC9', - bg: '#060D1C', - glow: 'rgba(74,143,201,0.12)', - meta: 'Ergebnis: August 2026', - }, - { - num: '03', - tag: 'ABGESCHLOSSEN', - pulse: false, - phase: 'Preisverleihung · Oktober 2026', - headline: 'DER BMP 2026 WAR EIN ERFOLG.', - body: 'Wir danken allen 247 Bewerberinnen und Bewerbern für Mut und Exzellenz. Drei außergewöhnliche Unternehmen wurden in München ausgezeichnet – ein unvergesslicher Abend.', - cta: { label: 'Alle Preisträger ansehen', to: '/preistraeger' }, - accent: '#EFBF04', - bg: '#06090F', - glow: 'rgba(239,191,4,0.08)', - meta: 'Gala · Oktober 2026 · München', - }, -]; - -function HomeWinnerCard({ winner }: { winner: any }) { +function HomeWinnerCard({ + winner, + fallbackTitle, + fallbackImage, + hoverLabel, +}: { + winner: WinnerCardData; + fallbackTitle: string; + fallbackImage: string; + hoverLabel: string; +}) { const [hovered, setHovered] = useState(false); const isMobile = useIsMobile(); const staticWinner = WINNERS.find( @@ -139,8 +77,8 @@ function HomeWinnerCard({ winner }: { winner: any }) { item.name === winner?.title || item.name === winner?.name, ); - const title = fallbackText(winner?.title, fallbackText(winner?.name, staticWinner?.name || 'Preisträger')); - const imageSrc = mediaUrl(winner?.image, winner?.img || staticWinner?.img || '/images/gala-saal-overview.jpg'); + const title = fallbackText(winner?.title, fallbackText(winner?.name, staticWinner?.name || fallbackTitle)); + const imageSrc = mediaUrl(winner?.image, winner?.img || staticWinner?.img || fallbackImage); const href = winner?.spaPath || (winner?.slug ? `/preistraeger/${winner.slug}` : staticWinner ? `/preistraeger/${staticWinner.slug}` : '/preistraeger'); return ( {title}

{winner?.category || staticWinner?.category}

- Detail → + {hoverLabel}
@@ -182,22 +120,21 @@ const Home: React.FC = () => { const cmsPreistraeger = useCmsCollection('preistraeger'); const selectedWinners = fallbackArray(winnersSection.featured, []); const selectedWinnerDocs = selectedWinners - .map((selected: any) => { - const selectedID = typeof selected === 'object' ? selected.id : selected - const listDoc = cmsPreistraeger.find((doc: any) => String(doc.id) === String(selectedID)) + .map((selected) => { + const selectedRecord = selected as { id?: string | number } + const selectedID = typeof selected === 'object' && selected !== null ? selectedRecord.id : selected + const listDoc = cmsPreistraeger.find((doc) => String((doc as { id?: string | number }).id) === String(selectedID)) return listDoc || (typeof selected === 'object' ? selected : undefined) }) - .filter(Boolean) + .filter(Boolean) const homeWinners = selectedWinnerDocs.length ? selectedWinnerDocs - : (cmsPreistraeger.length ? cmsPreistraeger : WINNERS).filter((w: any) => Number(w.year) === 2025).slice(0, 8); - const quickCriteria = fallbackArray(quickCheck.criteria, DEFAULT_QUICK_CRITERIA); - const introStats = fallbackArray( - intro.stats, - DEFAULT_INTRO_STATS.map(([value, label]) => ({ value, label })), - ); - const benefitItems = fallbackArray(benefits.items, DEFAULT_BENEFITS); - const applicationBenefits = fallbackArray(application.benefits, DEFAULT_APPLICATION_BENEFITS); + : (cmsPreistraeger.length ? cmsPreistraeger : WINNERS).filter((w) => Number((w as { year?: string | number }).year) === 2025).slice(0, 8); + const quickCriteria = fallbackArray(quickCheck.criteria, homeContent.quickCheck.criteria); + const introStats = fallbackArray(intro.stats, homeContent.intro.stats); + const benefitItems = fallbackArray(benefits.items, homeContent.benefits.items); + const applicationBenefits = fallbackArray(application.benefits, homeContent.application.benefits); + const winnerFallbackImage = mediaUrl(winnersSection.fallbackImage, `/images/${homeContent.winners.fallbackImageFilename}`); return (
@@ -205,8 +142,8 @@ const Home: React.FC = () => {
{mediaAlt(hero.backgroundImage, @@ -217,35 +154,35 @@ const Home: React.FC = () => {
- {fallbackText(hero.badge, 'Bewerbungsphase 2026 geschlossen')} + {fallbackText(hero.badge, homeContent.hero.badge)}

- {fallbackText(hero.headingLine1, 'BAYERNS BESTE')}
- {fallbackText(hero.headingAccent, 'MITTELSTÄNDLER.')} + {fallbackText(hero.headingLine1, homeContent.hero.headingLine1)}
+ {fallbackText(hero.headingAccent, homeContent.hero.headingAccent)}

- {fallbackText(hero.description, 'Der Bayerische Mittelstandspreis würdigt herausragende Leistungen – von Innovation über Nachhaltigkeit bis hin zur Exzellenz.')} + {fallbackText(hero.description, homeContent.hero.description)}

{ (e.currentTarget as HTMLElement).style.background = '#FFD130'; (e.currentTarget as HTMLElement).style.boxShadow = '0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)'; }} onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = '#EFBF04'; (e.currentTarget as HTMLElement).style.boxShadow = 'none'; }} > - {fallbackText(hero.primaryCta?.label, 'Jetzt kostenlos bewerben')} + {fallbackText(hero.primaryCta?.label, homeContent.hero.primaryCta.label)}
{ (e.currentTarget as HTMLElement).style.borderColor = '#EFBF04'; (e.currentTarget as HTMLElement).style.color = '#EFBF04'; }} onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(255,255,255,0.35)'; (e.currentTarget as HTMLElement).style.color = 'rgba(255,255,255,0.85)'; }} > - {fallbackText(hero.secondaryCta?.label, 'Mitglied werden')} + {fallbackText(hero.secondaryCta?.label, homeContent.hero.secondaryCta.label)}
@@ -266,12 +203,12 @@ const Home: React.FC = () => {
{/* Left: label – stays put */} - {fallbackText(hero.partnersLabel, 'Unsere Partner')} + {fallbackText(hero.partnersLabel, homeContent.hero.partnersLabel)} {/* Right: ticker – fixed window, exactly 4 logos wide */}
- partner.label ? partner : { id: partner.id || partner.name, label: {partner.name} })} /> +
@@ -287,15 +224,15 @@ const Home: React.FC = () => { {/* Eyebrow + heading – desktop only (on mobile these are overlaid on the image) */} {!isMobile && ( <> - {fallbackText(quickCheck.eyebrow, 'Schnell-Check')} + {fallbackText(quickCheck.eyebrow, homeContent.quickCheck.eyebrow)}

- +

)}

- {fallbackText(quickCheck.body, 'Der Bayerische Mittelstandspreis richtet sich an inhabergeführte KMU im Freistaat. Fünf Kriterien entscheiden, erfüllen Sie alle fünf, steht Ihrer Bewerbung nichts im Weg.')} + {fallbackText(quickCheck.body, homeContent.quickCheck.body)}

{/* Criteria list */}
@@ -312,23 +249,23 @@ const Home: React.FC = () => { ))}

- {fallbackText(quickCheck.note, 'Auch ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen mit Schwerpunkt in Bayern kann teilnehmen.')} + {fallbackText(quickCheck.note, homeContent.quickCheck.note)}

{ (e.currentTarget as HTMLElement).style.background = '#FFD130'; (e.currentTarget as HTMLElement).style.boxShadow = '0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)'; }} onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = '#EFBF04'; (e.currentTarget as HTMLElement).style.boxShadow = 'none'; }} > - {fallbackText(quickCheck.cta?.label, 'Ihr Weg zum Preis')} + {fallbackText(quickCheck.cta?.label, homeContent.quickCheck.cta.label)}
{/* Right (desktop) / Top (mobile) – image; on mobile the heading is overlaid here */}
{mediaAlt(quickCheck.image, {/* Overlay: mobile = navy shadow bottom-left (for the heading); desktop = leftward fade into text bg */} @@ -338,9 +275,9 @@ const Home: React.FC = () => { {/* Heading overlay – mobile only */} {isMobile && (
- {fallbackText(quickCheck.eyebrow, 'Schnell-Check')} + {fallbackText(quickCheck.eyebrow, homeContent.quickCheck.eyebrow)}

- +

)} @@ -357,8 +294,8 @@ const Home: React.FC = () => { {/* Left – full-bleed image with stats overlay (+ heading on mobile) */}
{mediaAlt(intro.image, {/* Dark overlay */} @@ -368,9 +305,9 @@ const Home: React.FC = () => { {/* Heading overlay – mobile only (bottom-left) */} {isMobile && (
- {fallbackText(intro.eyebrow, 'Ein Gewinn für alle')} + {fallbackText(intro.eyebrow, homeContent.intro.eyebrow)}

- +

)} @@ -391,9 +328,9 @@ const Home: React.FC = () => {
{!isMobile && ( <> - {fallbackText(intro.eyebrow, 'Ein Gewinn für alle')} + {fallbackText(intro.eyebrow, homeContent.intro.eyebrow)}

- +

)} @@ -410,18 +347,18 @@ const Home: React.FC = () => { )}
- {fallbackText(intro.quote, '„Ein Unternehmen zu gründen erfordert nicht allein spezielle Fähigkeiten. Es erfordert Persönlichkeit!“')} + {fallbackText(intro.quote, homeContent.intro.quote)}

- {fallbackText(intro.body, 'Wir suchen Vorbilder, die sich trotz aller Risiken nicht scheuen, Visionen in Businesspläne und Ideen in Unternehmungen zu verwandeln, und das mit großem persönlichen Einsatz und Verantwortung für Ihre Mitarbeiter. Seit vielen Jahren würdigen wir herausragende unternehmerische Leistungen im Freistaat.')} + {fallbackText(intro.body, homeContent.intro.body)}

{ (e.currentTarget as HTMLElement).style.background = '#FFD130'; (e.currentTarget as HTMLElement).style.boxShadow = '0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)'; }} onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = '#EFBF04'; (e.currentTarget as HTMLElement).style.boxShadow = 'none'; }} > - {fallbackText(intro.cta?.label, 'Mehr über den Preis')} + {fallbackText(intro.cta?.label, homeContent.intro.cta.label)}
@@ -434,16 +371,16 @@ const Home: React.FC = () => {
- {fallbackText(winnersSection.eyebrow, 'Success Stories')} + {fallbackText(winnersSection.eyebrow, homeContent.winners.eyebrow)}

- +

- {fallbackText(winnersSection.cta?.label, 'Alle Preisträger')} + {fallbackText(winnersSection.cta?.label, homeContent.winners.cta.label)}
@@ -460,8 +397,14 @@ const Home: React.FC = () => { WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none', }}> - {homeWinners.map((w: any) => ( - + {homeWinners.map((w) => ( + ))}
@@ -479,9 +422,9 @@ const Home: React.FC = () => {
{!isMobile && ( <> - {fallbackText(benefits.eyebrow, 'Mehr als nur ein Preis')} + {fallbackText(benefits.eyebrow, homeContent.benefits.eyebrow)}

- +

)} @@ -508,8 +451,8 @@ const Home: React.FC = () => { {/* Right – full-bleed photo (+ heading on mobile) */}
{mediaAlt(benefits.image,
{ {/* Heading overlay – mobile only (bottom-left) */} {isMobile && (
- {fallbackText(benefits.eyebrow, 'Mehr als nur ein Preis')} + {fallbackText(benefits.eyebrow, homeContent.benefits.eyebrow)}

- +

)} {/* Bottom-left label – desktop only */} {!isMobile && (
-
{fallbackText(benefits.imageLabel, 'BMP Gala-Netzwerk')}
+
{fallbackText(benefits.imageLabel, homeContent.benefits.imageLabel)}
)} @@ -544,13 +487,13 @@ const Home: React.FC = () => { {/* Left – pitch copy */}
- {fallbackText(application.eyebrow, 'Chance ergreifen')} + {fallbackText(application.eyebrow, homeContent.application.eyebrow)}

- +

- {fallbackText(application.body, 'Werden Sie Teil der Wall of Fame des bayerischen Mittelstands – vollständig kostenfrei.')} + {fallbackText(application.body, homeContent.application.body)}

{/* Benefit rows */} @@ -590,8 +533,8 @@ const Home: React.FC = () => { {!isMobile && (
{mediaAlt(application.awardImage, {/* Blend photo into gold panel */} @@ -599,15 +542,15 @@ const Home: React.FC = () => {
- {fallbackText(application.awardCaption, 'Preisverleihung 2025')} + {fallbackText(application.awardCaption, homeContent.application.awardCaption)}
)} {/* Form area */}
- {fallbackText(application.formEyebrow, 'Direktbewerbung 2026')} -

{fallbackText(application.formTitle, 'Kostenlos bewerben')}

+ {fallbackText(application.formEyebrow, homeContent.application.formEyebrow)} +

{fallbackText(application.formTitle, homeContent.application.formTitle)}

@@ -616,8 +559,8 @@ const Home: React.FC = () => { {/* Footnote row */}
- {fallbackText(application.footnote, 'Kostenlos und unverbindlich –')} - {fallbackText(application.footnoteStrong, 'keine Teilnahmegebühr')} + {fallbackText(application.footnote, homeContent.application.footnote)} + {fallbackText(application.footnoteStrong, homeContent.application.footnoteStrong)}
@@ -634,9 +577,9 @@ const Home: React.FC = () => {
-

{fallbackText(videoModal.title, 'Video-Player Platzhalter')}

-

{fallbackText(videoModal.description, 'Hier würde das Image-Video des BMP geladen werden.')}

- +

{fallbackText(videoModal.title, homeContent.videoModal.title)}

+

{fallbackText(videoModal.description, homeContent.videoModal.description)}

+
@@ -649,19 +592,24 @@ const Home: React.FC = () => { function StatusSlider({ data }: { data?: HomeCms }) { const isMobile = useIsMobile(); - const phases = fallbackArray(data?.phases, STATUS_PHASES).map((phase: any, i) => { - const fallbackPhase = STATUS_PHASES[i] - const cta = phase?.cta?.label - ? { label: phase.cta.label, to: phase.cta.to || phase.cta.url || fallbackPhase?.cta?.to || '/' } - : fallbackPhase?.cta - const membershipCta = phase?.membershipCta?.label - ? { label: phase.membershipCta.label, to: phase.membershipCta.to || phase.membershipCta.url || '/mitglied-werden' } + const phases = fallbackArray(data?.phases, homeContent.status.phases).map((phase, i) => { + const phaseRecord = phase as StatusPhase + const fallbackPhase = homeContent.status.phases[i] + const fallbackCta = fallbackPhase?.cta + ? { label: fallbackPhase.cta.label, to: fallbackPhase.cta.url } + : null + const cta = phaseRecord?.cta?.label + ? { label: phaseRecord.cta.label, to: phaseRecord.cta.to || phaseRecord.cta.url || fallbackPhase?.cta?.url || '/' } + : fallbackCta + const membershipCta = phaseRecord?.membershipCta?.label + ? { label: phaseRecord.membershipCta.label, to: phaseRecord.membershipCta.to || phaseRecord.membershipCta.url || homeContent.hero.secondaryCta.url } : undefined - return { ...fallbackPhase, ...phase, cta, membershipCta } + return { ...fallbackPhase, ...phaseRecord, cta, membershipCta } }); - const configuredActive = data?.activePhase !== undefined && data?.activePhase !== null ? Number(data.activePhase) : ACTIVE_PHASE; - const active = Math.max(0, Math.min(phases.length - 1, Number.isFinite(configuredActive) ? configuredActive : ACTIVE_PHASE)); + const fallbackActivePhase = Number(homeContent.status.activePhase); + const configuredActive = data?.activePhase !== undefined && data?.activePhase !== null ? Number(data.activePhase) : fallbackActivePhase; + const active = Math.max(0, Math.min(phases.length - 1, Number.isFinite(configuredActive) ? configuredActive : fallbackActivePhase)); const phase = phases[active]; return ( @@ -673,14 +621,32 @@ function StatusSlider({ data }: { data?: HomeCms }) { minHeight: isMobile ? 360 : 'auto', }} > - + ); } // ─── StatusSlideCard ────────────────────────────────────────────────────────── -function StatusSlideCard({ phase, isMobile, noActionLabel }: { phase: StatusPhase & HomeCms; isMobile: boolean; noActionLabel: string }) { +function StatusSlideCard({ + phase, + isMobile, + noActionLabel, + successApplicationsLabel, + successWinnersLabel, +}: { + phase: StatusPhase & HomeCms; + isMobile: boolean; + noActionLabel: string; + successApplicationsLabel: string; + successWinnersLabel: string; +}) { return (
-
{fallbackText(phase.successApplications, '247')}
-
Bewerbungen
+
{fallbackText(phase.successApplications, homeContent.status.phases[2]?.successApplications || '')}
+
{successApplicationsLabel}
-
{fallbackText(phase.successWinners, '3')}
-
Preisträger
+
{fallbackText(phase.successWinners, homeContent.status.phases[2]?.successWinners || '')}
+
{successWinnersLabel}
)} @@ -768,7 +734,7 @@ function StatusSlideCard({ phase, isMobile, noActionLabel }: { phase: StatusPhas
e.stopPropagation()} style={{ fontFamily: FF, @@ -793,7 +759,7 @@ function StatusSlideCard({ phase, isMobile, noActionLabel }: { phase: StatusPhas (e.currentTarget as HTMLElement).style.borderColor = `${phase.accent}55`; }} > - {fallbackText(phase.membershipCta?.label, 'Mitglied werden')} + {fallbackText(phase.membershipCta?.label, homeContent.hero.secondaryCta.label)}
)}