feat: centralize contact cms defaults
This commit is contained in:
61
src/spa/contactContent.ts
Normal file
61
src/spa/contactContent.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { contactFormContent } from '@/spa/contactFormContent'
|
||||
|
||||
export const contactContent = {
|
||||
hero: {
|
||||
backgroundImageFilename: 'gala-dinner.jpg',
|
||||
imageAlt: 'BMP Gala',
|
||||
eyebrow: 'Dialog & Service',
|
||||
heading: 'Wir sind\nfür Sie da.',
|
||||
description: 'Fragen zur Bewerbung, zur Gala oder zu Partnermöglichkeiten – unser Team begleitet Sie persönlich.',
|
||||
},
|
||||
info: {
|
||||
eyebrow: 'Direktkontakt',
|
||||
heading: 'Kontaktmöglichkeiten',
|
||||
items: [
|
||||
{ icon: 'mapPin', title: 'Geschäftsstelle', lines: 'Maximilianstraße 42\n80538 München' },
|
||||
{ icon: 'mail', title: 'E-Mail', lines: 'info@bmp-bayern.de' },
|
||||
{ icon: 'phone', title: 'Telefon', lines: '+49 89 123 456 78' },
|
||||
{ icon: 'clock', title: 'Bürozeiten', lines: 'Mo – Fr: 09:00 – 17:00 Uhr' },
|
||||
],
|
||||
nextAwardLabel: 'Nächste Preisverleihung:',
|
||||
nextAwardDate: '22. Oktober 2026',
|
||||
formImageFilename: 'preistraeger-gruppe.jpg',
|
||||
formImageAlt: 'BMP Gala 2025',
|
||||
formImageLabel: 'Gala 2025 München',
|
||||
formEyebrow: 'Kontaktformular',
|
||||
formTitle: 'Schreiben Sie uns',
|
||||
formCopy: contactFormContent,
|
||||
},
|
||||
deadlines: {
|
||||
watermark: '2026',
|
||||
eyebrow: 'Fahrplan 2026',
|
||||
items: [
|
||||
{ step: '01', label: 'Bewerbungsschluss', date: '30. Juni 2026', desc: 'Letzte Möglichkeit zur Einreichung Ihrer Unterlagen.' },
|
||||
{ step: '02', label: 'Nominierung', date: '15. August 2026', desc: 'Bekanntgabe der Nominierten nach Jury-Sichtung.' },
|
||||
{ step: '03', label: 'Preisverleihung', date: '22. Oktober 2026', desc: 'Festliche Gala in München.' },
|
||||
],
|
||||
},
|
||||
faq: {
|
||||
eyebrow: 'Antworten',
|
||||
heading: 'Häufige\nFragen',
|
||||
items: [
|
||||
{
|
||||
q: 'Was kostet die Teilnahme am BMP?',
|
||||
a: 'Die Teilnahme am Bayerischen Mittelstandspreis ist für alle Unternehmen vollständig kostenfrei. Wir finanzieren uns durch Partner und Sponsoren, um exzellenten Mittelstand ohne finanzielle Hürden zu fördern.',
|
||||
},
|
||||
{
|
||||
q: 'Nach welchen Kriterien wird bewertet?',
|
||||
a: 'Die Bewertung basiert auf vier Säulen: Zukunftsfähigkeit & Innovation, Nachhaltigkeit & Verantwortung, Unternehmenskultur sowie Regionale Verwurzelung. Jede Säule fließt mit 25 % in das Gesamtergebnis ein.',
|
||||
},
|
||||
{
|
||||
q: 'Wie läuft der Bewerbungsprozess ab?',
|
||||
a: 'Der Prozess ist mehrstufig: 1. Online-Bewerbung oder Vorschlag, 2. Formale Prüfung & erste Jury-Sichtung, 3. Fach-Audits bei den Finalisten vor Ort, 4. Finale Jury-Sitzung und abschließende Preisverleihung.',
|
||||
},
|
||||
{
|
||||
q: 'Wie lange dauert die Bewerbung?',
|
||||
a: 'Dank unseres optimierten Bewerbungsformulars benötigen Sie für die erste Einreichung ca. 30–45 Minuten. Tiefergehende Unterlagen werden erst in der Audit-Phase angefordert.',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField';
|
||||
import { useCmsRoute } from '@/spa/cmsRoute';
|
||||
import Image from '@/spa/components/ui/UnoptimizedImage'
|
||||
import type { Page } from '@/payload-types';
|
||||
import { contactContent } from '@/spa/contactContent';
|
||||
|
||||
const NAVY = '#111D55';
|
||||
const GOLD = '#EFBF04';
|
||||
@@ -17,7 +18,7 @@ type ContactCms = NonNullable<Page['contact']>;
|
||||
type ContactItem = NonNullable<NonNullable<ContactCms['info']>['items']>[number] | {
|
||||
icon: string;
|
||||
title: string;
|
||||
lines: string[];
|
||||
lines: string | string[];
|
||||
};
|
||||
type DeadlineItem = NonNullable<NonNullable<ContactCms['deadlines']>['items']>[number];
|
||||
type FaqItem = NonNullable<NonNullable<ContactCms['faq']>['items']>[number];
|
||||
@@ -39,38 +40,6 @@ const ICONS = {
|
||||
clock: Clock,
|
||||
};
|
||||
|
||||
const DEFAULT_FAQS = [
|
||||
{
|
||||
q: 'Was kostet die Teilnahme am BMP?',
|
||||
a: 'Die Teilnahme am Bayerischen Mittelstandspreis ist für alle Unternehmen vollständig kostenfrei. Wir finanzieren uns durch Partner und Sponsoren, um exzellenten Mittelstand ohne finanzielle Hürden zu fördern.',
|
||||
},
|
||||
{
|
||||
q: 'Nach welchen Kriterien wird bewertet?',
|
||||
a: 'Die Bewertung basiert auf vier Säulen: Zukunftsfähigkeit & Innovation, Nachhaltigkeit & Verantwortung, Unternehmenskultur sowie Regionale Verwurzelung. Jede Säule fließt mit 25 % in das Gesamtergebnis ein.',
|
||||
},
|
||||
{
|
||||
q: 'Wie läuft der Bewerbungsprozess ab?',
|
||||
a: 'Der Prozess ist mehrstufig: 1. Online-Bewerbung oder Vorschlag, 2. Formale Prüfung & erste Jury-Sichtung, 3. Fach-Audits bei den Finalisten vor Ort, 4. Finale Jury-Sitzung und abschließende Preisverleihung.',
|
||||
},
|
||||
{
|
||||
q: 'Wie lange dauert die Bewerbung?',
|
||||
a: 'Dank unseres optimierten Bewerbungsformulars benötigen Sie für die erste Einreichung ca. 30–45 Minuten. Tiefergehende Unterlagen werden erst in der Audit-Phase angefordert.',
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_DEADLINES = [
|
||||
{ step: '01', label: 'Bewerbungsschluss', date: '30. Juni 2026', desc: 'Letzte Möglichkeit zur Einreichung Ihrer Unterlagen.' },
|
||||
{ step: '02', label: 'Nominierung', date: '15. August 2026', desc: 'Bekanntgabe der Nominierten nach Jury-Sichtung.' },
|
||||
{ step: '03', label: 'Preisverleihung', date: '22. Oktober 2026', desc: 'Festliche Gala in München.' },
|
||||
];
|
||||
|
||||
const DEFAULT_CONTACT_ITEMS = [
|
||||
{ icon: 'mapPin', title: 'Geschäftsstelle', lines: ['Maximilianstraße 42', '80538 München'] },
|
||||
{ icon: 'mail', title: 'E-Mail', lines: ['info@bmp-bayern.de'] },
|
||||
{ icon: 'phone', title: 'Telefon', lines: ['+49 89 123 456 78'] },
|
||||
{ icon: 'clock', title: 'Bürozeiten', lines: ['Mo – Fr: 09:00 – 17:00 Uhr'] },
|
||||
];
|
||||
|
||||
const Contact: React.FC = () => {
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(0);
|
||||
const isMobile = useIsMobile();
|
||||
@@ -79,9 +48,9 @@ const Contact: React.FC = () => {
|
||||
const info = contact.info || {};
|
||||
const deadlines = contact.deadlines || {};
|
||||
const faq = contact.faq || {};
|
||||
const contactItems = fallbackArray<ContactItem>(info.items, DEFAULT_CONTACT_ITEMS);
|
||||
const deadlineItems = fallbackArray<DeadlineItem>(deadlines.items, DEFAULT_DEADLINES);
|
||||
const faqItems = fallbackArray<FaqItem>(faq.items, DEFAULT_FAQS);
|
||||
const contactItems = fallbackArray<ContactItem>(info.items, contactContent.info.items);
|
||||
const deadlineItems = fallbackArray<DeadlineItem>(deadlines.items, contactContent.deadlines.items);
|
||||
const faqItems = fallbackArray<FaqItem>(faq.items, contactContent.faq.items);
|
||||
|
||||
return (
|
||||
<div style={{ background: CREAM }}>
|
||||
@@ -89,8 +58,8 @@ const Contact: React.FC = () => {
|
||||
{/* ── Hero ───────────────────────────────────────────────────────── */}
|
||||
<section style={{ position: 'relative', height: '68vh', minHeight: 520, overflow: 'hidden', display: 'flex', alignItems: 'flex-end' }}>
|
||||
<Image unoptimized
|
||||
src={mediaUrl(hero.backgroundImage, '/images/gala-dinner.jpg')}
|
||||
alt={mediaAlt(hero.backgroundImage, fallbackText(hero.imageAlt, 'BMP Gala'))}
|
||||
src={mediaUrl(hero.backgroundImage, `/images/${contactContent.hero.backgroundImageFilename}`)}
|
||||
alt={mediaAlt(hero.backgroundImage, fallbackText(hero.imageAlt, contactContent.hero.imageAlt))}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 40%' }}
|
||||
/>
|
||||
{/* Diagonal overlay */}
|
||||
@@ -100,13 +69,13 @@ const Contact: React.FC = () => {
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 2, width: '100%', paddingBottom: isMobile ? '3rem' : '5rem', paddingLeft: isMobile ? 24 : 80, paddingRight: isMobile ? 24 : 80 }}>
|
||||
<p style={{ fontSize: 11, fontWeight: 800, letterSpacing: '0.28em', textTransform: 'uppercase', color: GOLD, marginBottom: 16 }}>
|
||||
{fallbackText(hero.eyebrow, 'Dialog & Service')}
|
||||
{fallbackText(hero.eyebrow, contactContent.hero.eyebrow)}
|
||||
</p>
|
||||
<h1 style={{ fontSize: isMobile ? 'clamp(2rem, 10vw, 3rem)' : 'clamp(3rem, 7vw, 6rem)', fontWeight: 900, lineHeight: 1.0, color: '#fff', letterSpacing: '-0.03em', marginBottom: 24, maxWidth: 640 }}>
|
||||
<Lines text={fallbackText(hero.heading, 'Wir sind\nfür Sie da.')} />
|
||||
<Lines text={fallbackText(hero.heading, contactContent.hero.heading)} />
|
||||
</h1>
|
||||
<p style={{ fontSize: 18, color: 'rgba(255,255,255,0.72)', maxWidth: 480, lineHeight: 1.65, fontWeight: 400 }}>
|
||||
{fallbackText(hero.description, 'Fragen zur Bewerbung, zur Gala oder zu Partnermöglichkeiten – unser Team begleitet Sie persönlich.')}
|
||||
{fallbackText(hero.description, contactContent.hero.description)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -121,11 +90,11 @@ const Contact: React.FC = () => {
|
||||
{/* Left – info panel */}
|
||||
<div style={{ padding: isMobile ? '48px 24px' : '88px 56px 88px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center', position: 'relative' }}>
|
||||
<p style={{ fontSize: 10, fontWeight: 800, letterSpacing: '0.28em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.4)', marginBottom: 20 }}>
|
||||
{fallbackText(info.eyebrow, 'Direktkontakt')}
|
||||
{fallbackText(info.eyebrow, contactContent.info.eyebrow)}
|
||||
</p>
|
||||
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 20 }} />
|
||||
<h2 style={{ fontSize: 'clamp(1.6rem, 3vw, 2.4rem)', fontWeight: 900, color: '#fff', lineHeight: 1.15, letterSpacing: '-0.03em', marginBottom: 52 }}>
|
||||
<Lines text={fallbackText(info.heading, 'Kontaktmöglichkeiten')} />
|
||||
<Lines text={fallbackText(info.heading, contactContent.info.heading)} />
|
||||
</h2>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
|
||||
@@ -152,7 +121,7 @@ const Contact: React.FC = () => {
|
||||
|
||||
<div style={{ marginTop: 52, paddingTop: 28, borderTop: '1px solid rgba(255,255,255,0.10)' }}>
|
||||
<p style={{ fontSize: 15, color: 'rgba(255,255,255,0.40)', lineHeight: 1.65 }}>
|
||||
{fallbackText(info.nextAwardLabel, 'Nächste Preisverleihung:')} <strong style={{ color: '#fff' }}>{fallbackText(info.nextAwardDate, '22. Oktober 2026')}</strong>
|
||||
{fallbackText(info.nextAwardLabel, contactContent.info.nextAwardLabel)} <strong style={{ color: '#fff' }}>{fallbackText(info.nextAwardDate, contactContent.info.nextAwardDate)}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,19 +147,19 @@ const Contact: React.FC = () => {
|
||||
|
||||
{!isMobile && (
|
||||
<div style={{ height: 200, position: 'relative', overflow: 'hidden', flexShrink: 0, zIndex: 1 }}>
|
||||
<Image unoptimized src={mediaUrl(info.formImage, '/images/preistraeger-gruppe.jpg')} alt={mediaAlt(info.formImage, fallbackText(info.formImageAlt, 'BMP Gala 2025'))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', filter: 'sepia(0.18) brightness(0.92)' }} />
|
||||
<Image unoptimized src={mediaUrl(info.formImage, `/images/${contactContent.info.formImageFilename}`)} alt={mediaAlt(info.formImage, fallbackText(info.formImageAlt, contactContent.info.formImageAlt))} 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: 14, left: 40, display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{ width: 5, height: 5, borderRadius: '50%', background: 'rgba(17,29,85,0.7)' }} />
|
||||
<span style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 10, fontWeight: 700, color: 'rgba(17,29,85,0.75)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>{fallbackText(info.formImageLabel, 'Gala 2025 München')}</span>
|
||||
<span style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 10, fontWeight: 700, color: 'rgba(17,29,85,0.75)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>{fallbackText(info.formImageLabel, contactContent.info.formImageLabel)}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ padding: isMobile ? '36px 20px' : '32px 48px 32px 40px', flex: isMobile ? 'none' : 1, display: 'flex', flexDirection: 'column', minHeight: isMobile ? 480 : 0, position: 'relative', zIndex: 1 }}>
|
||||
<span style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 8 }}>{fallbackText(info.formEyebrow, 'Kontaktformular')}</span>
|
||||
<span style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 8 }}>{fallbackText(info.formEyebrow, contactContent.info.formEyebrow)}</span>
|
||||
<h3 style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 'clamp(1.2rem, 1.8vw, 1.6rem)', fontWeight: 900, color: '#111D55', letterSpacing: '-0.025em', textTransform: 'uppercase', lineHeight: 1.1, marginBottom: 24 }}>
|
||||
{fallbackText(info.formTitle, 'Schreiben Sie uns')}
|
||||
{fallbackText(info.formTitle, contactContent.info.formTitle)}
|
||||
</h3>
|
||||
<KontaktForm theme="gold" content={info.formCopy || undefined} />
|
||||
</div>
|
||||
@@ -201,12 +170,12 @@ const Contact: React.FC = () => {
|
||||
{/* ── Deadlines ──────────────────────────────────────────────────── */}
|
||||
<section style={{ background: '#0D1640', padding: isMobile ? '56px 0' : '88px 0', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', right: -20, top: '50%', transform: 'translateY(-50%)', fontSize: 'clamp(160px, 22vw, 280px)', fontWeight: 900, color: '#fff', opacity: 0.025, lineHeight: 1, pointerEvents: 'none', userSelect: 'none', letterSpacing: '-0.05em' }}>
|
||||
{fallbackText(deadlines.watermark, '2026')}
|
||||
{fallbackText(deadlines.watermark, contactContent.deadlines.watermark)}
|
||||
</div>
|
||||
<div className="container mx-auto px-6" style={{ position: 'relative' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 20, marginBottom: isMobile ? 48 : 72 }}>
|
||||
<span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '0.28em', textTransform: 'uppercase', color: '#EFBF04', whiteSpace: 'nowrap' }}>
|
||||
{fallbackText(deadlines.eyebrow, 'Fahrplan 2026')}
|
||||
{fallbackText(deadlines.eyebrow, contactContent.deadlines.eyebrow)}
|
||||
</span>
|
||||
<div style={{ flex: 1, height: 1, background: 'rgba(255,255,255,0.10)' }} />
|
||||
</div>
|
||||
@@ -248,11 +217,11 @@ const Contact: React.FC = () => {
|
||||
{/* Left – sticky label */}
|
||||
<div style={isMobile ? undefined : { position: 'sticky', top: 96, alignSelf: 'start' }}>
|
||||
<p style={{ fontSize: 10, fontWeight: 800, letterSpacing: '0.28em', textTransform: 'uppercase', color: '#4A8FC9', marginBottom: 20 }}>
|
||||
{fallbackText(faq.eyebrow, 'Antworten')}
|
||||
{fallbackText(faq.eyebrow, contactContent.faq.eyebrow)}
|
||||
</p>
|
||||
<div style={{ width: 28, height: 2, background: GOLD, marginBottom: 20 }} />
|
||||
<h2 style={{ fontSize: isMobile ? 'clamp(1.8rem, 8vw, 2.4rem)' : 'clamp(2rem, 3.5vw, 2.8rem)', fontWeight: 900, color: NAVY, letterSpacing: '-0.03em', lineHeight: 1.1 }}>
|
||||
<Lines text={fallbackText(faq.heading, 'Häufige\nFragen')} />
|
||||
<Lines text={fallbackText(faq.heading, contactContent.faq.heading)} />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user