feat: manage participation page via payload
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
|
||||
"preload:contact-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-contact-page-cms.ts",
|
||||
"preload:home-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-home-page-cms.ts",
|
||||
"preload:participation-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-participation-page-cms.ts",
|
||||
"reinstall": "cross-env NODE_OPTIONS=--no-deprecation rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install",
|
||||
"start": "cross-env NODE_OPTIONS=--no-deprecation next start",
|
||||
"test": "pnpm run test:int && pnpm run test:e2e",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { MediaBlock } from '../../blocks/MediaBlock/config'
|
||||
import { hero } from '@/heros/config'
|
||||
import { contactFields } from './contactFields'
|
||||
import { homeFields } from './homeFields'
|
||||
import { participationFields } from './participationFields'
|
||||
import { slugField } from 'payload'
|
||||
import { populatePublishedAt } from '../../hooks/populatePublishedAt'
|
||||
import { generatePreviewPath } from '../../utilities/generatePreviewPath'
|
||||
@@ -39,8 +40,16 @@ const isContactPage = (_: unknown, siblingData?: { slug?: string; spaPath?: stri
|
||||
return spaPath === '/kontakt' || slug === 'kontakt' || slug === 'contact' || title === 'kontakt' || title === 'contact'
|
||||
}
|
||||
|
||||
const isParticipationPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) => {
|
||||
const slug = siblingData?.slug
|
||||
const spaPath = siblingData?.spaPath
|
||||
const title = siblingData?.title?.toLowerCase()
|
||||
|
||||
return spaPath === '/teilnahme' || slug === 'teilnahme' || slug === 'participation' || title === 'teilnahme' || title === 'participation'
|
||||
}
|
||||
|
||||
const isManagedSpaPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) =>
|
||||
isHomePage(undefined, siblingData) || isContactPage(undefined, siblingData)
|
||||
isHomePage(undefined, siblingData) || isContactPage(undefined, siblingData) || isParticipationPage(undefined, siblingData)
|
||||
|
||||
export const Pages: CollectionConfig<'pages'> = {
|
||||
slug: 'pages',
|
||||
@@ -122,6 +131,13 @@ export const Pages: CollectionConfig<'pages'> = {
|
||||
fields: contactFields,
|
||||
label: 'Contact Page',
|
||||
},
|
||||
{
|
||||
admin: {
|
||||
condition: (data) => isParticipationPage(undefined, data),
|
||||
},
|
||||
fields: participationFields,
|
||||
label: 'Participation Page',
|
||||
},
|
||||
{
|
||||
name: 'meta',
|
||||
label: 'SEO',
|
||||
|
||||
400
src/collections/Pages/participationFields.ts
Normal file
400
src/collections/Pages/participationFields.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import { participationContent } from '@/spa/participationContent'
|
||||
|
||||
const uploadField = (name: string, label: string, description?: string): Field => ({
|
||||
name,
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
label,
|
||||
admin: description ? { description } : undefined,
|
||||
})
|
||||
|
||||
const text = (name: string, label: string, defaultValue?: string): Field => ({
|
||||
name,
|
||||
type: 'text',
|
||||
label,
|
||||
defaultValue,
|
||||
})
|
||||
|
||||
const textarea = (name: string, label: string, defaultValue?: string): Field => ({
|
||||
name,
|
||||
type: 'textarea',
|
||||
label,
|
||||
defaultValue,
|
||||
})
|
||||
|
||||
const checkbox = (name: string, label: string, defaultValue = false): Field => ({
|
||||
name,
|
||||
type: 'checkbox',
|
||||
label,
|
||||
defaultValue,
|
||||
})
|
||||
|
||||
const sectionAdmin = (description: string) => ({
|
||||
description,
|
||||
initCollapsed: true,
|
||||
})
|
||||
|
||||
const linkGroup = (labelDefault: string, urlDefault: string): Field[] => [
|
||||
text('label', 'Label', labelDefault),
|
||||
text('url', 'URL', urlDefault),
|
||||
]
|
||||
|
||||
const iconOptions = [
|
||||
{ label: 'Alert circle', value: 'alertCircle' },
|
||||
{ label: 'Arrow right', value: 'arrowRight' },
|
||||
{ label: 'Building', value: 'building2' },
|
||||
{ label: 'Check', value: 'check' },
|
||||
{ label: 'Check circle', value: 'checkCircle2' },
|
||||
{ label: 'File text', value: 'fileText' },
|
||||
{ label: 'Map pin', value: 'mapPin' },
|
||||
{ label: 'Search', value: 'search' },
|
||||
{ label: 'Send', value: 'send' },
|
||||
{ label: 'Star', value: 'star' },
|
||||
{ label: 'Trending up', value: 'trendingUp' },
|
||||
{ label: 'Trophy', value: 'trophy' },
|
||||
{ label: 'User', value: 'user' },
|
||||
{ label: 'User check', value: 'userCheck' },
|
||||
{ label: 'User plus', value: 'userPlus' },
|
||||
{ label: 'Users', value: 'users' },
|
||||
]
|
||||
|
||||
const iconField = (defaultValue = 'star', dbName?: string): Field => ({
|
||||
name: 'icon',
|
||||
label: 'Icon',
|
||||
type: 'select',
|
||||
dbName,
|
||||
defaultValue,
|
||||
options: iconOptions,
|
||||
})
|
||||
|
||||
const factFields: Field[] = [text('label', 'Label'), textarea('desc', 'Description')]
|
||||
const ctaGroup = (name: string, label: string, labelDefault: string, urlDefault: string): Field => ({
|
||||
name,
|
||||
label,
|
||||
type: 'group',
|
||||
fields: linkGroup(labelDefault, urlDefault),
|
||||
})
|
||||
|
||||
export const participationFields: Field[] = [
|
||||
{
|
||||
name: 'participation',
|
||||
label: 'Participation page layout sections',
|
||||
type: 'group',
|
||||
admin: {
|
||||
description:
|
||||
'Edit the participation page in the same order it appears on the frontend. The generic Hero and Content tabs are hidden for this SPA-managed route.',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'hero',
|
||||
label: '01 · Hero',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Top image, eyebrow, headline, and intro copy.'),
|
||||
fields: [
|
||||
uploadField('backgroundImage', 'Background image', `Current frontend image: /images/${participationContent.hero.backgroundImageFilename}`),
|
||||
text('imageAlt', 'Image alt text', participationContent.hero.imageAlt),
|
||||
text('eyebrow', 'Eyebrow', participationContent.hero.eyebrow),
|
||||
textarea('heading', 'Headline', participationContent.hero.heading),
|
||||
textarea('description', 'Intro paragraph', participationContent.hero.description),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'process',
|
||||
label: '02 · Weg zur Auszeichnung timeline',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Timeline section rendered directly after the hero.'),
|
||||
fields: [
|
||||
text('eyebrow', 'Eyebrow', participationContent.process.eyebrow),
|
||||
textarea('heading', 'Heading', participationContent.process.heading),
|
||||
textarea('description', 'Intro paragraph', participationContent.process.description),
|
||||
text('scrollHint', 'Desktop scroll hint', participationContent.process.scrollHint),
|
||||
text('progressLabel', 'Desktop progress label', participationContent.process.progressLabel),
|
||||
{
|
||||
name: 'steps',
|
||||
label: 'Process steps',
|
||||
type: 'array',
|
||||
dbName: 'proc_steps',
|
||||
defaultValue: participationContent.process.steps,
|
||||
fields: [text('step', 'Step number'), text('title', 'Title'), textarea('desc', 'Description'), text('date', 'Date'), iconField('fileText')],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'eligibility',
|
||||
label: '03 · Eligibility section',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Cream eligibility section with criteria rows, notes, and CTAs.'),
|
||||
fields: [
|
||||
text('eyebrow', 'Eyebrow', participationContent.eligibility.eyebrow),
|
||||
textarea('heading', 'Heading', participationContent.eligibility.heading),
|
||||
textarea('description', 'Intro paragraph', participationContent.eligibility.description),
|
||||
ctaGroup('primaryCta', 'Primary CTA', participationContent.eligibility.primaryCta.label, participationContent.eligibility.primaryCta.url),
|
||||
ctaGroup('secondaryCta', 'Secondary CTA', participationContent.eligibility.secondaryCta.label, participationContent.eligibility.secondaryCta.url),
|
||||
{
|
||||
name: 'criteria',
|
||||
label: 'Eligibility rows',
|
||||
type: 'array',
|
||||
dbName: 'elig_crit',
|
||||
defaultValue: participationContent.eligibility.criteria,
|
||||
fields: [text('num', 'Number'), text('title', 'Title'), textarea('body', 'Body'), iconField('mapPin')],
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
label: 'Additional notes',
|
||||
type: 'array',
|
||||
dbName: 'elig_notes',
|
||||
defaultValue: participationContent.eligibility.notes,
|
||||
fields: [text('title', 'Title'), textarea('body', 'Body'), iconField('building2')],
|
||||
},
|
||||
text('nudgeText', 'Bottom nudge text', participationContent.eligibility.nudgeText),
|
||||
ctaGroup('nudgeCta', 'Bottom CTA', participationContent.eligibility.nudgeCta.label, participationContent.eligibility.nudgeCta.url),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'evaluation',
|
||||
label: '04 · Criteria / evaluation section',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Navy evaluation section with image, link, and criteria grid.'),
|
||||
fields: [
|
||||
uploadField('image', 'Image', `Current frontend image: /images/${participationContent.evaluation.imageFilename}`),
|
||||
text('imageAlt', 'Image alt text', participationContent.evaluation.imageAlt),
|
||||
text('eyebrow', 'Eyebrow', participationContent.evaluation.eyebrow),
|
||||
textarea('heading', 'Heading', participationContent.evaluation.heading),
|
||||
textarea('body', 'Body copy', participationContent.evaluation.body),
|
||||
ctaGroup('link', 'External criteria link', participationContent.evaluation.link.label, participationContent.evaluation.link.url),
|
||||
{
|
||||
name: 'criteria',
|
||||
label: 'Evaluation criteria',
|
||||
type: 'array',
|
||||
dbName: 'eval_crit',
|
||||
defaultValue: participationContent.evaluation.criteria,
|
||||
fields: factFields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'applicationWays',
|
||||
label: '05 · Application paths',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Cream section explaining proposal, self-application, and special award paths.'),
|
||||
fields: [
|
||||
text('eyebrow', 'Eyebrow', participationContent.applicationWays.eyebrow),
|
||||
textarea('heading', 'Heading', participationContent.applicationWays.heading),
|
||||
textarea('description', 'Intro paragraph', participationContent.applicationWays.description),
|
||||
text('recommendedLabel', 'Featured card label', participationContent.applicationWays.recommendedLabel),
|
||||
{
|
||||
name: 'institutions',
|
||||
label: 'Proposal institutions',
|
||||
type: 'array',
|
||||
dbName: 'app_inst',
|
||||
defaultValue: participationContent.applicationWays.institutions,
|
||||
fields: [textarea('text', 'Institution')],
|
||||
},
|
||||
{
|
||||
name: 'ways',
|
||||
label: 'Application path cards',
|
||||
type: 'array',
|
||||
dbName: 'app_ways',
|
||||
defaultValue: participationContent.applicationWays.ways,
|
||||
fields: [
|
||||
text('label', 'Path label'),
|
||||
textarea('title', 'Title'),
|
||||
textarea('desc', 'Description'),
|
||||
iconField('send', 'ico'),
|
||||
checkbox('featured', 'Featured card'),
|
||||
{
|
||||
name: 'listType',
|
||||
label: 'Detail list type',
|
||||
type: 'select',
|
||||
dbName: 'lt',
|
||||
defaultValue: 'facts',
|
||||
options: [
|
||||
{ label: 'Use proposal institutions list', value: 'institutions' },
|
||||
{ label: 'Use facts below', value: 'facts' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'facts',
|
||||
label: 'Fact rows',
|
||||
type: 'array',
|
||||
dbName: 'way_facts',
|
||||
fields: factFields,
|
||||
},
|
||||
ctaGroup('cta', 'CTA', 'Zum Formular', '#bewerben'),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'datesBanner',
|
||||
label: '06 · Dates banner',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Gold important-dates banner. Mobile and desktop labels differ slightly in the current design.'),
|
||||
fields: [
|
||||
text('heading', 'Heading', participationContent.datesBanner.heading),
|
||||
textarea('description', 'Desktop description', participationContent.datesBanner.description),
|
||||
{
|
||||
name: 'mobileItems',
|
||||
label: 'Mobile date items',
|
||||
type: 'array',
|
||||
dbName: 'date_mob',
|
||||
defaultValue: participationContent.datesBanner.mobileItems,
|
||||
fields: [text('date', 'Date'), text('label', 'Label')],
|
||||
},
|
||||
{
|
||||
name: 'desktopItems',
|
||||
label: 'Desktop date items',
|
||||
type: 'array',
|
||||
dbName: 'date_desk',
|
||||
defaultValue: participationContent.datesBanner.desktopItems,
|
||||
fields: [text('date', 'Date'), text('label', 'Label')],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'applicationForm',
|
||||
label: '07 · Application form wrapper',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Navy/gold section containing the application form and PDF upload links.'),
|
||||
fields: [
|
||||
uploadField('image', 'Form image', `Current frontend image: /images/${participationContent.applicationForm.imageFilename}`),
|
||||
text('imageAlt', 'Image alt text', participationContent.applicationForm.imageAlt),
|
||||
text('imageCaption', 'Image caption', participationContent.applicationForm.imageCaption),
|
||||
text('eyebrow', 'Eyebrow', participationContent.applicationForm.eyebrow),
|
||||
textarea('heading', 'Heading', participationContent.applicationForm.heading),
|
||||
textarea('description', 'Body copy', participationContent.applicationForm.description),
|
||||
{
|
||||
name: 'facts',
|
||||
label: 'Fact rows',
|
||||
type: 'array',
|
||||
dbName: 'form_facts',
|
||||
defaultValue: participationContent.applicationForm.facts,
|
||||
fields: [text('num', 'Number'), text('label', 'Label'), textarea('desc', 'Description')],
|
||||
},
|
||||
text('formEyebrow', 'Form eyebrow', participationContent.applicationForm.formEyebrow),
|
||||
ctaGroup('uploadCta', 'Top upload CTA', participationContent.applicationForm.uploadCta.label, participationContent.applicationForm.uploadCta.url),
|
||||
textarea('uploadPrompt', 'Bottom upload prompt', participationContent.applicationForm.uploadPrompt),
|
||||
ctaGroup('uploadFooterCta', 'Bottom upload CTA', participationContent.applicationForm.uploadFooterCta.label, participationContent.applicationForm.uploadFooterCta.url),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'form',
|
||||
label: '08 · Application form copy and logic labels',
|
||||
type: 'group',
|
||||
admin: sectionAdmin('Text, options, and eligibility thresholds used inside the multi-step application form.'),
|
||||
fields: [
|
||||
text('stepCompleteLabel', 'Completed step label', participationContent.form.stepCompleteLabel),
|
||||
text('backLabel', 'Back button label', participationContent.form.backLabel),
|
||||
text('nextLabel', 'Next button label', participationContent.form.nextLabel),
|
||||
text('submitLabel', 'Submit button label', participationContent.form.submitLabel),
|
||||
text('yesLabel', 'Yes option label', participationContent.form.yesLabel),
|
||||
text('noLabel', 'No option label', participationContent.form.noLabel),
|
||||
text('requiredSuffix', 'Required field suffix', participationContent.form.requiredSuffix),
|
||||
{
|
||||
name: 'validation',
|
||||
label: 'Validation messages',
|
||||
type: 'group',
|
||||
fields: [
|
||||
text('choose', 'Choose message', participationContent.form.validation.choose),
|
||||
text('required', 'Required message', participationContent.form.validation.required),
|
||||
text('invalidEmail', 'Invalid email message', participationContent.form.validation.invalidEmail),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'typeStep',
|
||||
label: 'Step 1 · Submission type',
|
||||
type: 'group',
|
||||
fields: [
|
||||
text('eyebrow', 'Eyebrow', participationContent.form.typeStep.eyebrow),
|
||||
text('heading', 'Heading', participationContent.form.typeStep.heading),
|
||||
text('selfTitle', 'Self-application title', participationContent.form.typeStep.selfTitle),
|
||||
textarea('selfDesc', 'Self-application description', participationContent.form.typeStep.selfDesc),
|
||||
text('nominationTitle', 'Nomination title', participationContent.form.typeStep.nominationTitle),
|
||||
textarea('nominationDesc', 'Nomination description', participationContent.form.typeStep.nominationDesc),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'selfSteps',
|
||||
label: 'Self-application step labels',
|
||||
type: 'array',
|
||||
dbName: 'self_steps',
|
||||
defaultValue: participationContent.form.selfSteps,
|
||||
fields: [text('label', 'Label'), iconField('trophy')],
|
||||
},
|
||||
{
|
||||
name: 'nominationSteps',
|
||||
label: 'Nomination step labels',
|
||||
type: 'array',
|
||||
dbName: 'nom_steps',
|
||||
defaultValue: participationContent.form.nominationSteps,
|
||||
fields: [text('label', 'Label'), iconField('trophy')],
|
||||
},
|
||||
{
|
||||
name: 'employeeOptions',
|
||||
label: 'Employee options',
|
||||
type: 'array',
|
||||
dbName: 'emp_opts',
|
||||
defaultValue: participationContent.form.employeeOptions,
|
||||
fields: [text('value', 'Value'), text('label', 'Label')],
|
||||
},
|
||||
{
|
||||
name: 'eligibility',
|
||||
label: 'Self-application eligibility step',
|
||||
type: 'group',
|
||||
fields: [
|
||||
text('allowedEmployeeValues', 'Allowed employee option values, comma-separated', participationContent.form.eligibility.allowedEmployeeValues),
|
||||
text('requiredBayernValue', 'Required Bavaria answer value', participationContent.form.eligibility.requiredBayernValue),
|
||||
text('eyebrow', 'Eyebrow', participationContent.form.eligibility.eyebrow),
|
||||
text('heading', 'Heading', participationContent.form.eligibility.heading),
|
||||
text('employeesLabel', 'Employees field label', participationContent.form.eligibility.employeesLabel),
|
||||
text('bayernLabel', 'Bavaria field label', participationContent.form.eligibility.bayernLabel),
|
||||
text('ownerLabel', 'Owner field label', participationContent.form.eligibility.ownerLabel),
|
||||
text('ineligibleHeading', 'Ineligible heading', participationContent.form.eligibility.ineligibleHeading),
|
||||
textarea('ineligibleBody', 'Ineligible body', participationContent.form.eligibility.ineligibleBody),
|
||||
text('ineligibleContactPrefix', 'Ineligible contact prefix', participationContent.form.eligibility.ineligibleContactPrefix),
|
||||
text('ineligibleContactEmail', 'Ineligible contact email', participationContent.form.eligibility.ineligibleContactEmail),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'selfContact',
|
||||
label: 'Self-application contact step',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.selfContact).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
{
|
||||
name: 'selfSummary',
|
||||
label: 'Self-application summary step',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.selfSummary).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
{
|
||||
name: 'nominationCompany',
|
||||
label: 'Nomination company step',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.nominationCompany).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
{
|
||||
name: 'nominationContact',
|
||||
label: 'Nomination contact step',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.nominationContact).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
{
|
||||
name: 'nominationSummary',
|
||||
label: 'Nomination summary step',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.nominationSummary).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
{
|
||||
name: 'success',
|
||||
label: 'Success state copy',
|
||||
type: 'group',
|
||||
fields: Object.entries(participationContent.form.success).map(([name, value]) => text(name, name, value)),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -488,6 +488,431 @@ export interface Page {
|
||||
| null;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Edit the participation page in the same order it appears on the frontend. The generic Hero and Content tabs are hidden for this SPA-managed route.
|
||||
*/
|
||||
participation?: {
|
||||
/**
|
||||
* Top image, eyebrow, headline, and intro copy.
|
||||
*/
|
||||
hero?: {
|
||||
/**
|
||||
* Current frontend image: /images/preisuebergabe.jpg
|
||||
*/
|
||||
backgroundImage?: (number | null) | Media;
|
||||
imageAlt?: string | null;
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
};
|
||||
/**
|
||||
* Timeline section rendered directly after the hero.
|
||||
*/
|
||||
process?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
scrollHint?: string | null;
|
||||
progressLabel?: string | null;
|
||||
steps?:
|
||||
| {
|
||||
step?: string | null;
|
||||
title?: string | null;
|
||||
desc?: string | null;
|
||||
date?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
};
|
||||
/**
|
||||
* Cream eligibility section with criteria rows, notes, and CTAs.
|
||||
*/
|
||||
eligibility?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
primaryCta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
secondaryCta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
criteria?:
|
||||
| {
|
||||
num?: string | null;
|
||||
title?: string | null;
|
||||
body?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
notes?:
|
||||
| {
|
||||
title?: string | null;
|
||||
body?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
nudgeText?: string | null;
|
||||
nudgeCta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Navy evaluation section with image, link, and criteria grid.
|
||||
*/
|
||||
evaluation?: {
|
||||
/**
|
||||
* Current frontend image: /images/buehne-gewinner.jpg
|
||||
*/
|
||||
image?: (number | null) | Media;
|
||||
imageAlt?: string | null;
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
body?: string | null;
|
||||
link?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
criteria?:
|
||||
| {
|
||||
label?: string | null;
|
||||
desc?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
};
|
||||
/**
|
||||
* Cream section explaining proposal, self-application, and special award paths.
|
||||
*/
|
||||
applicationWays?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
recommendedLabel?: string | null;
|
||||
institutions?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
ways?:
|
||||
| {
|
||||
label?: string | null;
|
||||
title?: string | null;
|
||||
desc?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
featured?: boolean | null;
|
||||
listType?: ('institutions' | 'facts') | null;
|
||||
facts?:
|
||||
| {
|
||||
label?: string | null;
|
||||
desc?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
cta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
};
|
||||
/**
|
||||
* Gold important-dates banner. Mobile and desktop labels differ slightly in the current design.
|
||||
*/
|
||||
datesBanner?: {
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
mobileItems?:
|
||||
| {
|
||||
date?: string | null;
|
||||
label?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
desktopItems?:
|
||||
| {
|
||||
date?: string | null;
|
||||
label?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
};
|
||||
/**
|
||||
* Navy/gold section containing the application form and PDF upload links.
|
||||
*/
|
||||
applicationForm?: {
|
||||
/**
|
||||
* Current frontend image: /images/preistraeger-jubel.jpg
|
||||
*/
|
||||
image?: (number | null) | Media;
|
||||
imageAlt?: string | null;
|
||||
imageCaption?: string | null;
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
description?: string | null;
|
||||
facts?:
|
||||
| {
|
||||
num?: string | null;
|
||||
label?: string | null;
|
||||
desc?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
formEyebrow?: string | null;
|
||||
uploadCta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
uploadPrompt?: string | null;
|
||||
uploadFooterCta?: {
|
||||
label?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Text, options, and eligibility thresholds used inside the multi-step application form.
|
||||
*/
|
||||
form?: {
|
||||
stepCompleteLabel?: string | null;
|
||||
backLabel?: string | null;
|
||||
nextLabel?: string | null;
|
||||
submitLabel?: string | null;
|
||||
yesLabel?: string | null;
|
||||
noLabel?: string | null;
|
||||
requiredSuffix?: string | null;
|
||||
validation?: {
|
||||
choose?: string | null;
|
||||
required?: string | null;
|
||||
invalidEmail?: string | null;
|
||||
};
|
||||
typeStep?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
selfTitle?: string | null;
|
||||
selfDesc?: string | null;
|
||||
nominationTitle?: string | null;
|
||||
nominationDesc?: string | null;
|
||||
};
|
||||
selfSteps?:
|
||||
| {
|
||||
label?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
nominationSteps?:
|
||||
| {
|
||||
label?: string | null;
|
||||
icon?:
|
||||
| (
|
||||
| 'alertCircle'
|
||||
| 'arrowRight'
|
||||
| 'building2'
|
||||
| 'check'
|
||||
| 'checkCircle2'
|
||||
| 'fileText'
|
||||
| 'mapPin'
|
||||
| 'search'
|
||||
| 'send'
|
||||
| 'star'
|
||||
| 'trendingUp'
|
||||
| 'trophy'
|
||||
| 'user'
|
||||
| 'userCheck'
|
||||
| 'userPlus'
|
||||
| 'users'
|
||||
)
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
employeeOptions?:
|
||||
| {
|
||||
value?: string | null;
|
||||
label?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
eligibility?: {
|
||||
allowedEmployeeValues?: string | null;
|
||||
requiredBayernValue?: string | null;
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
employeesLabel?: string | null;
|
||||
bayernLabel?: string | null;
|
||||
ownerLabel?: string | null;
|
||||
ineligibleHeading?: string | null;
|
||||
ineligibleBody?: string | null;
|
||||
ineligibleContactPrefix?: string | null;
|
||||
ineligibleContactEmail?: string | null;
|
||||
};
|
||||
selfContact?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
companyLabel?: string | null;
|
||||
companyPlaceholder?: string | null;
|
||||
nameLabel?: string | null;
|
||||
namePlaceholder?: string | null;
|
||||
emailLabel?: string | null;
|
||||
emailPlaceholder?: string | null;
|
||||
phoneLabel?: string | null;
|
||||
phonePlaceholder?: string | null;
|
||||
footnote?: string | null;
|
||||
};
|
||||
selfSummary?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
companyLabel?: string | null;
|
||||
employeesLabel?: string | null;
|
||||
locationLabel?: string | null;
|
||||
locationPrefix?: string | null;
|
||||
contactLabel?: string | null;
|
||||
};
|
||||
nominationCompany?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
companyLabel?: string | null;
|
||||
companyPlaceholder?: string | null;
|
||||
industryLabel?: string | null;
|
||||
industryPlaceholder?: string | null;
|
||||
locationLabel?: string | null;
|
||||
locationPlaceholder?: string | null;
|
||||
};
|
||||
nominationContact?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
nameLabel?: string | null;
|
||||
namePlaceholder?: string | null;
|
||||
emailLabel?: string | null;
|
||||
emailPlaceholder?: string | null;
|
||||
relationshipLabel?: string | null;
|
||||
relationshipPlaceholder?: string | null;
|
||||
footnote?: string | null;
|
||||
};
|
||||
nominationSummary?: {
|
||||
eyebrow?: string | null;
|
||||
heading?: string | null;
|
||||
companyLabel?: string | null;
|
||||
industryLabel?: string | null;
|
||||
locationLabel?: string | null;
|
||||
nominatedByLabel?: string | null;
|
||||
emptyValue?: string | null;
|
||||
};
|
||||
success?: {
|
||||
selfHeading?: string | null;
|
||||
nominationHeading?: string | null;
|
||||
selfPrefix?: string | null;
|
||||
selfMiddle?: string | null;
|
||||
selfSuffix?: string | null;
|
||||
nominationPrefix?: string | null;
|
||||
nominationMiddle?: string | null;
|
||||
nominationSuffix?: string | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
/**
|
||||
@@ -1764,6 +2189,331 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
};
|
||||
};
|
||||
};
|
||||
participation?:
|
||||
| T
|
||||
| {
|
||||
hero?:
|
||||
| T
|
||||
| {
|
||||
backgroundImage?: T;
|
||||
imageAlt?: T;
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
description?: T;
|
||||
};
|
||||
process?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
description?: T;
|
||||
scrollHint?: T;
|
||||
progressLabel?: T;
|
||||
steps?:
|
||||
| T
|
||||
| {
|
||||
step?: T;
|
||||
title?: T;
|
||||
desc?: T;
|
||||
date?: T;
|
||||
icon?: T;
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
eligibility?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
description?: T;
|
||||
primaryCta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
secondaryCta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
criteria?:
|
||||
| T
|
||||
| {
|
||||
num?: T;
|
||||
title?: T;
|
||||
body?: T;
|
||||
icon?: T;
|
||||
id?: T;
|
||||
};
|
||||
notes?:
|
||||
| T
|
||||
| {
|
||||
title?: T;
|
||||
body?: T;
|
||||
icon?: T;
|
||||
id?: T;
|
||||
};
|
||||
nudgeText?: T;
|
||||
nudgeCta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
};
|
||||
evaluation?:
|
||||
| T
|
||||
| {
|
||||
image?: T;
|
||||
imageAlt?: T;
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
body?: T;
|
||||
link?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
criteria?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
desc?: T;
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
applicationWays?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
description?: T;
|
||||
recommendedLabel?: T;
|
||||
institutions?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
};
|
||||
ways?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
title?: T;
|
||||
desc?: T;
|
||||
icon?: T;
|
||||
featured?: T;
|
||||
listType?: T;
|
||||
facts?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
desc?: T;
|
||||
id?: T;
|
||||
};
|
||||
cta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
datesBanner?:
|
||||
| T
|
||||
| {
|
||||
heading?: T;
|
||||
description?: T;
|
||||
mobileItems?:
|
||||
| T
|
||||
| {
|
||||
date?: T;
|
||||
label?: T;
|
||||
id?: T;
|
||||
};
|
||||
desktopItems?:
|
||||
| T
|
||||
| {
|
||||
date?: T;
|
||||
label?: T;
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
applicationForm?:
|
||||
| T
|
||||
| {
|
||||
image?: T;
|
||||
imageAlt?: T;
|
||||
imageCaption?: T;
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
description?: T;
|
||||
facts?:
|
||||
| T
|
||||
| {
|
||||
num?: T;
|
||||
label?: T;
|
||||
desc?: T;
|
||||
id?: T;
|
||||
};
|
||||
formEyebrow?: T;
|
||||
uploadCta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
uploadPrompt?: T;
|
||||
uploadFooterCta?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
url?: T;
|
||||
};
|
||||
};
|
||||
form?:
|
||||
| T
|
||||
| {
|
||||
stepCompleteLabel?: T;
|
||||
backLabel?: T;
|
||||
nextLabel?: T;
|
||||
submitLabel?: T;
|
||||
yesLabel?: T;
|
||||
noLabel?: T;
|
||||
requiredSuffix?: T;
|
||||
validation?:
|
||||
| T
|
||||
| {
|
||||
choose?: T;
|
||||
required?: T;
|
||||
invalidEmail?: T;
|
||||
};
|
||||
typeStep?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
selfTitle?: T;
|
||||
selfDesc?: T;
|
||||
nominationTitle?: T;
|
||||
nominationDesc?: T;
|
||||
};
|
||||
selfSteps?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
icon?: T;
|
||||
id?: T;
|
||||
};
|
||||
nominationSteps?:
|
||||
| T
|
||||
| {
|
||||
label?: T;
|
||||
icon?: T;
|
||||
id?: T;
|
||||
};
|
||||
employeeOptions?:
|
||||
| T
|
||||
| {
|
||||
value?: T;
|
||||
label?: T;
|
||||
id?: T;
|
||||
};
|
||||
eligibility?:
|
||||
| T
|
||||
| {
|
||||
allowedEmployeeValues?: T;
|
||||
requiredBayernValue?: T;
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
employeesLabel?: T;
|
||||
bayernLabel?: T;
|
||||
ownerLabel?: T;
|
||||
ineligibleHeading?: T;
|
||||
ineligibleBody?: T;
|
||||
ineligibleContactPrefix?: T;
|
||||
ineligibleContactEmail?: T;
|
||||
};
|
||||
selfContact?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
companyLabel?: T;
|
||||
companyPlaceholder?: T;
|
||||
nameLabel?: T;
|
||||
namePlaceholder?: T;
|
||||
emailLabel?: T;
|
||||
emailPlaceholder?: T;
|
||||
phoneLabel?: T;
|
||||
phonePlaceholder?: T;
|
||||
footnote?: T;
|
||||
};
|
||||
selfSummary?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
companyLabel?: T;
|
||||
employeesLabel?: T;
|
||||
locationLabel?: T;
|
||||
locationPrefix?: T;
|
||||
contactLabel?: T;
|
||||
};
|
||||
nominationCompany?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
companyLabel?: T;
|
||||
companyPlaceholder?: T;
|
||||
industryLabel?: T;
|
||||
industryPlaceholder?: T;
|
||||
locationLabel?: T;
|
||||
locationPlaceholder?: T;
|
||||
};
|
||||
nominationContact?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
nameLabel?: T;
|
||||
namePlaceholder?: T;
|
||||
emailLabel?: T;
|
||||
emailPlaceholder?: T;
|
||||
relationshipLabel?: T;
|
||||
relationshipPlaceholder?: T;
|
||||
footnote?: T;
|
||||
};
|
||||
nominationSummary?:
|
||||
| T
|
||||
| {
|
||||
eyebrow?: T;
|
||||
heading?: T;
|
||||
companyLabel?: T;
|
||||
industryLabel?: T;
|
||||
locationLabel?: T;
|
||||
nominatedByLabel?: T;
|
||||
emptyValue?: T;
|
||||
};
|
||||
success?:
|
||||
| T
|
||||
| {
|
||||
selfHeading?: T;
|
||||
nominationHeading?: T;
|
||||
selfPrefix?: T;
|
||||
selfMiddle?: T;
|
||||
selfSuffix?: T;
|
||||
nominationPrefix?: T;
|
||||
nominationMiddle?: T;
|
||||
nominationSuffix?: T;
|
||||
};
|
||||
};
|
||||
};
|
||||
meta?:
|
||||
| T
|
||||
| {
|
||||
|
||||
78
src/scripts/preload-participation-page-cms.ts
Normal file
78
src/scripts/preload-participation-page-cms.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'dotenv/config'
|
||||
|
||||
import config from '@payload-config'
|
||||
import { getPayload, type Payload } from 'payload'
|
||||
|
||||
import { participationContent } from '@/spa/participationContent'
|
||||
|
||||
const mediaByFilename = async (payload: Payload, filename: string) => {
|
||||
const result = await payload.find({
|
||||
collection: 'media',
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
where: { filename: { equals: filename } },
|
||||
})
|
||||
return result.docs[0]?.id
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
const pageResult = await payload.find({
|
||||
collection: 'pages',
|
||||
limit: 1,
|
||||
pagination: false,
|
||||
draft: true,
|
||||
overrideAccess: true,
|
||||
where: {
|
||||
or: [{ spaPath: { equals: '/teilnahme' } }, { slug: { equals: 'teilnahme' } }, { slug: { equals: 'participation' } }],
|
||||
} as never,
|
||||
})
|
||||
|
||||
const page = pageResult.docs[0]
|
||||
if (!page) throw new Error('Participation page not found. Expected spaPath=/teilnahme or slug=teilnahme/participation.')
|
||||
|
||||
const [heroImage, evaluationImage, formImage] = await Promise.all([
|
||||
mediaByFilename(payload, participationContent.hero.backgroundImageFilename),
|
||||
mediaByFilename(payload, participationContent.evaluation.imageFilename),
|
||||
mediaByFilename(payload, participationContent.applicationForm.imageFilename),
|
||||
])
|
||||
const { backgroundImageFilename: _heroFilename, ...heroContent } = participationContent.hero
|
||||
const { imageFilename: _evaluationFilename, ...evaluationContent } = participationContent.evaluation
|
||||
const { imageFilename: _formFilename, ...applicationFormContent } = participationContent.applicationForm
|
||||
|
||||
await payload.update({
|
||||
collection: 'pages',
|
||||
id: page.id,
|
||||
overrideAccess: true,
|
||||
context: { disableRevalidate: true },
|
||||
data: {
|
||||
participation: {
|
||||
hero: {
|
||||
...heroContent,
|
||||
backgroundImage: heroImage,
|
||||
},
|
||||
process: participationContent.process,
|
||||
eligibility: participationContent.eligibility,
|
||||
evaluation: {
|
||||
...evaluationContent,
|
||||
image: evaluationImage,
|
||||
},
|
||||
applicationWays: participationContent.applicationWays,
|
||||
datesBanner: participationContent.datesBanner,
|
||||
applicationForm: {
|
||||
...applicationFormContent,
|
||||
image: formImage,
|
||||
},
|
||||
form: participationContent.form,
|
||||
},
|
||||
} as never,
|
||||
})
|
||||
|
||||
payload.logger.info(`Preloaded participation CMS fields for page ${page.id}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -8,21 +8,36 @@ import {
|
||||
ProcessCardTitle,
|
||||
} from '@/spa/components/ui/process-timeline';
|
||||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||||
import { participationContent } from '@/spa/participationContent';
|
||||
|
||||
const FF = '"IBM Plex Sans", sans-serif';
|
||||
const FB = '"Inter", sans-serif';
|
||||
const NAVY = '#111D55';
|
||||
const GOLD = '#EFBF04';
|
||||
|
||||
const STEPS = [
|
||||
{ step: '01', title: 'Online-Einreichung', desc: 'Bewerbungsformular vollständig ausfüllen. Die Teilnahme ist kostenfrei und ohne bürokratischen Aufwand möglich.', Icon: FileText, date: 'Bis 30. Juni 2026' },
|
||||
{ step: '02', title: 'Formale Vorprüfung', desc: 'Das Gremium sichtet alle Unterlagen auf Vollständigkeit, KMU-Konformität und regionale Zugehörigkeit im Freistaat.', Icon: Search, date: 'Juli 2026' },
|
||||
{ step: '03', title: 'Audit & Jury-Sitzung',desc: 'Die besten Unternehmen werden durch unabhängige Experten-Audits vor Ort evaluiert und in der Jury-Sitzung bewertet.', Icon: UserPlus, date: 'Aug – Sep 2026' },
|
||||
{ step: '04', title: 'Gala-Preisverleihung',desc: 'Die Gewinner werden im Rahmen der feierlichen Gala in München vor geladenen Gästen aus Wirtschaft und Politik geehrt.', Icon: Trophy, date: 'Oktober 2026' },
|
||||
];
|
||||
type ProcessContent = Partial<typeof participationContent.process>;
|
||||
|
||||
export default function WegZurAuszeichnungSection() {
|
||||
const ICONS = {
|
||||
fileText: FileText,
|
||||
search: Search,
|
||||
userPlus: UserPlus,
|
||||
trophy: Trophy,
|
||||
};
|
||||
|
||||
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 > 0 ? (value as T[]) : fallback;
|
||||
|
||||
function Lines({ text }: { text: string }) {
|
||||
return <>{text.split('\n').map((line, i) => <React.Fragment key={`${line}-${i}`}>{i > 0 && <br />}{line}</React.Fragment>)}</>;
|
||||
}
|
||||
|
||||
export default function WegZurAuszeichnungSection({ content }: { content?: ProcessContent }) {
|
||||
const isMobile = useIsMobile();
|
||||
const section = { ...participationContent.process, ...(content || {}) };
|
||||
const steps = fallbackArray(section.steps, participationContent.process.steps);
|
||||
|
||||
// ── MOBILE: stacked vertical list (no scroll-jack, no horizontal overflow) ──
|
||||
if (isMobile) {
|
||||
@@ -30,42 +45,42 @@ export default function WegZurAuszeichnungSection() {
|
||||
<section id="schritte" style={{ background: NAVY, padding: '56px 22px' }}>
|
||||
{/* Section header */}
|
||||
<div style={{ marginBottom: 36 }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 12 }}>Schritt für Schritt</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 12 }}>{fallbackText(section.eyebrow, participationContent.process.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(1.9rem, 8vw, 2.6rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.05, margin: '0 0 16px' }}>
|
||||
IHR WEG ZUM<br />PREIS.
|
||||
<Lines text={fallbackText(section.heading, participationContent.process.heading)} />
|
||||
</h2>
|
||||
<p style={{ fontFamily: FB, fontSize: 15, color: 'rgba(255,255,255,0.65)', lineHeight: 1.7, margin: 0 }}>
|
||||
Von der Einreichung bis zur Gala – vier klar definierte Schritte auf dem Weg zur höchsten Auszeichnung des bayerischen Mittelstands.
|
||||
{fallbackText(section.description, participationContent.process.description)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Stacked step cards */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
{STEPS.map((item, index) => {
|
||||
const Icon = item.Icon;
|
||||
{steps.map((item, index) => {
|
||||
const Icon = ICONS[item.icon as keyof typeof ICONS] || FileText;
|
||||
return (
|
||||
<div key={item.step} style={{ border: '1px solid rgba(239,191,4,0.25)', background: '#111D55', padding: '24px 22px' }}>
|
||||
<div key={`${item.step}-${index}`} style={{ border: '1px solid rgba(239,191,4,0.25)', background: '#111D55', padding: '24px 22px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 20 }}>
|
||||
<div style={{ width: 44, height: 44, border: '1px solid rgba(239,191,4,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||||
<Icon size={18} style={{ color: GOLD }} strokeWidth={1.5} />
|
||||
</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 {item.step}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: GOLD, letterSpacing: '0.18em', textTransform: 'uppercase' }}>Schritt {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' }}>{item.date}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(255,255,255,0.62)', letterSpacing: '0.06em' }}>{fallbackText(item.date, '')}</span>
|
||||
</div>
|
||||
<h3 style={{ fontFamily: FF, fontSize: '1.35rem', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: 0 }}>
|
||||
{item.title}
|
||||
{fallbackText(item.title, '')}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 15, color: 'rgba(255,255,255,0.68)', lineHeight: 1.7, margin: 0 }}>
|
||||
{item.desc}
|
||||
{fallbackText(item.desc, '')}
|
||||
</p>
|
||||
{/* Step progress indicator */}
|
||||
<div style={{ display: 'flex', gap: 4, marginTop: 20 }}>
|
||||
{[0, 1, 2, 3].map(i => (
|
||||
{steps.map((_, i) => (
|
||||
<div key={i} style={{ height: 2, flex: i === index ? 2 : 1, background: i === index ? GOLD : 'rgba(255,255,255,0.1)' }} />
|
||||
))}
|
||||
</div>
|
||||
@@ -84,13 +99,13 @@ export default function WegZurAuszeichnungSection() {
|
||||
{/* Section header */}
|
||||
<div style={{ padding: '56px 80px 40px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 40, alignItems: 'flex-end', borderBottom: '1px solid rgba(255,255,255,0.07)', flexShrink: 0 }}>
|
||||
<div>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 14 }}>Schritt für Schritt</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 14 }}>{fallbackText(section.eyebrow, participationContent.process.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(1.8rem, 3vw, 2.8rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.03, margin: 0 }}>
|
||||
IHR WEG ZUM<br />PREIS.
|
||||
<Lines text={fallbackText(section.heading, participationContent.process.heading)} />
|
||||
</h2>
|
||||
</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 14, color: 'rgba(255,255,255,0.65)', lineHeight: 1.8, margin: 0 }}>
|
||||
Von der Einreichung bis zur Gala – vier klar definierte Schritte auf dem Weg zur höchsten Auszeichnung des bayerischen Mittelstands.
|
||||
{fallbackText(section.description, participationContent.process.description)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -109,23 +124,23 @@ export default function WegZurAuszeichnungSection() {
|
||||
))}
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.55)' }}>
|
||||
Scrollen zum Erkunden
|
||||
{fallbackText(section.scrollHint, participationContent.process.scrollHint)}
|
||||
</span>
|
||||
{/* Progress bar */}
|
||||
<div style={{ flex: 1, height: 1, background: 'rgba(255,255,255,0.07)', position: 'relative', overflow: 'hidden', maxWidth: 200 }}>
|
||||
<div style={{ position: 'absolute', left: 0, top: 0, height: '100%', width: '25%', background: GOLD, opacity: 0.6 }} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, color: 'rgba(255,255,255,0.50)', letterSpacing: '0.1em' }}>01 / 04</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, color: 'rgba(255,255,255,0.50)', letterSpacing: '0.1em' }}>{fallbackText(section.progressLabel, participationContent.process.progressLabel)}</span>
|
||||
</div>
|
||||
|
||||
{/* Cards strip */}
|
||||
<div className="flex flex-nowrap flex-1 items-stretch px-20 pb-16 gap-0">
|
||||
{STEPS.map((item, index) => {
|
||||
const Icon = item.Icon;
|
||||
{steps.map((item, index) => {
|
||||
const Icon = ICONS[item.icon as keyof typeof ICONS] || FileText;
|
||||
return (
|
||||
<ProcessCard
|
||||
key={item.step}
|
||||
itemsLength={4}
|
||||
key={`${item.step}-${index}`}
|
||||
itemsLength={steps.length}
|
||||
index={index}
|
||||
variant="bmp"
|
||||
className="min-w-[70%] max-w-[70%] flex-shrink-0"
|
||||
@@ -136,7 +151,7 @@ export default function WegZurAuszeichnungSection() {
|
||||
<Icon size={16} style={{ color: GOLD }} strokeWidth={1.5} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 11, fontWeight: 900, color: 'rgba(239,191,4,0.4)', letterSpacing: '0.1em', writingMode: 'vertical-rl', transform: 'rotate(180deg)' }}>
|
||||
{item.step}
|
||||
{fallbackText(item.step, '')}
|
||||
</span>
|
||||
</ProcessCardTitle>
|
||||
|
||||
@@ -145,22 +160,22 @@ export default function WegZurAuszeichnungSection() {
|
||||
<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 {item.step}
|
||||
Schritt {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' }}>{item.date}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(255,255,255,0.62)', letterSpacing: '0.08em' }}>{fallbackText(item.date, '')}</span>
|
||||
</div>
|
||||
<div style={{ width: 32, height: 1, background: GOLD, marginBottom: 20, opacity: 0.5 }} />
|
||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.5rem, 2.5vw, 2.2rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: 0 }}>
|
||||
{item.title}
|
||||
{fallbackText(item.title, '')}
|
||||
</h3>
|
||||
</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 15, color: 'rgba(255,255,255,0.68)', lineHeight: 1.75, margin: 0 }}>
|
||||
{item.desc}
|
||||
{fallbackText(item.desc, '')}
|
||||
</p>
|
||||
{/* Step progress indicator */}
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{[0,1,2,3].map(i => (
|
||||
{steps.map((_, i) => (
|
||||
<div key={i} style={{ height: 2, flex: i === index ? 2 : 1, background: i === index ? GOLD : 'rgba(255,255,255,0.1)', transition: 'flex 0.3s' }} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { ArrowRight, ArrowLeft, Check, Trophy, User, Send, Building2, AlertCircle } from 'lucide-react';
|
||||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||||
import { participationContent } from '@/spa/participationContent';
|
||||
|
||||
const FF = '"IBM Plex Sans", sans-serif';
|
||||
|
||||
@@ -138,43 +139,55 @@ type FormData = {
|
||||
nomBeziehung: string;
|
||||
};
|
||||
|
||||
const STEPS_SELBST = [
|
||||
{ label: 'Einreichung', icon: Trophy },
|
||||
{ label: 'Schnell-Check',icon: Check },
|
||||
{ label: 'Kontakt', icon: User },
|
||||
{ label: 'Absenden', icon: Send },
|
||||
];
|
||||
|
||||
const STEPS_VORSCHLAG = [
|
||||
{ label: 'Einreichung', icon: Trophy },
|
||||
{ label: 'Unternehmen', icon: Building2},
|
||||
{ label: 'Nominierender',icon: User },
|
||||
{ label: 'Absenden', icon: Send },
|
||||
];
|
||||
|
||||
const MITARBEITER_OPTIONS = [
|
||||
{ value: 'unter10', label: 'Unter 10' },
|
||||
{ value: '10-50', label: '10 – 50' },
|
||||
{ value: '51-200', label: '51 – 200' },
|
||||
{ value: '201-500', label: '201 – 500'},
|
||||
{ value: 'über500', label: 'Über 500' },
|
||||
];
|
||||
|
||||
const variants = {
|
||||
enter: (dir: number) => ({ x: dir > 0 ? 40 : -40, opacity: 0 }),
|
||||
center: { x: 0, opacity: 1 },
|
||||
exit: (dir: number) => ({ x: dir > 0 ? -40 : 40, opacity: 0 }),
|
||||
};
|
||||
|
||||
type FormContent = Partial<typeof participationContent.form>;
|
||||
type StepItem = { label?: string | null; icon?: string | null };
|
||||
type EmployeeOption = { value?: string | null; label?: string | null };
|
||||
|
||||
const ICONS = {
|
||||
alertCircle: AlertCircle,
|
||||
arrowRight: ArrowRight,
|
||||
building2: Building2,
|
||||
check: Check,
|
||||
send: Send,
|
||||
trophy: Trophy,
|
||||
user: User,
|
||||
};
|
||||
|
||||
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 > 0 ? (value as T[]) : fallback;
|
||||
|
||||
const mergeFormContent = (content?: FormContent) => ({
|
||||
...participationContent.form,
|
||||
...(content || {}),
|
||||
validation: { ...participationContent.form.validation, ...(content?.validation || {}) },
|
||||
typeStep: { ...participationContent.form.typeStep, ...(content?.typeStep || {}) },
|
||||
eligibility: { ...participationContent.form.eligibility, ...(content?.eligibility || {}) },
|
||||
selfContact: { ...participationContent.form.selfContact, ...(content?.selfContact || {}) },
|
||||
selfSummary: { ...participationContent.form.selfSummary, ...(content?.selfSummary || {}) },
|
||||
nominationCompany: { ...participationContent.form.nominationCompany, ...(content?.nominationCompany || {}) },
|
||||
nominationContact: { ...participationContent.form.nominationContact, ...(content?.nominationContact || {}) },
|
||||
nominationSummary: { ...participationContent.form.nominationSummary, ...(content?.nominationSummary || {}) },
|
||||
success: { ...participationContent.form.success, ...(content?.success || {}) },
|
||||
});
|
||||
|
||||
/* ── Sub-components ───────────────────────────────────────────────────── */
|
||||
|
||||
function Field({ label, error, children, optional, c }: {
|
||||
label: string; error?: string; children: React.ReactNode; optional?: boolean; c: C;
|
||||
function Field({ label, error, children, optional, c, requiredSuffix }: {
|
||||
label: string; error?: string; children: React.ReactNode; optional?: boolean; c: C; requiredSuffix: string;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>
|
||||
{label}{!optional && ' *'}
|
||||
{label}{!optional && ` ${requiredSuffix}`}
|
||||
</label>
|
||||
{children}
|
||||
{error && <span style={{ fontFamily: FF, fontSize: 11, color: c.fieldError }}>{error}</span>}
|
||||
@@ -242,28 +255,36 @@ function OptionButton({ selected, onClick, label, c, style }: {
|
||||
);
|
||||
}
|
||||
|
||||
function YesNo({ value, onChange, c }: {
|
||||
value: 'ja' | 'nein' | ''; onChange: (v: 'ja' | 'nein') => void; c: C;
|
||||
function YesNo({ value, onChange, c, yesLabel, noLabel }: {
|
||||
value: 'ja' | 'nein' | ''; onChange: (v: 'ja' | 'nein') => void; c: C; yesLabel: string; noLabel: string;
|
||||
}) {
|
||||
const isMobile = useIsMobile();
|
||||
const flexStyle = isMobile ? { flex: 1 } : undefined;
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<OptionButton selected={value === 'ja'} onClick={() => onChange('ja')} label="Ja" c={c} style={flexStyle} />
|
||||
<OptionButton selected={value === 'nein'} onClick={() => onChange('nein')} label="Nein" c={c} style={flexStyle} />
|
||||
<OptionButton selected={value === 'ja'} onClick={() => onChange('ja')} label={yesLabel} c={c} style={flexStyle} />
|
||||
<OptionButton selected={value === 'nein'} onClick={() => onChange('nein')} label={noLabel} c={c} style={flexStyle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function isEligible(data: FormData) {
|
||||
return ['10-50', '51-200', '201-500'].includes(data.mitarbeiter) && data.standortBayern === 'ja';
|
||||
function isEligible(data: FormData, formContent: ReturnType<typeof mergeFormContent>) {
|
||||
const allowed = fallbackText(formContent.eligibility.allowedEmployeeValues, participationContent.form.eligibility.allowedEmployeeValues)
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
return allowed.includes(data.mitarbeiter) && data.standortBayern === fallbackText(formContent.eligibility.requiredBayernValue, 'ja');
|
||||
}
|
||||
|
||||
/* ── Main component ───────────────────────────────────────────────────── */
|
||||
|
||||
export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'gold' }) {
|
||||
export default function BewerbungsForm({ theme = 'dark', content }: { theme?: 'dark' | 'gold'; content?: FormContent }) {
|
||||
const c = theme === 'gold' ? GOLD : DARK;
|
||||
const isMobile = useIsMobile();
|
||||
const formContent = mergeFormContent(content);
|
||||
const selfSteps = fallbackArray<StepItem>(formContent.selfSteps, participationContent.form.selfSteps);
|
||||
const nominationSteps = fallbackArray<StepItem>(formContent.nominationSteps, participationContent.form.nominationSteps);
|
||||
const employeeOptions = fallbackArray<EmployeeOption>(formContent.employeeOptions, participationContent.form.employeeOptions);
|
||||
|
||||
const [step, setStep] = useState(0);
|
||||
const [dir, setDir] = useState(1);
|
||||
@@ -276,7 +297,7 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
});
|
||||
const [errors, setErrors] = useState<Partial<Record<keyof FormData, string>>>({});
|
||||
|
||||
const STEPS = data.type === 'vorschlag' ? STEPS_VORSCHLAG : STEPS_SELBST;
|
||||
const STEPS = data.type === 'vorschlag' ? nominationSteps : selfSteps;
|
||||
const progress = step === 0 ? 0 : (step / (STEPS.length - 1)) * 100;
|
||||
|
||||
const set = (k: keyof FormData, v: string) => {
|
||||
@@ -288,25 +309,25 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
const e: typeof errors = {};
|
||||
if (data.type === 'selbst') {
|
||||
if (step === 1) {
|
||||
if (!data.mitarbeiter) e.mitarbeiter = 'Bitte wählen';
|
||||
if (!data.standortBayern) e.standortBayern = 'Bitte wählen';
|
||||
if (!data.familiengeführt)e.familiengeführt= 'Bitte wählen';
|
||||
if (!data.mitarbeiter) e.mitarbeiter = fallbackText(formContent.validation.choose, participationContent.form.validation.choose);
|
||||
if (!data.standortBayern) e.standortBayern = fallbackText(formContent.validation.choose, participationContent.form.validation.choose);
|
||||
if (!data.familiengeführt)e.familiengeführt= fallbackText(formContent.validation.choose, participationContent.form.validation.choose);
|
||||
}
|
||||
if (step === 2) {
|
||||
if (!data.firmenname) e.firmenname = 'Pflichtfeld';
|
||||
if (!data.kontaktName) e.kontaktName = 'Pflichtfeld';
|
||||
if (!data.email) e.email = 'Pflichtfeld';
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) e.email = 'Ungültige E-Mail';
|
||||
if (!data.firmenname) e.firmenname = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
if (!data.kontaktName) e.kontaktName = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
if (!data.email) e.email = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) e.email = fallbackText(formContent.validation.invalidEmail, participationContent.form.validation.invalidEmail);
|
||||
}
|
||||
} else {
|
||||
if (step === 1) {
|
||||
if (!data.nomFirma) e.nomFirma = 'Pflichtfeld';
|
||||
if (!data.nomBranche) e.nomBranche = 'Pflichtfeld';
|
||||
if (!data.nomFirma) e.nomFirma = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
if (!data.nomBranche) e.nomBranche = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
}
|
||||
if (step === 2) {
|
||||
if (!data.nomName) e.nomName = 'Pflichtfeld';
|
||||
if (!data.nomEmail) e.nomEmail = 'Pflichtfeld';
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.nomEmail)) e.nomEmail = 'Ungültige E-Mail';
|
||||
if (!data.nomName) e.nomName = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
if (!data.nomEmail) e.nomEmail = fallbackText(formContent.validation.required, participationContent.form.validation.required);
|
||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.nomEmail)) e.nomEmail = fallbackText(formContent.validation.invalidEmail, participationContent.form.validation.invalidEmail);
|
||||
}
|
||||
}
|
||||
setErrors(e);
|
||||
@@ -325,12 +346,12 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
<Check size={22} color={c.successIconInner} strokeWidth={3} />
|
||||
</div>
|
||||
<h3 style={{ fontSize: 18, fontWeight: 900, color: c.successHeading, textTransform: 'uppercase', letterSpacing: '-0.01em', marginBottom: 10 }}>
|
||||
{data.type === 'selbst' ? 'Anfrage eingegangen' : 'Nominierung eingegangen'}
|
||||
{data.type === 'selbst' ? fallbackText(formContent.success.selfHeading, participationContent.form.success.selfHeading) : fallbackText(formContent.success.nominationHeading, participationContent.form.success.nominationHeading)}
|
||||
</h3>
|
||||
<p style={{ fontSize: 13, color: c.successBody, maxWidth: 340, margin: '0 auto', lineHeight: 1.7 }}>
|
||||
{data.type === 'selbst'
|
||||
? <><span>Vielen Dank, </span><strong style={{ color: c.successName }}>{data.firmenname}</strong><span>. Wir senden Ihnen den vollständigen Fragebogen an </span><strong style={{ color: c.successAccent }}>{data.email}</strong>.</>
|
||||
: <><span>Vielen Dank, </span><strong style={{ color: c.successName }}>{data.nomName}</strong><span>. Wir nehmen Kontakt mit </span><strong style={{ color: c.successAccent }}>{data.nomFirma}</strong><span> auf und informieren sie über Ihre Nominierung.</span></>
|
||||
? <><span>{fallbackText(formContent.success.selfPrefix, participationContent.form.success.selfPrefix)}</span><strong style={{ color: c.successName }}>{data.firmenname}</strong><span>{fallbackText(formContent.success.selfMiddle, participationContent.form.success.selfMiddle)}</span><strong style={{ color: c.successAccent }}>{data.email}</strong><span>{fallbackText(formContent.success.selfSuffix, participationContent.form.success.selfSuffix)}</span></>
|
||||
: <><span>{fallbackText(formContent.success.nominationPrefix, participationContent.form.success.nominationPrefix)}</span><strong style={{ color: c.successName }}>{data.nomName}</strong><span>{fallbackText(formContent.success.nominationMiddle, participationContent.form.success.nominationMiddle)}</span><strong style={{ color: c.successAccent }}>{data.nomFirma}</strong><span>{fallbackText(formContent.success.nominationSuffix, participationContent.form.success.nominationSuffix)}</span></>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
@@ -349,7 +370,7 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{/* Step indicators */}
|
||||
<div style={{ display: 'flex', borderBottom: `1px solid ${c.border}`, flexShrink: 0 }}>
|
||||
{STEPS.map((s, i) => {
|
||||
const Icon = s.icon;
|
||||
const Icon = ICONS[s.icon as keyof typeof ICONS] || Trophy;
|
||||
const done = i < step;
|
||||
const active = i === step;
|
||||
return (
|
||||
@@ -363,7 +384,7 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
marginBottom: -1, transition: 'color 0.2s',
|
||||
}}>
|
||||
<Icon size={11} strokeWidth={active ? 2 : 1.5} />
|
||||
<span>{done ? '✓' : s.label}</span>
|
||||
<span>{done ? fallbackText(formContent.stepCompleteLabel, participationContent.form.stepCompleteLabel) : fallbackText(s.label, '')}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -379,14 +400,14 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{/* ── Step 0: Typ-Wahl ── */}
|
||||
{step === 0 && (
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 8 }}>Schritt 1</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em', marginBottom: 14 }}>Art der Einreichung</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 8 }}>{fallbackText(formContent.typeStep.eyebrow, participationContent.form.typeStep.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em', marginBottom: 14 }}>{fallbackText(formContent.typeStep.heading, participationContent.form.typeStep.heading)}</h3>
|
||||
<div style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', gap: 8 }}>
|
||||
<TypeCard c={c} selected={data.type === 'selbst'} title="Eigenbewerbung"
|
||||
desc="Ich bewerbe mein eigenes Unternehmen für den Bayerischen Mittelstandspreis."
|
||||
<TypeCard c={c} selected={data.type === 'selbst'} title={fallbackText(formContent.typeStep.selfTitle, participationContent.form.typeStep.selfTitle)}
|
||||
desc={fallbackText(formContent.typeStep.selfDesc, participationContent.form.typeStep.selfDesc)}
|
||||
onClick={() => { set('type', 'selbst'); setDir(1); setStep(1); }} />
|
||||
<TypeCard c={c} selected={data.type === 'vorschlag'} title="Vorschlag"
|
||||
desc="Ich schlage ein anderes Unternehmen vor, das den Preis verdient."
|
||||
<TypeCard c={c} selected={data.type === 'vorschlag'} title={fallbackText(formContent.typeStep.nominationTitle, participationContent.form.typeStep.nominationTitle)}
|
||||
desc={fallbackText(formContent.typeStep.nominationDesc, participationContent.form.typeStep.nominationDesc)}
|
||||
onClick={() => { set('type', 'vorschlag'); setDir(1); setStep(1); }} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -396,27 +417,27 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{step === 1 && data.type === 'selbst' && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Schnell-Check</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Grundlegende Eignung</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.eligibility.eyebrow, participationContent.form.eligibility.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.eligibility.heading, participationContent.form.eligibility.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>Wie viele Mitarbeiter hat Ihr Unternehmen? *</label>
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>{fallbackText(formContent.eligibility.employeesLabel, participationContent.form.eligibility.employeesLabel)} {fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}</label>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
||||
{MITARBEITER_OPTIONS.map(o => (
|
||||
<OptionButton c={c} key={o.value} selected={data.mitarbeiter === o.value} onClick={() => set('mitarbeiter', o.value)} label={o.label} />
|
||||
{employeeOptions.map(o => (
|
||||
<OptionButton c={c} key={fallbackText(o.value, '')} selected={data.mitarbeiter === o.value} onClick={() => set('mitarbeiter', fallbackText(o.value, ''))} label={fallbackText(o.label, '')} />
|
||||
))}
|
||||
</div>
|
||||
{errors.mitarbeiter && <span style={{ fontFamily: FF, fontSize: 11, color: c.fieldError }}>{errors.mitarbeiter}</span>}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>Hat Ihr Unternehmen seinen Sitz in Bayern? *</label>
|
||||
<YesNo c={c} value={data.standortBayern} onChange={v => set('standortBayern', v)} />
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>{fallbackText(formContent.eligibility.bayernLabel, participationContent.form.eligibility.bayernLabel)} {fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}</label>
|
||||
<YesNo c={c} value={data.standortBayern} onChange={v => set('standortBayern', v)} yesLabel={fallbackText(formContent.yesLabel, participationContent.form.yesLabel)} noLabel={fallbackText(formContent.noLabel, participationContent.form.noLabel)} />
|
||||
{errors.standortBayern && <span style={{ fontFamily: FF, fontSize: 11, color: c.fieldError }}>{errors.standortBayern}</span>}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>Ist Ihr Unternehmen inhabergeführt oder familiengeführt? *</label>
|
||||
<YesNo c={c} value={data.familiengeführt} onChange={v => set('familiengeführt', v)} />
|
||||
<label style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: c.fieldLabel }}>{fallbackText(formContent.eligibility.ownerLabel, participationContent.form.eligibility.ownerLabel)} {fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}</label>
|
||||
<YesNo c={c} value={data.familiengeführt} onChange={v => set('familiengeführt', v)} yesLabel={fallbackText(formContent.yesLabel, participationContent.form.yesLabel)} noLabel={fallbackText(formContent.noLabel, participationContent.form.noLabel)} />
|
||||
{errors.familiengeführt && <span style={{ fontFamily: FF, fontSize: 11, color: c.fieldError }}>{errors.familiengeführt}</span>}
|
||||
</div>
|
||||
</div>
|
||||
@@ -425,42 +446,42 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
|
||||
{/* ── Eigenbewerbung Step 2: Kontakt / Ineligible ── */}
|
||||
{step === 2 && data.type === 'selbst' && (() => {
|
||||
if (!isEligible(data)) return (
|
||||
if (!isEligible(data, formContent)) return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, alignItems: 'flex-start' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<AlertCircle size={18} color={c.alertIcon} />
|
||||
<h3 style={{ fontFamily: FF, fontSize: 15, fontWeight: 800, color: c.alertTitle, textTransform: 'uppercase' }}>Leider nicht förderfähig</h3>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 15, fontWeight: 800, color: c.alertTitle, textTransform: 'uppercase' }}>{fallbackText(formContent.eligibility.ineligibleHeading, participationContent.form.eligibility.ineligibleHeading)}</h3>
|
||||
</div>
|
||||
<p style={{ fontFamily: FF, fontSize: 13, color: c.bodyText, lineHeight: 1.7 }}>
|
||||
Der Bayerische Mittelstandspreis richtet sich an inhabergeführte Unternehmen mit 10–500 Mitarbeitern und Sitz in Bayern. Ihr Unternehmen erfüllt diese Voraussetzungen aktuell nicht.
|
||||
{fallbackText(formContent.eligibility.ineligibleBody, participationContent.form.eligibility.ineligibleBody)}
|
||||
</p>
|
||||
<p style={{ fontFamily: FF, fontSize: 13, color: c.mutedText, lineHeight: 1.7 }}>
|
||||
Fragen? Schreiben Sie uns: <span style={{ color: c.alertEmail }}>info@bmp-bayern.de</span>
|
||||
{fallbackText(formContent.eligibility.ineligibleContactPrefix, participationContent.form.eligibility.ineligibleContactPrefix)} <span style={{ color: c.alertEmail }}>{fallbackText(formContent.eligibility.ineligibleContactEmail, participationContent.form.eligibility.ineligibleContactEmail)}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Kontakt</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Ihre Kontaktdaten</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.selfContact.eyebrow, participationContent.form.selfContact.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.selfContact.heading, participationContent.form.selfContact.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
|
||||
<Field c={c} label="Firmenname" error={errors.firmenname}>
|
||||
<StyledInput c={c} value={data.firmenname} onChange={e => set('firmenname', e.target.value)} placeholder="Muster GmbH" />
|
||||
<Field c={c} label={fallbackText(formContent.selfContact.companyLabel, participationContent.form.selfContact.companyLabel)} error={errors.firmenname} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.firmenname} onChange={e => set('firmenname', e.target.value)} placeholder={fallbackText(formContent.selfContact.companyPlaceholder, participationContent.form.selfContact.companyPlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Ihr Name" error={errors.kontaktName}>
|
||||
<StyledInput c={c} value={data.kontaktName} onChange={e => set('kontaktName', e.target.value)} placeholder="Vor- und Nachname" />
|
||||
<Field c={c} label={fallbackText(formContent.selfContact.nameLabel, participationContent.form.selfContact.nameLabel)} error={errors.kontaktName} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.kontaktName} onChange={e => set('kontaktName', e.target.value)} placeholder={fallbackText(formContent.selfContact.namePlaceholder, participationContent.form.selfContact.namePlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="E-Mail" error={errors.email}>
|
||||
<StyledInput c={c} type="email" value={data.email} onChange={e => set('email', e.target.value)} placeholder="name@unternehmen.de" />
|
||||
<Field c={c} label={fallbackText(formContent.selfContact.emailLabel, participationContent.form.selfContact.emailLabel)} error={errors.email} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} type="email" value={data.email} onChange={e => set('email', e.target.value)} placeholder={fallbackText(formContent.selfContact.emailPlaceholder, participationContent.form.selfContact.emailPlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Telefon" optional>
|
||||
<StyledInput c={c} type="tel" value={data.telefon} onChange={e => set('telefon', e.target.value)} placeholder="+49 89 ..." />
|
||||
<Field c={c} label={fallbackText(formContent.selfContact.phoneLabel, participationContent.form.selfContact.phoneLabel)} optional requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} type="tel" value={data.telefon} onChange={e => set('telefon', e.target.value)} placeholder={fallbackText(formContent.selfContact.phonePlaceholder, participationContent.form.selfContact.phonePlaceholder)} />
|
||||
</Field>
|
||||
</div>
|
||||
<p style={{ fontFamily: FF, fontSize: 12, color: c.mutedText, lineHeight: 1.6 }}>
|
||||
Wir senden Ihnen den vollständigen Fragebogen automatisch per E-Mail zu.
|
||||
{fallbackText(formContent.selfContact.footnote, participationContent.form.selfContact.footnote)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -470,15 +491,15 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{step === 3 && data.type === 'selbst' && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Zusammenfassung</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Ihre Bewerbung</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.selfSummary.eyebrow, participationContent.form.selfSummary.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.selfSummary.heading, participationContent.form.selfSummary.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ borderTop: `1px solid ${c.border}` }}>
|
||||
{[
|
||||
{ label: 'Unternehmen', value: data.firmenname },
|
||||
{ label: 'Mitarbeiter', value: MITARBEITER_OPTIONS.find(o => o.value === data.mitarbeiter)?.label || '' },
|
||||
{ label: 'Standort', value: `Bayern: ${data.standortBayern === 'ja' ? 'Ja' : 'Nein'}` },
|
||||
{ label: 'Kontakt', value: `${data.kontaktName} · ${data.email}` },
|
||||
{ label: fallbackText(formContent.selfSummary.companyLabel, participationContent.form.selfSummary.companyLabel), value: data.firmenname },
|
||||
{ label: fallbackText(formContent.selfSummary.employeesLabel, participationContent.form.selfSummary.employeesLabel), value: employeeOptions.find(o => o.value === data.mitarbeiter)?.label || '' },
|
||||
{ label: fallbackText(formContent.selfSummary.locationLabel, participationContent.form.selfSummary.locationLabel), value: `${fallbackText(formContent.selfSummary.locationPrefix, participationContent.form.selfSummary.locationPrefix)} ${data.standortBayern === 'ja' ? fallbackText(formContent.yesLabel, participationContent.form.yesLabel) : fallbackText(formContent.noLabel, participationContent.form.noLabel)}` },
|
||||
{ label: fallbackText(formContent.selfSummary.contactLabel, participationContent.form.selfSummary.contactLabel), value: `${data.kontaktName} · ${data.email}` },
|
||||
].map(row => (
|
||||
<div key={row.label} style={{ display: 'grid', gridTemplateColumns: '90px 1fr', gap: 12, padding: '10px 0', borderBottom: `1px solid ${c.border}`, alignItems: 'baseline' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: c.rowLabel, textTransform: 'uppercase', letterSpacing: '0.12em' }}>{row.label}</span>
|
||||
@@ -493,18 +514,18 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{step === 1 && data.type === 'vorschlag' && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Nominierung</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Nominiertes Unternehmen</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.nominationCompany.eyebrow, participationContent.form.nominationCompany.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.nominationCompany.heading, participationContent.form.nominationCompany.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
|
||||
<Field c={c} label="Firmenname" error={errors.nomFirma}>
|
||||
<StyledInput c={c} value={data.nomFirma} onChange={e => set('nomFirma', e.target.value)} placeholder="Muster GmbH" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationCompany.companyLabel, participationContent.form.nominationCompany.companyLabel)} error={errors.nomFirma} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.nomFirma} onChange={e => set('nomFirma', e.target.value)} placeholder={fallbackText(formContent.nominationCompany.companyPlaceholder, participationContent.form.nominationCompany.companyPlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Branche" error={errors.nomBranche}>
|
||||
<StyledInput c={c} value={data.nomBranche} onChange={e => set('nomBranche', e.target.value)} placeholder="z. B. Maschinenbau" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationCompany.industryLabel, participationContent.form.nominationCompany.industryLabel)} error={errors.nomBranche} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.nomBranche} onChange={e => set('nomBranche', e.target.value)} placeholder={fallbackText(formContent.nominationCompany.industryPlaceholder, participationContent.form.nominationCompany.industryPlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Standort Bayern" optional>
|
||||
<StyledInput c={c} value={data.nomStandort} onChange={e => set('nomStandort', e.target.value)} placeholder="z. B. München" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationCompany.locationLabel, participationContent.form.nominationCompany.locationLabel)} optional requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.nomStandort} onChange={e => set('nomStandort', e.target.value)} placeholder={fallbackText(formContent.nominationCompany.locationPlaceholder, participationContent.form.nominationCompany.locationPlaceholder)} />
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
@@ -514,22 +535,22 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{step === 2 && data.type === 'vorschlag' && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Nominierender</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Ihre Angaben</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.nominationContact.eyebrow, participationContent.form.nominationContact.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.nominationContact.heading, participationContent.form.nominationContact.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
|
||||
<Field c={c} label="Ihr Name" error={errors.nomName}>
|
||||
<StyledInput c={c} value={data.nomName} onChange={e => set('nomName', e.target.value)} placeholder="Vor- und Nachname" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationContact.nameLabel, participationContent.form.nominationContact.nameLabel)} error={errors.nomName} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.nomName} onChange={e => set('nomName', e.target.value)} placeholder={fallbackText(formContent.nominationContact.namePlaceholder, participationContent.form.nominationContact.namePlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Ihre E-Mail" error={errors.nomEmail}>
|
||||
<StyledInput c={c} type="email" value={data.nomEmail} onChange={e => set('nomEmail', e.target.value)} placeholder="ihre@email.de" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationContact.emailLabel, participationContent.form.nominationContact.emailLabel)} error={errors.nomEmail} requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} type="email" value={data.nomEmail} onChange={e => set('nomEmail', e.target.value)} placeholder={fallbackText(formContent.nominationContact.emailPlaceholder, participationContent.form.nominationContact.emailPlaceholder)} />
|
||||
</Field>
|
||||
<Field c={c} label="Ihre Beziehung zum Unternehmen" optional>
|
||||
<StyledInput c={c} value={data.nomBeziehung} onChange={e => set('nomBeziehung', e.target.value)} placeholder="z. B. Kunde, Partner, Bekannter" />
|
||||
<Field c={c} label={fallbackText(formContent.nominationContact.relationshipLabel, participationContent.form.nominationContact.relationshipLabel)} optional requiredSuffix={fallbackText(formContent.requiredSuffix, participationContent.form.requiredSuffix)}>
|
||||
<StyledInput c={c} value={data.nomBeziehung} onChange={e => set('nomBeziehung', e.target.value)} placeholder={fallbackText(formContent.nominationContact.relationshipPlaceholder, participationContent.form.nominationContact.relationshipPlaceholder)} />
|
||||
</Field>
|
||||
</div>
|
||||
<p style={{ fontFamily: FF, fontSize: 12, color: c.mutedText, lineHeight: 1.6 }}>
|
||||
Wir nehmen Kontakt mit dem nominierten Unternehmen auf und informieren es über Ihre Nominierung.
|
||||
{fallbackText(formContent.nominationContact.footnote, participationContent.form.nominationContact.footnote)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -538,15 +559,15 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
{step === 3 && data.type === 'vorschlag' && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>Zusammenfassung</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>Ihre Nominierung</h3>
|
||||
<p style={{ fontFamily: FF, fontSize: 10, color: c.accentLabel, textTransform: 'uppercase', letterSpacing: '0.25em', fontWeight: 700, marginBottom: 6 }}>{fallbackText(formContent.nominationSummary.eyebrow, participationContent.form.nominationSummary.eyebrow)}</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 16, fontWeight: 900, color: c.heading, textTransform: 'uppercase', letterSpacing: '-0.01em' }}>{fallbackText(formContent.nominationSummary.heading, participationContent.form.nominationSummary.heading)}</h3>
|
||||
</div>
|
||||
<div style={{ borderTop: `1px solid ${c.border}` }}>
|
||||
{[
|
||||
{ label: 'Unternehmen', value: data.nomFirma },
|
||||
{ label: 'Branche', value: data.nomBranche },
|
||||
{ label: 'Standort', value: data.nomStandort || '–' },
|
||||
{ label: 'Nominiert von',value: `${data.nomName} · ${data.nomEmail}` },
|
||||
{ label: fallbackText(formContent.nominationSummary.companyLabel, participationContent.form.nominationSummary.companyLabel), value: data.nomFirma },
|
||||
{ label: fallbackText(formContent.nominationSummary.industryLabel, participationContent.form.nominationSummary.industryLabel), value: data.nomBranche },
|
||||
{ label: fallbackText(formContent.nominationSummary.locationLabel, participationContent.form.nominationSummary.locationLabel), value: data.nomStandort || fallbackText(formContent.nominationSummary.emptyValue, participationContent.form.nominationSummary.emptyValue) },
|
||||
{ label: fallbackText(formContent.nominationSummary.nominatedByLabel, participationContent.form.nominationSummary.nominatedByLabel),value: `${data.nomName} · ${data.nomEmail}` },
|
||||
].map(row => (
|
||||
<div key={row.label} style={{ display: 'grid', gridTemplateColumns: '100px 1fr', gap: 12, padding: '10px 0', borderBottom: `1px solid ${c.border}`, alignItems: 'baseline' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: c.rowLabel, textTransform: 'uppercase', letterSpacing: '0.12em' }}>{row.label}</span>
|
||||
@@ -574,11 +595,11 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
onMouseEnter={e => (e.currentTarget.style.color = c.backTextHover)}
|
||||
onMouseLeave={e => (e.currentTarget.style.color = c.backText)}
|
||||
>
|
||||
<ArrowLeft size={12} /> Zurück
|
||||
<ArrowLeft size={12} /> {fallbackText(formContent.backLabel, participationContent.form.backLabel)}
|
||||
</button>
|
||||
|
||||
{step < STEPS.length - 1 ? (
|
||||
step === 2 && data.type === 'selbst' && !isEligible(data) ? <div /> :
|
||||
step === 2 && data.type === 'selbst' && !isEligible(data, formContent) ? <div /> :
|
||||
<button type="button" onClick={next} style={{
|
||||
fontFamily: FF, fontSize: 12, fontWeight: 700, textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em', color: c.btnText, background: c.btnBg,
|
||||
@@ -588,7 +609,7 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = c.btnBgHover; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = c.btnBg; }}
|
||||
>
|
||||
Weiter <ArrowRight size={12} />
|
||||
{fallbackText(formContent.nextLabel, participationContent.form.nextLabel)} <ArrowRight size={12} />
|
||||
</button>
|
||||
) : (
|
||||
<button type="button" onClick={submit} style={{
|
||||
@@ -600,7 +621,7 @@ export default function BewerbungsForm({ theme = 'dark' }: { theme?: 'dark' | 'g
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = c.btnBgHover; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = c.btnBg; }}
|
||||
>
|
||||
Absenden <Send size={12} />
|
||||
{fallbackText(formContent.submitLabel, participationContent.form.submitLabel)} <Send size={12} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { MapPin, Users, TrendingUp, Building2, ArrowRight, CheckCircle2, UserCheck, Send, Star, type LucideIcon } from 'lucide-react';
|
||||
import { MapPin, Users, TrendingUp, Building2, ArrowRight, CheckCircle2, UserCheck, Send, Star } from 'lucide-react';
|
||||
import { Link } from '@/spa/router';
|
||||
import BewerbungsForm from '@/spa/components/forms/BewerbungsForm';
|
||||
import WegZurAuszeichnungSection from '@/spa/components/WegZurAuszeichnungSection';
|
||||
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
|
||||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||||
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField';
|
||||
import { useCmsRoute } from '@/spa/cmsRoute';
|
||||
import { participationContent } from '@/spa/participationContent';
|
||||
import Image from '@/spa/components/ui/UnoptimizedImage'
|
||||
|
||||
const FF = '"IBM Plex Sans", sans-serif';
|
||||
@@ -13,9 +16,50 @@ const NAVY = '#111D55';
|
||||
const GOLD = '#EFBF04';
|
||||
const CREAM = '#E4E2E3';
|
||||
|
||||
type ParticipationCms = Partial<typeof participationContent> & {
|
||||
hero?: Partial<typeof participationContent.hero> & { backgroundImage?: unknown };
|
||||
evaluation?: Partial<typeof participationContent.evaluation> & { image?: unknown };
|
||||
applicationForm?: Partial<typeof participationContent.applicationForm> & { image?: unknown };
|
||||
};
|
||||
|
||||
const ICONS = {
|
||||
building2: Building2,
|
||||
mapPin: MapPin,
|
||||
send: Send,
|
||||
star: Star,
|
||||
trendingUp: TrendingUp,
|
||||
userCheck: UserCheck,
|
||||
users: Users,
|
||||
};
|
||||
|
||||
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 > 0 ? (value as T[]) : fallback;
|
||||
|
||||
function Lines({ text }: { text: string }) {
|
||||
return <>{text.split('\n').map((line, i) => <React.Fragment key={`${line}-${i}`}>{i > 0 && <br />}{line}</React.Fragment>)}</>;
|
||||
}
|
||||
|
||||
const Participation: React.FC = () => {
|
||||
const isMobile = useIsMobile();
|
||||
const qualSectionRef = useRef<HTMLElement>(null);
|
||||
const cms = (useCmsRoute()?.doc?.participation || {}) as ParticipationCms;
|
||||
const hero = { ...participationContent.hero, ...(cms.hero || {}) };
|
||||
const process = { ...participationContent.process, ...(cms.process || {}) };
|
||||
const eligibility = { ...participationContent.eligibility, ...(cms.eligibility || {}) };
|
||||
const evaluation = { ...participationContent.evaluation, ...(cms.evaluation || {}) };
|
||||
const applicationWays = { ...participationContent.applicationWays, ...(cms.applicationWays || {}) };
|
||||
const datesBanner = { ...participationContent.datesBanner, ...(cms.datesBanner || {}) };
|
||||
const applicationForm = { ...participationContent.applicationForm, ...(cms.applicationForm || {}) };
|
||||
const form = { ...participationContent.form, ...(cms.form || {}) };
|
||||
const eligibilityCriteria = fallbackArray(eligibility.criteria, participationContent.eligibility.criteria);
|
||||
const eligibilityNotes = fallbackArray(eligibility.notes, participationContent.eligibility.notes);
|
||||
const evaluationCriteria = fallbackArray(evaluation.criteria, participationContent.evaluation.criteria);
|
||||
const mobileDates = fallbackArray(datesBanner.mobileItems, participationContent.datesBanner.mobileItems);
|
||||
const desktopDates = fallbackArray(datesBanner.desktopItems, participationContent.datesBanner.desktopItems);
|
||||
const applicationFacts = fallbackArray(applicationForm.facts, participationContent.applicationForm.facts);
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
@@ -23,8 +67,8 @@ const Participation: React.FC = () => {
|
||||
{/* ── HERO ─────────────────────────────────────────────────────────────── */}
|
||||
<section style={{ position: 'relative', minHeight: '65vh', display: 'flex', alignItems: 'flex-end', overflow: 'hidden', background: NAVY }}>
|
||||
<Image unoptimized
|
||||
src="/images/preisuebergabe.jpg"
|
||||
alt="Teilnahme BMP"
|
||||
src={mediaUrl(hero.backgroundImage, '/images/preisuebergabe.jpg')}
|
||||
alt={mediaAlt(hero.backgroundImage, fallbackText(hero.imageAlt, participationContent.hero.imageAlt))}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
|
||||
/>
|
||||
<div style={{ position: 'absolute', inset: 0, background: isMobile ? 'linear-gradient(to top, rgba(2,9,48,0.95) 0%, rgba(2,9,48,0.72) 38%, rgba(2,9,48,0.22) 72%, transparent 100%)' : 'linear-gradient(to right, #020930 0%, rgba(2,9,48,0.90) 38%, rgba(2,9,48,0.18) 65%, transparent 100%)' }} />
|
||||
@@ -33,20 +77,20 @@ const Participation: React.FC = () => {
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '0 24px 48px' : '0 80px 80px', maxWidth: 800 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 24 }}>
|
||||
<div style={{ width: 36, height: 2, background: GOLD }} />
|
||||
<span style={{ fontFamily: FF, fontSize: 12, fontWeight: 700, letterSpacing: '0.28em', textTransform: 'uppercase', color: '#EFBF04' }}>Bewerbungsphase 2026</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 12, fontWeight: 700, letterSpacing: '0.28em', textTransform: 'uppercase', color: '#EFBF04' }}>{fallbackText(hero.eyebrow, participationContent.hero.eyebrow)}</span>
|
||||
</div>
|
||||
<h1 style={{ fontFamily: FF, fontSize: isMobile ? 'clamp(1.6rem, 6vw, 5rem)' : 'clamp(2.8rem, 6vw, 5rem)', fontWeight: 900, color: '#fff', lineHeight: 0.95, letterSpacing: '-0.03em', textTransform: 'uppercase', margin: '0 0 24px' }}>
|
||||
GESTALTEN SIE<br />BAYERNS ZUKUNFT.
|
||||
<Lines text={fallbackText(hero.heading, participationContent.hero.heading)} />
|
||||
</h1>
|
||||
<div style={{ width: 48, height: 2, background: GOLD, marginBottom: 24 }} />
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.6)', lineHeight: 1.7, maxWidth: 520, fontWeight: 300 }}>
|
||||
Alles, was Sie für Ihre erfolgreiche Bewerbung zum Bayerischen Mittelstandspreis wissen müssen.
|
||||
{fallbackText(hero.description, participationContent.hero.description)}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── TIMELINE – direkt nach Hero ──────────────────────────────────────── */}
|
||||
<WegZurAuszeichnungSection />
|
||||
<WegZurAuszeichnungSection content={process} />
|
||||
|
||||
{/* ── QUALIFICATIONS ───────────────────────────────────────────────────── */}
|
||||
<section id="voraussetzungen" ref={qualSectionRef} style={{ background: CREAM, overflow: 'hidden', position: 'relative', isolation: 'isolate' }}>
|
||||
@@ -55,45 +99,40 @@ const Participation: React.FC = () => {
|
||||
{/* Header */}
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '48px 24px 32px' : '80px 80px 56px', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: isMobile ? 24 : 48, alignItems: 'flex-end', borderBottom: `1px solid #D0D5DD` }}>
|
||||
<div>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 16 }}>Berechtigung</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 16 }}>{fallbackText(eligibility.eyebrow, participationContent.eligibility.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 3.5vw, 3rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.03, margin: 0 }}>
|
||||
WER KANN<br />TEILNEHMEN?
|
||||
<Lines text={fallbackText(eligibility.heading, participationContent.eligibility.heading)} />
|
||||
</h2>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: isMobile ? 'flex-start' : 'flex-end', justifyContent: 'space-between', gap: 24 }}>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, flexShrink: 0 }}>
|
||||
<Link
|
||||
to="#bewerben"
|
||||
to={fallbackText(eligibility.primaryCta?.url, participationContent.eligibility.primaryCta.url)}
|
||||
style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#101828', background: GOLD, padding: '11px 22px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'opacity 0.2s', whiteSpace: 'nowrap' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '0.85')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}
|
||||
>
|
||||
Jetzt bewerben <ArrowRight size={13} />
|
||||
{fallbackText(eligibility.primaryCta?.label, participationContent.eligibility.primaryCta.label)} <ArrowRight size={13} />
|
||||
</Link>
|
||||
<Link
|
||||
to="/mitglied-werden"
|
||||
to={fallbackText(eligibility.secondaryCta?.url, participationContent.eligibility.secondaryCta.url)}
|
||||
style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: NAVY, border: `1.5px solid rgba(3,9,58,0.3)`, padding: '10px 22px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'border-color 0.2s, color 0.2s', whiteSpace: 'nowrap' }}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.borderColor = NAVY; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(3,9,58,0.3)'; }}
|
||||
>
|
||||
Mitglied werden
|
||||
{fallbackText(eligibility.secondaryCta?.label, participationContent.eligibility.secondaryCta.label)}
|
||||
</Link>
|
||||
</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.8, margin: 0, textAlign: isMobile ? 'left' : 'right' }}>
|
||||
Vier klar definierte Kriterien: Erfüllen Sie alle, bewerben Sie sich kostenlos und ohne bürokratischen Aufwand.
|
||||
{fallbackText(eligibility.description, participationContent.eligibility.description)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 4 criteria as editorial rows */}
|
||||
<div style={{ position: 'relative', zIndex: 1, borderBottom: `1px solid #D0D5DD` }}>
|
||||
{[
|
||||
{ num: '01', title: 'Standort Bayern', body: 'Sitz oder wesentliche Betriebsstätte im Freistaat Bayern.', Icon: MapPin },
|
||||
{ num: '02', title: 'KMU-Größe', body: '50 bis 1.500 Mitarbeiter: klassischer Mittelstand im privatwirtschaftlichen Bereich.', Icon: Users },
|
||||
{ num: '03', title: 'Marktreife', body: 'Mindestens drei vollständige Geschäftsjahre am Markt tätig.', Icon: TrendingUp },
|
||||
{ num: '04', title: 'Privatwirtschaft', body: 'Keine überwiegende Zugehörigkeit zu staatlichen oder kommunalen Trägern.', Icon: Building2 },
|
||||
].map((item, i, arr) => {
|
||||
const Icon = item.Icon;
|
||||
{eligibilityCriteria.map((item, i, arr) => {
|
||||
const Icon = ICONS[item.icon as keyof typeof ICONS] || MapPin;
|
||||
return (
|
||||
<QualRow key={i} item={item} Icon={Icon} last={i === arr.length - 1} />
|
||||
);
|
||||
@@ -102,39 +141,35 @@ const Participation: React.FC = () => {
|
||||
|
||||
{/* Zusätzliche Hinweise: Verbund + Wiederbewerber */}
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '28px 24px' : '40px 80px', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: isMobile ? 24 : 48, borderBottom: '1px solid #D0D5DD' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
|
||||
<Building2 size={18} style={{ color: GOLD, flexShrink: 0, marginTop: 3 }} strokeWidth={1.7} />
|
||||
{eligibilityNotes.map((note, i) => {
|
||||
const Icon = ICONS[note.icon as keyof typeof ICONS] || Building2;
|
||||
return (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
|
||||
<Icon size={18} style={{ color: GOLD, flexShrink: 0, marginTop: 3 }} strokeWidth={1.7} />
|
||||
<div>
|
||||
<div style={{ fontFamily: FF, fontSize: 13, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>Verbund & Gemeinschaft</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 13, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>{fallbackText(note.title, '')}</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 17, color: 'rgba(16,24,40,0.6)', lineHeight: 1.7, margin: 0 }}>
|
||||
Oder Sie sind ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen, die einen Schwerpunkt in Bayern hat.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
|
||||
<UserCheck size={18} style={{ color: GOLD, flexShrink: 0, marginTop: 3 }} strokeWidth={1.7} />
|
||||
<div>
|
||||
<div style={{ fontFamily: FF, fontSize: 13, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>Erneute Bewerbung</div>
|
||||
<p style={{ fontFamily: FB, fontSize: 17, color: 'rgba(16,24,40,0.6)', lineHeight: 1.7, margin: 0 }}>
|
||||
Nominierte ohne Gewinn können sich sofort erneut bewerben. Preisträger können sich nach 7 Jahren erneut bewerben.
|
||||
{fallbackText(note.body, '')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* CTA nudge */}
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '24px 24px' : '32px 80px', display: 'flex', flexDirection: isMobile ? 'column' : 'row', alignItems: isMobile ? 'flex-start' : 'center', justifyContent: 'space-between', gap: 24 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<CheckCircle2 size={18} style={{ color: GOLD, flexShrink: 0 }} />
|
||||
<span style={{ fontFamily: FF, fontSize: 18, fontWeight: 600, color: '#101828' }}>Alle 4 Punkte erfüllt? Dann jetzt kostenlos bewerben.</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 18, fontWeight: 600, color: '#101828' }}>{fallbackText(eligibility.nudgeText, participationContent.eligibility.nudgeText)}</span>
|
||||
</div>
|
||||
<Link
|
||||
to="#bewerben"
|
||||
to={fallbackText(eligibility.nudgeCta?.url, participationContent.eligibility.nudgeCta.url)}
|
||||
style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#101828', background: GOLD, padding: '13px 28px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'opacity 0.2s', whiteSpace: 'nowrap' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '0.85')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}
|
||||
>
|
||||
Zum Formular <ArrowRight size={13} />
|
||||
{fallbackText(eligibility.nudgeCta?.label, participationContent.eligibility.nudgeCta.label)} <ArrowRight size={13} />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
@@ -148,8 +183,8 @@ 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="/images/buehne-gewinner.jpg"
|
||||
alt="Jury-Bewertung BMP"
|
||||
src={mediaUrl(evaluation.image, '/images/buehne-gewinner.jpg')}
|
||||
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' }}
|
||||
/>
|
||||
{/* Dark overlay for depth */}
|
||||
@@ -163,9 +198,9 @@ const Participation: React.FC = () => {
|
||||
{/* Heading overlay – mobile only */}
|
||||
{isMobile && (
|
||||
<div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, padding: '0 24px 24px', zIndex: 2 }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 12 }}>Bewertung</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 12 }}>{fallbackText(evaluation.eyebrow, participationContent.evaluation.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 9vw, 2.8rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.03em', lineHeight: 0.97, margin: 0, textShadow: '0 2px 18px rgba(3,9,58,0.6)' }}>
|
||||
UNSERE<br />KRITERIEN.
|
||||
<Lines text={fallbackText(evaluation.heading, participationContent.evaluation.heading)} />
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
@@ -175,40 +210,32 @@ const Participation: React.FC = () => {
|
||||
<div style={{ padding: isMobile ? '32px 24px 40px' : '80px 80px 72px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center', borderBottom: '1px solid rgba(255,255,255,0.07)' }}>
|
||||
{!isMobile && (
|
||||
<>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 20 }}>Bewertung</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 20 }}>{fallbackText(evaluation.eyebrow, participationContent.evaluation.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2.4rem, 4vw, 3.6rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.03em', lineHeight: 0.97, margin: '0 0 28px' }}>
|
||||
UNSERE<br />KRITERIEN.
|
||||
<Lines text={fallbackText(evaluation.heading, participationContent.evaluation.heading)} />
|
||||
</h2>
|
||||
</>
|
||||
)}
|
||||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 28 }} />
|
||||
<p style={{ fontFamily: FB, fontSize: 19, color: 'rgba(255,255,255,0.68)', lineHeight: 1.8, margin: 0, maxWidth: 420 }}>
|
||||
Vier zentrale Säulen, bewertet durch eine vollständig unabhängige Expertenjury aus Wirtschaft, Wissenschaft und Gesellschaft.
|
||||
<br />
|
||||
<br />
|
||||
Die Kriterien werden nicht gleich gewichtet.
|
||||
<Lines text={fallbackText(evaluation.body, participationContent.evaluation.body)} />
|
||||
</p>
|
||||
<a
|
||||
href="https://www.der-bayerische-mittelstandspreis.de/der-preis/kriterien/"
|
||||
href={fallbackText(evaluation.link?.url, participationContent.evaluation.link.url)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ fontFamily: FF, fontSize: 14, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: GOLD, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, marginTop: 28, borderBottom: `1px solid rgba(239,191,4,0.4)`, paddingBottom: 2, alignSelf: 'flex-start' }}
|
||||
onMouseEnter={e => ((e.currentTarget as HTMLElement).style.opacity = '0.8')}
|
||||
onMouseLeave={e => ((e.currentTarget as HTMLElement).style.opacity = '1')}
|
||||
>
|
||||
Kriterien & Bewertung <ArrowRight size={13} />
|
||||
{fallbackText(evaluation.link?.label, participationContent.evaluation.link.label)} <ArrowRight size={13} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* BOTTOM: full-width 4-column criteria grid */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(4, 1fr)', borderTop: '1px solid rgba(255,255,255,0.07)', position: 'relative' }}>
|
||||
{[
|
||||
{ title: 'Innovations\u00ADkraft', desc: 'Zukunftsweisende Produkte, Dienstleistungen oder interne Prozessinnovationen.' },
|
||||
{ title: 'Nachhaltigkeit', desc: 'Ökologische Verantwortung und Ressourcen-Effizienz im operativen Kern.' },
|
||||
{ title: 'Unternehmens\u00ADkultur', desc: 'Mitarbeiterbindung, Aus- und Weiterbildung sowie wertebasierte Führung.' },
|
||||
{ title: 'Regionale Wurzeln', desc: 'Engagement am Standort Bayern und Beitrag zur regionalen Wertschöpfung.' },
|
||||
].map((crit, i) => (
|
||||
{evaluationCriteria.map((crit, i) => (
|
||||
<CriteriaCell key={i} crit={crit} idx={i} />
|
||||
))}
|
||||
</div>
|
||||
@@ -217,23 +244,19 @@ const Participation: React.FC = () => {
|
||||
</section>
|
||||
|
||||
{/* ── BEWERBUNGSWEGE ───────────────────────────────────────────────────── */}
|
||||
<BewerbungswegeSection />
|
||||
<BewerbungswegeSection content={applicationWays} />
|
||||
|
||||
{/* ── DATES BANNER ─────────────────────────────────────────────────────── */}
|
||||
<section id="fristen" style={{ background: GOLD }}>
|
||||
{isMobile ? (
|
||||
/* Mobile: slim horizontal bar */
|
||||
<div style={{ padding: '24px 22px 26px' }}>
|
||||
<div style={{ fontFamily: FF, fontSize: 12, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.2em', marginBottom: 18 }}>Wichtige Termine 2026</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 12, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.2em', marginBottom: 18 }}>{fallbackText(datesBanner.heading, participationContent.datesBanner.heading)}</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
{[
|
||||
{ date: '30. Juni 2026', label: 'Bewerbungsschluss' },
|
||||
{ date: 'August 2026', label: 'Nominierte' },
|
||||
{ date: 'Oktober 2026', label: 'Gala-Verleihung' },
|
||||
].map((d, i) => (
|
||||
{mobileDates.map((d, i) => (
|
||||
<div key={i} style={{ flex: 1, minWidth: 0, paddingLeft: i > 0 ? 14 : 0, borderLeft: i > 0 ? '1px solid rgba(3,9,58,0.2)' : 'none' }}>
|
||||
<div style={{ fontFamily: FF, fontSize: 17, fontWeight: 900, color: '#101828', letterSpacing: '-0.02em', lineHeight: 1.12, marginBottom: 6 }}>{d.date}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 9.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.55)', lineHeight: 1.25 }}>{d.label}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 17, fontWeight: 900, color: '#101828', letterSpacing: '-0.02em', lineHeight: 1.12, marginBottom: 6 }}>{fallbackText(d.date, '')}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 9.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.55)', lineHeight: 1.25 }}>{fallbackText(d.label, '')}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -242,21 +265,17 @@ const Participation: React.FC = () => {
|
||||
/* Desktop: full banner */
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1px 1fr 1px 1fr 1px 1fr', maxWidth: 1200, margin: '0 auto' }}>
|
||||
<div style={{ padding: '48px 56px' }}>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 18, fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', marginBottom: 6 }}>Wichtige Termine 2026</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.6)', margin: 0 }}>Planen Sie Ihre Teilnahme rechtzeitig.</p>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 18, fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', marginBottom: 6 }}>{fallbackText(datesBanner.heading, participationContent.datesBanner.heading)}</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.6)', margin: 0 }}>{fallbackText(datesBanner.description, participationContent.datesBanner.description)}</p>
|
||||
</div>
|
||||
<div style={{ background: 'rgba(3,9,58,0.12)' }} />
|
||||
{[
|
||||
{ date: '30. Juni 2026', label: 'Bewerbungsschluss' },
|
||||
{ date: 'August 2026', label: 'Bekanntgabe Nominierte' },
|
||||
{ date: 'Oktober 2026', label: 'Gala-Verleihung' },
|
||||
].map((d, i) => (
|
||||
{desktopDates.map((d, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<div style={{ padding: '48px 40px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||||
<div style={{ fontFamily: FF, fontSize: 'clamp(1.1rem, 1.8vw, 1.4rem)', fontWeight: 900, color: '#101828', letterSpacing: '-0.02em', marginBottom: 6 }}>{d.date}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.55)' }}>{d.label}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 'clamp(1.1rem, 1.8vw, 1.4rem)', fontWeight: 900, color: '#101828', letterSpacing: '-0.02em', marginBottom: 6 }}>{fallbackText(d.date, '')}</div>
|
||||
<div style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.55)' }}>{fallbackText(d.label, '')}</div>
|
||||
</div>
|
||||
{i < 2 && <div style={{ background: 'rgba(3,9,58,0.12)' }} />}
|
||||
{i < desktopDates.length - 1 && <div style={{ background: 'rgba(3,9,58,0.12)' }} />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
@@ -268,25 +287,21 @@ const Participation: React.FC = () => {
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '4fr 1px 8fr', flex: 1, minHeight: 0, overflow: isMobile ? 'visible' : 'hidden' }}>
|
||||
{/* Left – copy */}
|
||||
<div style={{ padding: isMobile ? '32px 24px 24px' : '36px 36px 32px 52px', display: 'flex', flexDirection: 'column', justifyContent: 'center', overflow: isMobile ? 'visible' : 'hidden' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 20 }}>Direktbewerbung</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 20 }}>{fallbackText(applicationForm.eyebrow, participationContent.applicationForm.eyebrow)}</span>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 3.2vw, 2.8rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.04, margin: '0 0 20px' }}>
|
||||
BEWERBEN ODER<br />VORSCHLAGEN.
|
||||
<Lines text={fallbackText(applicationForm.heading, participationContent.applicationForm.heading)} />
|
||||
</h2>
|
||||
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 28 }} />
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.68)', lineHeight: 1.8, maxWidth: 400, marginBottom: 48 }}>
|
||||
Sie können sich selbst bewerben oder ein herausragendes Unternehmen vorschlagen. Firmenverbunde können sich ebenfalls bewerben. Die Teilnahme ist vollständig kostenfrei.
|
||||
{fallbackText(applicationForm.description, participationContent.applicationForm.description)}
|
||||
</p>
|
||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.08)' }}>
|
||||
{[
|
||||
{ num: '01', label: '0 EUR', desc: 'Teilnahmegebühr' },
|
||||
{ num: '02', label: 'Schnell', desc: 'Ca. 30–45 Minuten' },
|
||||
{ num: '03', label: 'Mehrstufig', desc: 'Transparenter Prozess' },
|
||||
].map((item, i) => (
|
||||
{applicationFacts.map((item, i) => (
|
||||
<div key={i} style={{ display: 'grid', gridTemplateColumns: '36px 1fr', gap: '0 16px', padding: '16px 0', borderBottom: '1px solid rgba(255,255,255,0.06)', alignItems: 'center' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(239,191,4,0.5)', letterSpacing: '0.1em' }}>{item.num}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(239,191,4,0.5)', letterSpacing: '0.1em' }}>{fallbackText(item.num, '')}</span>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{item.label}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(255,255,255,0.62)' }}>{item.desc}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{fallbackText(item.label, '')}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(255,255,255,0.62)' }}>{fallbackText(item.desc, '')}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -314,39 +329,39 @@ const Participation: React.FC = () => {
|
||||
|
||||
{!isMobile && (
|
||||
<div style={{ height: 220, position: 'relative', overflow: 'hidden', flexShrink: 0, zIndex: 1 }}>
|
||||
<Image unoptimized src="/images/preistraeger-jubel.jpg" alt="Preisverleihung 2025" style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', filter: 'sepia(0.18) brightness(0.92)' }} />
|
||||
<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)' }} />
|
||||
<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 }}>
|
||||
<div style={{ width: 5, height: 5, borderRadius: '50%', background: 'rgba(17,29,85,0.7)' }} />
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(17,29,85,0.75)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>Preisverleihung 2025</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(17,29,85,0.75)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>{fallbackText(applicationForm.imageCaption, participationContent.applicationForm.imageCaption)}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ padding: isMobile ? '28px 20px' : '24px 48px 32px 40px', flex: 1, display: 'flex', flexDirection: 'column', minHeight: isMobile ? 560 : 0, position: 'relative', zIndex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, marginBottom: 24 }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.28em', fontWeight: 700 }}>Bewerbung 2026</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.28em', fontWeight: 700 }}>{fallbackText(applicationForm.formEyebrow, participationContent.applicationForm.formEyebrow)}</span>
|
||||
<Link
|
||||
to="/formular-hochladen"
|
||||
to={fallbackText(applicationForm.uploadCta?.url, participationContent.applicationForm.uploadCta.url)}
|
||||
style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(17,29,85,0.55)', border: '1px solid rgba(17,29,85,0.2)', padding: '7px 14px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6, transition: 'color 0.15s, border-color 0.15s', whiteSpace: 'nowrap', flexShrink: 0 }}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.color = '#111D55'; (e.currentTarget as HTMLElement).style.borderColor = '#111D55'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.color = 'rgba(17,29,85,0.55)'; (e.currentTarget as HTMLElement).style.borderColor = 'rgba(17,29,85,0.2)'; }}
|
||||
>
|
||||
<svg width="10" height="10" viewBox="0 0 12 12" fill="none"><path d="M6 1v7M3 4l3-3 3 3M1 10h10" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
||||
PDF hochladen
|
||||
{fallbackText(applicationForm.uploadCta?.label, participationContent.applicationForm.uploadCta.label)}
|
||||
</Link>
|
||||
</div>
|
||||
<BewerbungsForm theme="gold" />
|
||||
<BewerbungsForm theme="gold" content={form} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '16px 20px 20px' : '14px 56px 18px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, justifyContent: 'center', textAlign: 'center', borderTop: '1px solid rgba(255,255,255,0.06)', flexShrink: 0 }}>
|
||||
<span style={{ fontFamily: FB, fontSize: 14, color: 'rgba(255,255,255,0.55)', lineHeight: 1.6, maxWidth: 520 }}>
|
||||
Sie haben Ihre Unterlagen bereits als PDF vorbereitet? Laden Sie sie direkt hoch, statt das Formular auszufüllen.
|
||||
{fallbackText(applicationForm.uploadPrompt, participationContent.applicationForm.uploadPrompt)}
|
||||
</span>
|
||||
<Link to="/formular-hochladen" style={{ fontFamily: FF, fontSize: 15, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(239,191,4,0.7)', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6, borderBottom: '1px solid rgba(239,191,4,0.3)', paddingBottom: 1 }}>
|
||||
Formular hochladen →
|
||||
<Link to={fallbackText(applicationForm.uploadFooterCta?.url, participationContent.applicationForm.uploadFooterCta.url)} style={{ fontFamily: FF, fontSize: 15, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(239,191,4,0.7)', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6, borderBottom: '1px solid rgba(239,191,4,0.3)', paddingBottom: 1 }}>
|
||||
{fallbackText(applicationForm.uploadFooterCta?.label, participationContent.applicationForm.uploadFooterCta.label)}
|
||||
</Link>
|
||||
</div>
|
||||
<div style={{ height: 2, background: 'linear-gradient(to right, #EFBF04, rgba(239,191,4,0.25), transparent)', flexShrink: 0 }} />
|
||||
@@ -408,31 +423,26 @@ function CriteriaCell({ crit, idx }: { crit: { title: string; desc: string }; id
|
||||
|
||||
// ── BewerbungswegeSection ─────────────────────────────────────────────────────
|
||||
|
||||
const INSTITUTIONEN = [
|
||||
'Senatoren und Mitglieder der Verbandsgruppe „Wir Eigentümerunternehmer"',
|
||||
'Bundesverband Deutscher Mittelstand (BM)',
|
||||
'Europäisches Wirtschaftsforum (EWIF)',
|
||||
'Union Mittelständischer Unternehmen (UMU)',
|
||||
'HAM – Hochschule für angewandtes Management und deren Regionalpartner',
|
||||
'Mitglieder des Wirtschaftsbeirates Bayern',
|
||||
'Ehemalige Preisträger des Bayerischen Mittelstandspreises',
|
||||
];
|
||||
type ApplicationWaysContent = Partial<typeof participationContent.applicationWays>;
|
||||
|
||||
function BewerbungswegeSection() {
|
||||
function BewerbungswegeSection({ content }: { content?: ApplicationWaysContent }) {
|
||||
const isMobile = useIsMobile();
|
||||
const section = { ...participationContent.applicationWays, ...(content || {}) };
|
||||
const institutions = fallbackArray(section.institutions, participationContent.applicationWays.institutions);
|
||||
const ways = fallbackArray(section.ways, participationContent.applicationWays.ways);
|
||||
return (
|
||||
<section style={{ background: CREAM, position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
|
||||
<MunichSkylineBg />
|
||||
|
||||
{/* Header */}
|
||||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '48px 24px 32px' : '80px 80px 56px', borderBottom: '1px solid #D0D5DD' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 16 }}>Bewerbung 2026</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 10, color: '#4A8FC9', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 16 }}>{fallbackText(section.eyebrow, participationContent.applicationWays.eyebrow)}</span>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: isMobile ? 16 : 48, alignItems: 'flex-end' }}>
|
||||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 3.5vw, 3rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.03, margin: 0 }}>
|
||||
HIER KÖNNEN SIE SICH<br />BEWERBEN ODER EIN<br />UNTERNEHMEN VORSCHLAGEN.
|
||||
<Lines text={fallbackText(section.heading, participationContent.applicationWays.heading)} />
|
||||
</h2>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.55)', lineHeight: 1.8, margin: 0 }}>
|
||||
Als Unternehmen haben Sie zwei Möglichkeiten: Sie werden von einem unterstützenden Partner oder einer Institution vorgeschlagen, oder Sie ergreifen selbst die Initiative und füllen unsere Anmeldung aus. Die Teilnahme ist in allen Fällen kostenfrei.
|
||||
{fallbackText(section.description, participationContent.applicationWays.description)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -441,47 +451,44 @@ function BewerbungswegeSection() {
|
||||
<div style={{ position: 'relative', zIndex: 1, display: isMobile ? 'flex' : 'grid', gridTemplateColumns: isMobile ? undefined : 'repeat(3, 1fr)', overflowX: isMobile ? 'auto' : undefined, scrollSnapType: isMobile ? 'x mandatory' : undefined, WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none', gap: isMobile ? 14 : 0, padding: isMobile ? '28px 24px 36px' : 0 }}>
|
||||
|
||||
{/* ── MOBILE: unified premium cards ── */}
|
||||
{isMobile && ([
|
||||
{ weg: 'Weg 01', title: 'Vorschlag unterbreiten', desc: 'Durch ehemalige Preisträger, die HAM, Senatoren und den Preis unterstützende Institutionen.', Icon: Send, checkItems: INSTITUTIONEN, cta: 'Vorschlag unterbreiten', featured: false },
|
||||
{ weg: 'Weg 02', title: 'Initiativbewerbung', desc: 'Für Unternehmer, die selbst die Initiative ergreifen.', Icon: UserCheck, kvItems: [ { k: '0 EUR', v: 'Teilnahmegebühr' }, { k: 'Eigeninitiative', v: 'Direkt ohne Intermediär' }, { k: 'Für Unternehmer', v: 'Inhabergeführte KMU in Bayern' }, { k: 'Deadline', v: '30. Juni 2026' } ], cta: 'Jetzt bewerben', featured: true },
|
||||
{ weg: 'Weg 03 · Sonderpreis', title: 'Bavarian Future Award', desc: 'Sonderpreis, initiiert durch die Studierenden der HAM, Hochschule für angewandtes Management.', Icon: Star, kvItems: [ { k: 'Vorschlag möglich', v: 'Durch HAM & Partner' }, { k: 'Selbstbewerbung', v: 'Direkte Einreichung' }, { k: 'Initiiert von', v: 'Studierenden der HAM' }, { k: 'Kategorie', v: 'Zukunft & Innovation' } ], cta: 'Zum Formular', featured: false },
|
||||
] as { weg: string; title: string; desc: string; Icon: LucideIcon; checkItems?: string[]; kvItems?: { k: string; v: string }[]; cta: string; featured: boolean }[]).map((w) => {
|
||||
const Icon = w.Icon;
|
||||
{isMobile && ways.map((w) => {
|
||||
const Icon = ICONS[w.icon as keyof typeof ICONS] || Send;
|
||||
const facts = fallbackArray<{ label?: string | null; desc?: string | null }>(w.facts, []);
|
||||
return (
|
||||
<div key={w.weg} style={{ flexShrink: 0, minWidth: '85vw', maxWidth: '85vw', scrollSnapAlign: 'center', display: 'flex', flexDirection: 'column', background: '#fff', borderRadius: 18, overflow: 'hidden', border: w.featured ? `1.5px solid ${GOLD}` : '1px solid rgba(17,29,85,0.10)', boxShadow: w.featured ? '0 14px 34px rgba(239,191,4,0.20), 0 6px 16px rgba(3,9,58,0.10)' : '0 10px 28px rgba(3,9,58,0.08)' }}>
|
||||
<div key={fallbackText(w.label, '')} style={{ flexShrink: 0, minWidth: '85vw', maxWidth: '85vw', scrollSnapAlign: 'center', display: 'flex', flexDirection: 'column', background: '#fff', borderRadius: 18, overflow: 'hidden', border: w.featured ? `1.5px solid ${GOLD}` : '1px solid rgba(17,29,85,0.10)', boxShadow: w.featured ? '0 14px 34px rgba(239,191,4,0.20), 0 6px 16px rgba(3,9,58,0.10)' : '0 10px 28px rgba(3,9,58,0.08)' }}>
|
||||
{/* Header */}
|
||||
<div style={{ padding: '22px 22px 0', position: 'relative' }}>
|
||||
{w.featured && (
|
||||
<span style={{ position: 'absolute', top: 22, right: 22, fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', color: '#101828', background: GOLD, padding: '5px 10px', borderRadius: 100 }}>Empfohlen</span>
|
||||
<span style={{ position: 'absolute', top: 22, right: 22, fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', color: '#101828', background: GOLD, padding: '5px 10px', borderRadius: 100 }}>{fallbackText(section.recommendedLabel, participationContent.applicationWays.recommendedLabel)}</span>
|
||||
)}
|
||||
<div style={{ width: 46, height: 46, borderRadius: 12, background: w.featured ? GOLD : NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 18 }}>
|
||||
<Icon size={20} style={{ color: w.featured ? '#101828' : GOLD }} strokeWidth={1.6} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.28em', textTransform: 'uppercase', color: w.featured ? '#A87800' : 'rgba(16,24,40,0.4)', display: 'block', marginBottom: 8 }}>{w.weg}</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: '1.45rem', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 10px' }}>{w.title}</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 16, color: 'rgba(16,24,40,0.55)', lineHeight: 1.6, margin: '0 0 20px' }}>{w.desc}</p>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.28em', textTransform: 'uppercase', color: w.featured ? '#A87800' : 'rgba(16,24,40,0.4)', display: 'block', marginBottom: 8 }}>{fallbackText(w.label, '')}</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: '1.45rem', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 10px' }}><Lines text={fallbackText(w.title, '')} /></h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 16, color: 'rgba(16,24,40,0.55)', lineHeight: 1.6, margin: '0 0 20px' }}>{fallbackText(w.desc, '')}</p>
|
||||
<div style={{ width: 36, height: 2, background: GOLD }} />
|
||||
</div>
|
||||
{/* Detail list */}
|
||||
<div style={{ padding: '4px 22px 0', flex: 1 }}>
|
||||
{w.checkItems && w.checkItems.map((inst, i) => (
|
||||
{w.listType === 'institutions' && institutions.map((inst, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '12px 0', borderBottom: '1px solid rgba(17,29,85,0.08)' }}>
|
||||
<div style={{ width: 18, height: 18, borderRadius: 5, background: GOLD, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, marginTop: 1 }}>
|
||||
<svg width="9" height="9" viewBox="0 0 8 8"><polyline points="1,4 3,6 7,2" stroke="#101828" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
||||
</div>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(16,24,40,0.7)', lineHeight: 1.5 }}>{inst}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(16,24,40,0.7)', lineHeight: 1.5 }}>{fallbackText(inst.text, '')}</span>
|
||||
</div>
|
||||
))}
|
||||
{w.kvItems && w.kvItems.map((item, i) => (
|
||||
{w.listType !== 'institutions' && facts.map((item, i) => (
|
||||
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12, padding: '13px 0', borderBottom: '1px solid rgba(17,29,85,0.08)' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 14, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.06em', flexShrink: 0 }}>{item.k}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 15, color: 'rgba(16,24,40,0.5)', textAlign: 'right' }}>{item.v}</span>
|
||||
<span style={{ fontFamily: FF, fontSize: 14, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.06em', flexShrink: 0 }}>{fallbackText(item.label, '')}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 15, color: 'rgba(16,24,40,0.5)', textAlign: 'right' }}>{fallbackText(item.desc, '')}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* CTA */}
|
||||
<div style={{ padding: '18px 22px 22px' }}>
|
||||
<a href="#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 }}>{w.cta} <ArrowRight size={14} /></a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -490,114 +497,51 @@ function BewerbungswegeSection() {
|
||||
{/* ── DESKTOP: original 3 columns ── */}
|
||||
{!isMobile && (<>
|
||||
|
||||
{/* ── Weg A: Vorschlag ── */}
|
||||
<div style={{ padding: isMobile ? '32px 24px' : '56px 48px 56px', borderRight: '1px solid #D0D5DD', display: 'flex', flexDirection: 'column', ...(isMobile ? { minWidth: '83vw', maxWidth: '83vw', flexShrink: 0, scrollSnapAlign: 'start' } : {}) }}>
|
||||
<div style={{ width: 44, height: 44, background: NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 28 }}>
|
||||
<Send size={18} style={{ color: GOLD }} strokeWidth={1.5} />
|
||||
{ways.map((w, index) => {
|
||||
const Icon = ICONS[w.icon as keyof typeof ICONS] || Send;
|
||||
const isFeatured = Boolean(w.featured);
|
||||
const facts = fallbackArray<{ label?: string | null; desc?: string | null }>(w.facts, []);
|
||||
const isLast = index === ways.length - 1;
|
||||
return (
|
||||
<div key={`${w.label}-${index}`} style={{ padding: isMobile ? '32px 24px' : '56px 48px 56px', borderRight: !isLast ? '1px solid #D0D5DD' : 'none', display: 'flex', flexDirection: 'column', background: isFeatured ? NAVY : undefined, ...(isMobile ? { minWidth: '83vw', maxWidth: '83vw', flexShrink: 0, scrollSnapAlign: 'start' } : {}) }}>
|
||||
<div style={{ width: 44, height: 44, background: isFeatured ? GOLD : NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 28 }}>
|
||||
<Icon size={18} style={{ color: isFeatured ? '#101828' : GOLD }} strokeWidth={1.5} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.4)', display: 'block', marginBottom: 10 }}>Weg 01</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: '0 0 8px' }}>
|
||||
Vorschlag<br />unterbreiten
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: isFeatured ? 'rgba(255,255,255,0.35)' : 'rgba(16,24,40,0.4)', display: 'block', marginBottom: 10 }}>{fallbackText(w.label, '')}</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: isFeatured ? '#fff' : '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: '0 0 8px' }}>
|
||||
<Lines text={fallbackText(w.title, '')} />
|
||||
</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.7, margin: '0 0 32px' }}>
|
||||
Durch ehemalige Preisträger, die HAM, Senatoren und den Preis unterstützende Institutionen.
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: isFeatured ? 'rgba(255,255,255,0.62)' : 'rgba(16,24,40,0.5)', lineHeight: 1.7, margin: '0 0 32px' }}>
|
||||
{fallbackText(w.desc, '')}
|
||||
</p>
|
||||
|
||||
{/* Institution list */}
|
||||
<div style={{ borderTop: '1px solid #D0D5DD', flex: 1 }}>
|
||||
{INSTITUTIONEN.map((inst, i) => (
|
||||
<div style={{ borderTop: isFeatured ? '1px solid rgba(255,255,255,0.1)' : '1px solid #D0D5DD', flex: 1 }}>
|
||||
{w.listType === 'institutions' ? institutions.map((inst, i) => (
|
||||
<div key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '12px 0', borderBottom: '1px solid #D0D5DD' }}>
|
||||
<div style={{ width: 16, height: 16, background: GOLD, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, marginTop: 2 }}>
|
||||
<svg width="8" height="8" viewBox="0 0 8 8"><polyline points="1,4 3,6 7,2" stroke="#101828" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
||||
</div>
|
||||
<span style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.65)', lineHeight: 1.55 }}>{inst}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.65)', lineHeight: 1.55 }}>{fallbackText(inst.text, '')}</span>
|
||||
</div>
|
||||
)) : facts.map((item, i) => (
|
||||
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '14px 0', borderBottom: isFeatured ? '1px solid rgba(255,255,255,0.07)' : '1px solid #D0D5DD' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: isFeatured ? '#fff' : '#101828', textTransform: 'uppercase', letterSpacing: '0.07em' }}>{fallbackText(item.label, '')}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: isFeatured ? 'rgba(255,255,255,0.50)' : 'rgba(16,24,40,0.45)' }}>{fallbackText(item.desc, '')}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="#bewerben"
|
||||
href={fallbackText(w.cta?.url, '#bewerben')}
|
||||
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')}
|
||||
>
|
||||
Vorschlag unterbreiten <ArrowRight size={13} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* ── Weg B: Initiativbewerbung ── */}
|
||||
<div style={{ padding: isMobile ? '32px 24px' : '56px 48px 56px', borderRight: '1px solid #D0D5DD', display: 'flex', flexDirection: 'column', background: NAVY, ...(isMobile ? { minWidth: '83vw', maxWidth: '83vw', flexShrink: 0, scrollSnapAlign: 'start' } : {}) }}>
|
||||
<div style={{ width: 44, height: 44, background: GOLD, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 28 }}>
|
||||
<UserCheck size={18} style={{ color: '#101828' }} strokeWidth={1.5} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.35)', display: 'block', marginBottom: 10 }}>Weg 02</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: '0 0 8px' }}>
|
||||
Initiativbewerbung
|
||||
</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.62)', lineHeight: 1.7, margin: '0 0 32px' }}>
|
||||
Für Unternehmer, die selbst die Initiative ergreifen.
|
||||
</p>
|
||||
|
||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.1)', flex: 1 }}>
|
||||
{[
|
||||
{ label: '0 EUR', desc: 'Teilnahmegebühr' },
|
||||
{ label: 'Eigeninitiative', desc: 'Direkt ohne Intermediär' },
|
||||
{ label: 'Für Unternehmer', desc: 'Inhabergeführte KMU in Bayern' },
|
||||
{ label: 'Deadline', desc: '30. Juni 2026' },
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '14px 0', borderBottom: '1px solid rgba(255,255,255,0.07)' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.07em' }}>{item.label}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(255,255,255,0.50)' }}>{item.desc}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="#bewerben"
|
||||
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')}
|
||||
>
|
||||
Jetzt bewerben <ArrowRight size={13} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* ── Weg C: Sonderpreis ── */}
|
||||
<div style={{ padding: isMobile ? '32px 24px' : '56px 48px 56px', display: 'flex', flexDirection: 'column', ...(isMobile ? { minWidth: '83vw', maxWidth: '83vw', flexShrink: 0, scrollSnapAlign: 'start' } : {}) }}>
|
||||
<div style={{ width: 44, height: 44, background: NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 28 }}>
|
||||
<Star size={18} style={{ color: GOLD }} strokeWidth={1.5} />
|
||||
</div>
|
||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: 'rgba(16,24,40,0.4)', display: 'block', marginBottom: 10 }}>Weg 03 · Sonderpreis</span>
|
||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, margin: '0 0 8px' }}>
|
||||
Bavarian<br />Future Award
|
||||
</h3>
|
||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.7, margin: '0 0 32px' }}>
|
||||
Sonderpreis, initiiert durch die Studierenden der HAM, Hochschule für angewandtes Management.
|
||||
</p>
|
||||
|
||||
<div style={{ borderTop: '1px solid #D0D5DD', flex: 1 }}>
|
||||
{[
|
||||
{ label: 'Vorschlag möglich', desc: 'Durch HAM & Partner' },
|
||||
{ label: 'Selbstbewerbung', desc: 'Direkte Einreichung' },
|
||||
{ label: 'Initiiert von', desc: 'Studierenden der HAM' },
|
||||
{ label: 'Kategorie', desc: 'Zukunft & Innovation' },
|
||||
].map((item, i) => (
|
||||
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '14px 0', borderBottom: '1px solid #D0D5DD' }}>
|
||||
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#101828', textTransform: 'uppercase', letterSpacing: '0.07em' }}>{item.label}</span>
|
||||
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(16,24,40,0.45)' }}>{item.desc}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="#bewerben"
|
||||
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')}
|
||||
>
|
||||
Zum Formular <ArrowRight size={13} />
|
||||
{fallbackText(w.cta?.label, 'Zum Formular')} <ArrowRight size={13} />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
</>)}
|
||||
|
||||
|
||||
301
src/spa/participationContent.ts
Normal file
301
src/spa/participationContent.ts
Normal file
@@ -0,0 +1,301 @@
|
||||
export const participationContent = {
|
||||
hero: {
|
||||
backgroundImageFilename: 'preisuebergabe.jpg',
|
||||
imageAlt: 'Teilnahme BMP',
|
||||
eyebrow: 'Bewerbungsphase 2026',
|
||||
heading: 'GESTALTEN SIE\nBAYERNS ZUKUNFT.',
|
||||
description: 'Alles, was Sie für Ihre erfolgreiche Bewerbung zum Bayerischen Mittelstandspreis wissen müssen.',
|
||||
},
|
||||
process: {
|
||||
eyebrow: 'Schritt für Schritt',
|
||||
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.',
|
||||
scrollHint: 'Scrollen zum Erkunden',
|
||||
progressLabel: '01 / 04',
|
||||
steps: [
|
||||
{
|
||||
step: '01',
|
||||
title: 'Online-Einreichung',
|
||||
desc: 'Bewerbungsformular vollständig ausfüllen. Die Teilnahme ist kostenfrei und ohne bürokratischen Aufwand möglich.',
|
||||
icon: 'fileText',
|
||||
date: 'Bis 30. Juni 2026',
|
||||
},
|
||||
{
|
||||
step: '02',
|
||||
title: 'Formale Vorprüfung',
|
||||
desc: 'Das Gremium sichtet alle Unterlagen auf Vollständigkeit, KMU-Konformität und regionale Zugehörigkeit im Freistaat.',
|
||||
icon: 'search',
|
||||
date: 'Juli 2026',
|
||||
},
|
||||
{
|
||||
step: '03',
|
||||
title: 'Audit & Jury-Sitzung',
|
||||
desc: 'Die besten Unternehmen werden durch unabhängige Experten-Audits vor Ort evaluiert und in der Jury-Sitzung bewertet.',
|
||||
icon: 'userPlus',
|
||||
date: 'Aug – Sep 2026',
|
||||
},
|
||||
{
|
||||
step: '04',
|
||||
title: 'Gala-Preisverleihung',
|
||||
desc: 'Die Gewinner werden im Rahmen der feierlichen Gala in München vor geladenen Gästen aus Wirtschaft und Politik geehrt.',
|
||||
icon: 'trophy',
|
||||
date: 'Oktober 2026',
|
||||
},
|
||||
],
|
||||
},
|
||||
eligibility: {
|
||||
eyebrow: 'Berechtigung',
|
||||
heading: 'WER KANN\nTEILNEHMEN?',
|
||||
primaryCta: { label: 'Jetzt bewerben', url: '#bewerben' },
|
||||
secondaryCta: { label: 'Mitglied werden', url: '/mitglied-werden' },
|
||||
description:
|
||||
'Vier klar definierte Kriterien: Erfüllen Sie alle, bewerben Sie sich kostenlos und ohne bürokratischen Aufwand.',
|
||||
criteria: [
|
||||
{ num: '01', title: 'Standort Bayern', body: 'Sitz oder wesentliche Betriebsstätte im Freistaat Bayern.', icon: 'mapPin' },
|
||||
{ num: '02', title: 'KMU-Größe', body: '50 bis 1.500 Mitarbeiter: klassischer Mittelstand im privatwirtschaftlichen Bereich.', icon: 'users' },
|
||||
{ num: '03', title: 'Marktreife', body: 'Mindestens drei vollständige Geschäftsjahre am Markt tätig.', icon: 'trendingUp' },
|
||||
{ num: '04', title: 'Privatwirtschaft', body: 'Keine überwiegende Zugehörigkeit zu staatlichen oder kommunalen Trägern.', icon: 'building2' },
|
||||
],
|
||||
notes: [
|
||||
{
|
||||
title: 'Verbund & Gemeinschaft',
|
||||
body:
|
||||
'Oder Sie sind ein Verbund bzw. eine Gemeinschaft von mittelständischen Unternehmen oder von mittelständischen Unternehmen und Organisationen/Institutionen, die einen Schwerpunkt in Bayern hat.',
|
||||
icon: 'building2',
|
||||
},
|
||||
{
|
||||
title: 'Erneute Bewerbung',
|
||||
body: 'Nominierte ohne Gewinn können sich sofort erneut bewerben. Preisträger können sich nach 7 Jahren erneut bewerben.',
|
||||
icon: 'userCheck',
|
||||
},
|
||||
],
|
||||
nudgeText: 'Alle 4 Punkte erfüllt? Dann jetzt kostenlos bewerben.',
|
||||
nudgeCta: { label: 'Zum Formular', url: '#bewerben' },
|
||||
},
|
||||
evaluation: {
|
||||
imageFilename: 'buehne-gewinner.jpg',
|
||||
imageAlt: 'Jury-Bewertung BMP',
|
||||
eyebrow: 'Bewertung',
|
||||
heading: 'UNSERE\nKRITERIEN.',
|
||||
body:
|
||||
'Vier zentrale Säulen, bewertet durch eine vollständig unabhängige Expertenjury aus Wirtschaft, Wissenschaft und Gesellschaft.\n\nDie Kriterien werden nicht gleich gewichtet.',
|
||||
link: {
|
||||
label: 'Kriterien & Bewertung',
|
||||
url: 'https://www.der-bayerische-mittelstandspreis.de/der-preis/kriterien/',
|
||||
},
|
||||
criteria: [
|
||||
{ title: 'Innovationskraft', desc: 'Zukunftsweisende Produkte, Dienstleistungen oder interne Prozessinnovationen.' },
|
||||
{ title: 'Nachhaltigkeit', desc: 'Ökologische Verantwortung und Ressourcen-Effizienz im operativen Kern.' },
|
||||
{ title: 'Unternehmenskultur', desc: 'Mitarbeiterbindung, Aus- und Weiterbildung sowie wertebasierte Führung.' },
|
||||
{ title: 'Regionale Wurzeln', desc: 'Engagement am Standort Bayern und Beitrag zur regionalen Wertschöpfung.' },
|
||||
],
|
||||
},
|
||||
applicationWays: {
|
||||
eyebrow: 'Bewerbung 2026',
|
||||
heading: 'HIER KÖNNEN SIE SICH\nBEWERBEN ODER EIN\nUNTERNEHMEN VORSCHLAGEN.',
|
||||
description:
|
||||
'Als Unternehmen haben Sie zwei Möglichkeiten: Sie werden von einem unterstützenden Partner oder einer Institution vorgeschlagen, oder Sie ergreifen selbst die Initiative und füllen unsere Anmeldung aus. Die Teilnahme ist in allen Fällen kostenfrei.',
|
||||
institutions: [
|
||||
'Senatoren und Mitglieder der Verbandsgruppe „Wir Eigentümerunternehmer"',
|
||||
'Bundesverband Deutscher Mittelstand (BM)',
|
||||
'Europäisches Wirtschaftsforum (EWIF)',
|
||||
'Union Mittelständischer Unternehmen (UMU)',
|
||||
'HAM – Hochschule für angewandtes Management und deren Regionalpartner',
|
||||
'Mitglieder des Wirtschaftsbeirates Bayern',
|
||||
'Ehemalige Preisträger des Bayerischen Mittelstandspreises',
|
||||
].map((text) => ({ text })),
|
||||
ways: [
|
||||
{
|
||||
label: 'Weg 01',
|
||||
title: 'Vorschlag\nunterbreiten',
|
||||
desc: 'Durch ehemalige Preisträger, die HAM, Senatoren und den Preis unterstützende Institutionen.',
|
||||
icon: 'send',
|
||||
featured: false,
|
||||
cta: { label: 'Vorschlag unterbreiten', url: '#bewerben' },
|
||||
listType: 'institutions',
|
||||
facts: [],
|
||||
},
|
||||
{
|
||||
label: 'Weg 02',
|
||||
title: 'Initiativbewerbung',
|
||||
desc: 'Für Unternehmer, die selbst die Initiative ergreifen.',
|
||||
icon: 'userCheck',
|
||||
featured: true,
|
||||
cta: { label: 'Jetzt bewerben', url: '#bewerben' },
|
||||
listType: 'facts',
|
||||
facts: [
|
||||
{ label: '0 EUR', desc: 'Teilnahmegebühr' },
|
||||
{ label: 'Eigeninitiative', desc: 'Direkt ohne Intermediär' },
|
||||
{ label: 'Für Unternehmer', desc: 'Inhabergeführte KMU in Bayern' },
|
||||
{ label: 'Deadline', desc: '30. Juni 2026' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Weg 03 · Sonderpreis',
|
||||
title: 'Bavarian\nFuture Award',
|
||||
desc: 'Sonderpreis, initiiert durch die Studierenden der HAM, Hochschule für angewandtes Management.',
|
||||
icon: 'star',
|
||||
featured: false,
|
||||
cta: { label: 'Zum Formular', url: '#bewerben' },
|
||||
listType: 'facts',
|
||||
facts: [
|
||||
{ label: 'Vorschlag möglich', desc: 'Durch HAM & Partner' },
|
||||
{ label: 'Selbstbewerbung', desc: 'Direkte Einreichung' },
|
||||
{ label: 'Initiiert von', desc: 'Studierenden der HAM' },
|
||||
{ label: 'Kategorie', desc: 'Zukunft & Innovation' },
|
||||
],
|
||||
},
|
||||
],
|
||||
recommendedLabel: 'Empfohlen',
|
||||
},
|
||||
datesBanner: {
|
||||
heading: 'Wichtige Termine 2026',
|
||||
description: 'Planen Sie Ihre Teilnahme rechtzeitig.',
|
||||
mobileItems: [
|
||||
{ date: '30. Juni 2026', label: 'Bewerbungsschluss' },
|
||||
{ date: 'August 2026', label: 'Nominierte' },
|
||||
{ date: 'Oktober 2026', label: 'Gala-Verleihung' },
|
||||
],
|
||||
desktopItems: [
|
||||
{ date: '30. Juni 2026', label: 'Bewerbungsschluss' },
|
||||
{ date: 'August 2026', label: 'Bekanntgabe Nominierte' },
|
||||
{ date: 'Oktober 2026', label: 'Gala-Verleihung' },
|
||||
],
|
||||
},
|
||||
applicationForm: {
|
||||
imageFilename: 'preistraeger-jubel.jpg',
|
||||
imageAlt: 'Preisverleihung 2025',
|
||||
imageCaption: 'Preisverleihung 2025',
|
||||
eyebrow: 'Direktbewerbung',
|
||||
heading: 'BEWERBEN ODER\nVORSCHLAGEN.',
|
||||
description:
|
||||
'Sie können sich selbst bewerben oder ein herausragendes Unternehmen vorschlagen. Firmenverbunde können sich ebenfalls bewerben. Die Teilnahme ist vollständig kostenfrei.',
|
||||
facts: [
|
||||
{ num: '01', label: '0 EUR', desc: 'Teilnahmegebühr' },
|
||||
{ num: '02', label: 'Schnell', desc: 'Ca. 30–45 Minuten' },
|
||||
{ num: '03', label: 'Mehrstufig', desc: 'Transparenter Prozess' },
|
||||
],
|
||||
formEyebrow: 'Bewerbung 2026',
|
||||
uploadCta: { label: 'PDF hochladen', url: '/formular-hochladen' },
|
||||
uploadPrompt: 'Sie haben Ihre Unterlagen bereits als PDF vorbereitet? Laden Sie sie direkt hoch, statt das Formular auszufüllen.',
|
||||
uploadFooterCta: { label: 'Formular hochladen →', url: '/formular-hochladen' },
|
||||
},
|
||||
form: {
|
||||
stepCompleteLabel: '✓',
|
||||
backLabel: 'Zurück',
|
||||
nextLabel: 'Weiter',
|
||||
submitLabel: 'Absenden',
|
||||
yesLabel: 'Ja',
|
||||
noLabel: 'Nein',
|
||||
requiredSuffix: '*',
|
||||
validation: {
|
||||
choose: 'Bitte wählen',
|
||||
required: 'Pflichtfeld',
|
||||
invalidEmail: 'Ungültige E-Mail',
|
||||
},
|
||||
typeStep: {
|
||||
eyebrow: 'Schritt 1',
|
||||
heading: 'Art der Einreichung',
|
||||
selfTitle: 'Eigenbewerbung',
|
||||
selfDesc: 'Ich bewerbe mein eigenes Unternehmen für den Bayerischen Mittelstandspreis.',
|
||||
nominationTitle: 'Vorschlag',
|
||||
nominationDesc: 'Ich schlage ein anderes Unternehmen vor, das den Preis verdient.',
|
||||
},
|
||||
selfSteps: [
|
||||
{ label: 'Einreichung', icon: 'trophy' },
|
||||
{ label: 'Schnell-Check', icon: 'check' },
|
||||
{ label: 'Kontakt', icon: 'user' },
|
||||
{ label: 'Absenden', icon: 'send' },
|
||||
],
|
||||
nominationSteps: [
|
||||
{ label: 'Einreichung', icon: 'trophy' },
|
||||
{ label: 'Unternehmen', icon: 'building2' },
|
||||
{ label: 'Nominierender', icon: 'user' },
|
||||
{ label: 'Absenden', icon: 'send' },
|
||||
],
|
||||
employeeOptions: [
|
||||
{ value: 'unter10', label: 'Unter 10' },
|
||||
{ value: '10-50', label: '10 – 50' },
|
||||
{ value: '51-200', label: '51 – 200' },
|
||||
{ value: '201-500', label: '201 – 500' },
|
||||
{ value: 'über500', label: 'Über 500' },
|
||||
],
|
||||
eligibility: {
|
||||
allowedEmployeeValues: '10-50,51-200,201-500',
|
||||
requiredBayernValue: 'ja',
|
||||
eyebrow: 'Schnell-Check',
|
||||
heading: 'Grundlegende Eignung',
|
||||
employeesLabel: 'Wie viele Mitarbeiter hat Ihr Unternehmen?',
|
||||
bayernLabel: 'Hat Ihr Unternehmen seinen Sitz in Bayern?',
|
||||
ownerLabel: 'Ist Ihr Unternehmen inhabergeführt oder familiengeführt?',
|
||||
ineligibleHeading: 'Leider nicht förderfähig',
|
||||
ineligibleBody:
|
||||
'Der Bayerische Mittelstandspreis richtet sich an inhabergeführte Unternehmen mit 10–500 Mitarbeitern und Sitz in Bayern. Ihr Unternehmen erfüllt diese Voraussetzungen aktuell nicht.',
|
||||
ineligibleContactPrefix: 'Fragen? Schreiben Sie uns:',
|
||||
ineligibleContactEmail: 'info@bmp-bayern.de',
|
||||
},
|
||||
selfContact: {
|
||||
eyebrow: 'Kontakt',
|
||||
heading: 'Ihre Kontaktdaten',
|
||||
companyLabel: 'Firmenname',
|
||||
companyPlaceholder: 'Muster GmbH',
|
||||
nameLabel: 'Ihr Name',
|
||||
namePlaceholder: 'Vor- und Nachname',
|
||||
emailLabel: 'E-Mail',
|
||||
emailPlaceholder: 'name@unternehmen.de',
|
||||
phoneLabel: 'Telefon',
|
||||
phonePlaceholder: '+49 89 ...',
|
||||
footnote: 'Wir senden Ihnen den vollständigen Fragebogen automatisch per E-Mail zu.',
|
||||
},
|
||||
selfSummary: {
|
||||
eyebrow: 'Zusammenfassung',
|
||||
heading: 'Ihre Bewerbung',
|
||||
companyLabel: 'Unternehmen',
|
||||
employeesLabel: 'Mitarbeiter',
|
||||
locationLabel: 'Standort',
|
||||
locationPrefix: 'Bayern:',
|
||||
contactLabel: 'Kontakt',
|
||||
},
|
||||
nominationCompany: {
|
||||
eyebrow: 'Nominierung',
|
||||
heading: 'Nominiertes Unternehmen',
|
||||
companyLabel: 'Firmenname',
|
||||
companyPlaceholder: 'Muster GmbH',
|
||||
industryLabel: 'Branche',
|
||||
industryPlaceholder: 'z. B. Maschinenbau',
|
||||
locationLabel: 'Standort Bayern',
|
||||
locationPlaceholder: 'z. B. München',
|
||||
},
|
||||
nominationContact: {
|
||||
eyebrow: 'Nominierender',
|
||||
heading: 'Ihre Angaben',
|
||||
nameLabel: 'Ihr Name',
|
||||
namePlaceholder: 'Vor- und Nachname',
|
||||
emailLabel: 'Ihre E-Mail',
|
||||
emailPlaceholder: 'ihre@email.de',
|
||||
relationshipLabel: 'Ihre Beziehung zum Unternehmen',
|
||||
relationshipPlaceholder: 'z. B. Kunde, Partner, Bekannter',
|
||||
footnote: 'Wir nehmen Kontakt mit dem nominierten Unternehmen auf und informieren es über Ihre Nominierung.',
|
||||
},
|
||||
nominationSummary: {
|
||||
eyebrow: 'Zusammenfassung',
|
||||
heading: 'Ihre Nominierung',
|
||||
companyLabel: 'Unternehmen',
|
||||
industryLabel: 'Branche',
|
||||
locationLabel: 'Standort',
|
||||
nominatedByLabel: 'Nominiert von',
|
||||
emptyValue: '–',
|
||||
},
|
||||
success: {
|
||||
selfHeading: 'Anfrage eingegangen',
|
||||
nominationHeading: 'Nominierung eingegangen',
|
||||
selfPrefix: 'Vielen Dank, ',
|
||||
selfMiddle: '. Wir senden Ihnen den vollständigen Fragebogen an ',
|
||||
selfSuffix: '.',
|
||||
nominationPrefix: 'Vielen Dank, ',
|
||||
nominationMiddle: '. Wir nehmen Kontakt mit ',
|
||||
nominationSuffix: ' auf und informieren sie über Ihre Nominierung.',
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user