Files
bmp-website-2026/src/collections/Pages/homeFields.ts

385 lines
17 KiB
TypeScript

import type { Field } from 'payload'
import { homeContent } from '@/spa/homeContent'
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 linkGroup = (labelDefault: string, urlDefault: string): Field[] => [
text('label', 'Button text', labelDefault),
text('url', 'Link URL', urlDefault),
]
const sectionAdmin = (description: string) => ({
description,
initCollapsed: true,
})
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'): Field => ({
name: 'icon',
label: 'Icon',
type: 'select',
defaultValue,
options: iconOptions,
})
export const homeFields: Field[] = [
{
name: 'home',
label: 'Home page layout sections',
type: 'group',
admin: {
description:
'Edit the homepage in the same order it appears on the frontend. The generic Hero and Content tabs are hidden for the home page to avoid duplicate controls.',
},
fields: [
{
name: 'hero',
label: '01 · Hero / opening screen',
type: 'group',
admin: sectionAdmin('Top full-screen gala image, badge, headline, intro text, primary buttons, video trigger, and partner ticker.'),
fields: [
uploadField('backgroundImage', 'Background image', `Current frontend image: /images/${homeContent.hero.backgroundImageFilename}`),
text('imageAlt', 'Image alt text', homeContent.hero.imageAlt),
text('badge', 'Badge text', homeContent.hero.badge),
text('headingLine1', 'Headline line 1', homeContent.hero.headingLine1),
text('headingAccent', 'Headline accent line', homeContent.hero.headingAccent),
textarea(
'description',
'Intro paragraph',
homeContent.hero.description,
),
{ name: 'primaryCta', label: 'Primary CTA', type: 'group', fields: linkGroup(homeContent.hero.primaryCta.label, homeContent.hero.primaryCta.url) },
{ name: 'secondaryCta', label: 'Secondary CTA', type: 'group', fields: linkGroup(homeContent.hero.secondaryCta.label, homeContent.hero.secondaryCta.url) },
text('videoLabel', 'Video button label', homeContent.hero.videoLabel),
text('partnersLabel', 'Partner ticker label', homeContent.hero.partnersLabel),
{
name: 'partners',
label: 'Fallback partner ticker names',
type: 'array',
defaultValue: homeContent.hero.partners,
admin: {
description:
'Used only if partner collection records are unavailable. The frontend prefers the real logo uploads from the Partners collection.',
},
fields: [text('name', 'Name')],
},
],
},
{
name: 'quickCheck',
label: '02 · Schnell-Check eligibility section',
type: 'group',
admin: sectionAdmin('White text panel plus right-side stage image and five eligibility rows.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.quickCheck.eyebrow),
textarea('heading', 'Heading', homeContent.quickCheck.heading),
uploadField('image', 'Image', `Current frontend image: /images/${homeContent.quickCheck.imageFilename}`),
text('imageAlt', 'Image alt text', homeContent.quickCheck.imageAlt),
textarea(
'body',
'Body copy',
homeContent.quickCheck.body,
),
{
name: 'criteria',
label: 'Criteria rows',
type: 'array',
defaultValue: homeContent.quickCheck.criteria,
fields: [text('label', 'Criterion'), text('desc', 'Description')],
},
textarea(
'note',
'Small note below criteria',
homeContent.quickCheck.note,
),
{ name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.quickCheck.cta.label, homeContent.quickCheck.cta.url) },
],
},
{
name: 'intro',
label: '03 · Award intro / value proposition',
type: 'group',
admin: sectionAdmin('Jubilation image, stats, editorial quote, body copy, and CTA.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.intro.eyebrow),
textarea('heading', 'Heading', homeContent.intro.heading),
uploadField('image', 'Image', `Current frontend image: /images/${homeContent.intro.imageFilename}`),
text('imageAlt', 'Image alt text', homeContent.intro.imageAlt),
{
name: 'stats',
label: 'Stats',
type: 'array',
defaultValue: homeContent.intro.stats,
fields: [text('value', 'Value'), text('label', 'Label')],
},
textarea('quote', 'Quote', homeContent.intro.quote),
textarea(
'body',
'Body copy',
homeContent.intro.body,
),
{ name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.intro.cta.label, homeContent.intro.cta.url) },
],
},
{
name: 'winners',
label: '04 · Preisträger card grid',
type: 'group',
admin: sectionAdmin('Blue card grid with featured winner relationships.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.winners.eyebrow),
textarea('heading', 'Heading', homeContent.winners.heading),
{ name: 'cta', label: 'CTA', type: 'group', fields: linkGroup(homeContent.winners.cta.label, homeContent.winners.cta.url) },
uploadField('fallbackImage', 'Fallback card image', `Current frontend image: /images/${homeContent.winners.fallbackImageFilename}`),
text('cardFallbackTitle', 'Fallback card title', homeContent.winners.cardFallbackTitle),
text('hoverLabel', 'Card hover label', homeContent.winners.hoverLabel),
{
name: 'featured',
label: 'Featured winner cards',
type: 'relationship',
relationTo: 'preistraeger',
hasMany: true,
admin: { description: 'Choose the winner cards shown on the home page. If empty, the latest 2025 winners are used.' },
},
],
},
{
name: 'testimonials',
label: '05 · Testimonials carousel',
type: 'group',
admin: sectionAdmin('Portrait carousel and quote copy.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.testimonials.eyebrow),
textarea('heading', 'Heading', homeContent.testimonials.heading),
textarea('desktopHeading', 'Desktop heading', homeContent.testimonials.desktopHeading),
text('yearPrefix', 'Award year prefix', homeContent.testimonials.yearPrefix),
text('previousLabel', 'Previous button label', homeContent.testimonials.previousLabel),
text('nextLabel', 'Next button label', homeContent.testimonials.nextLabel),
text('slideLabelPrefix', 'Slide dot label prefix', homeContent.testimonials.slideLabelPrefix),
{
name: 'items',
label: 'Testimonials',
type: 'array',
defaultValue: homeContent.testimonials.items,
fields: [uploadField('image', 'Portrait image'), text('imageUrl', 'External image URL'), text('name', 'Name'), text('role', 'Role'), text('company', 'Company'), text('year', 'Year'), textarea('quote', 'Quote')],
},
],
},
{
name: 'benefits',
label: '06 · Participation benefits',
type: 'group',
admin: sectionAdmin('White left panel and networking image.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.benefits.eyebrow),
textarea('heading', 'Heading', homeContent.benefits.heading),
uploadField('image', 'Image', `Current frontend image: /images/${homeContent.benefits.imageFilename}`),
text('imageAlt', 'Image alt text', homeContent.benefits.imageAlt),
text('imageLabel', 'Desktop image label', homeContent.benefits.imageLabel),
{
name: 'items', label: 'Benefit rows', type: 'array', defaultValue: homeContent.benefits.items, fields: [text('num', 'Number'), text('title', 'Title'), textarea('desc', 'Description')],
},
],
},
{
name: 'application',
label: '07 · Application form section',
type: 'group',
admin: sectionAdmin('Final application pitch, award image, embedded form heading, and footnote.'),
fields: [
text('eyebrow', 'Eyebrow', homeContent.application.eyebrow),
textarea('heading', 'Heading', homeContent.application.heading),
textarea('body', 'Body copy', homeContent.application.body),
{ name: 'benefits', label: 'Benefit rows', type: 'array', defaultValue: homeContent.application.benefits, fields: [text('num', 'Number'), text('label', 'Label'), text('desc', 'Description')] },
uploadField('awardImage', 'Award image', `Current frontend image: /images/${homeContent.application.awardImageFilename}`),
text('awardImageAlt', 'Award image alt text', homeContent.application.awardImageAlt),
text('awardCaption', 'Award image caption', homeContent.application.awardCaption),
text('formEyebrow', 'Form eyebrow', homeContent.application.formEyebrow),
text('formTitle', 'Form title', homeContent.application.formTitle),
text('footnote', 'Footnote normal text', homeContent.application.footnote),
text('footnoteStrong', 'Footnote highlighted text', homeContent.application.footnoteStrong),
],
},
{
name: 'form',
label: '08 · Embedded application form copy and logic labels',
type: 'group',
admin: sectionAdmin('Text, options, and eligibility thresholds used inside the home page multi-step application form.'),
fields: [
text('stepCompleteLabel', 'Completed step label', homeContent.form.stepCompleteLabel),
text('backLabel', 'Back button label', homeContent.form.backLabel),
text('nextLabel', 'Next button label', homeContent.form.nextLabel),
text('submitLabel', 'Submit button label', homeContent.form.submitLabel),
text('yesLabel', 'Yes option label', homeContent.form.yesLabel),
text('noLabel', 'No option label', homeContent.form.noLabel),
text('requiredSuffix', 'Required field suffix', homeContent.form.requiredSuffix),
{
name: 'validation',
label: 'Validation messages',
type: 'group',
fields: [
text('choose', 'Choose message', homeContent.form.validation.choose),
text('required', 'Required message', homeContent.form.validation.required),
text('invalidEmail', 'Invalid email message', homeContent.form.validation.invalidEmail),
],
},
{
name: 'typeStep',
label: 'Step 1 · Submission type',
type: 'group',
fields: [
text('eyebrow', 'Eyebrow', homeContent.form.typeStep.eyebrow),
text('heading', 'Heading', homeContent.form.typeStep.heading),
text('selfTitle', 'Self-application title', homeContent.form.typeStep.selfTitle),
textarea('selfDesc', 'Self-application description', homeContent.form.typeStep.selfDesc),
text('nominationTitle', 'Nomination title', homeContent.form.typeStep.nominationTitle),
textarea('nominationDesc', 'Nomination description', homeContent.form.typeStep.nominationDesc),
],
},
{
name: 'selfSteps',
label: 'Self-application step labels',
type: 'array',
dbName: 'home_self_steps',
defaultValue: homeContent.form.selfSteps,
fields: [text('label', 'Label'), iconField('trophy')],
},
{
name: 'nominationSteps',
label: 'Nomination step labels',
type: 'array',
dbName: 'home_nom_steps',
defaultValue: homeContent.form.nominationSteps,
fields: [text('label', 'Label'), iconField('trophy')],
},
{
name: 'employeeOptions',
label: 'Employee options',
type: 'array',
dbName: 'home_emp_opts',
defaultValue: homeContent.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', homeContent.form.eligibility.allowedEmployeeValues),
text('requiredBayernValue', 'Required Bavaria answer value', homeContent.form.eligibility.requiredBayernValue),
text('eyebrow', 'Eyebrow', homeContent.form.eligibility.eyebrow),
text('heading', 'Heading', homeContent.form.eligibility.heading),
text('employeesLabel', 'Employees field label', homeContent.form.eligibility.employeesLabel),
text('bayernLabel', 'Bavaria field label', homeContent.form.eligibility.bayernLabel),
text('ownerLabel', 'Owner field label', homeContent.form.eligibility.ownerLabel),
text('ineligibleHeading', 'Ineligible heading', homeContent.form.eligibility.ineligibleHeading),
textarea('ineligibleBody', 'Ineligible body', homeContent.form.eligibility.ineligibleBody),
text('ineligibleContactPrefix', 'Ineligible contact prefix', homeContent.form.eligibility.ineligibleContactPrefix),
text('ineligibleContactEmail', 'Ineligible contact email', homeContent.form.eligibility.ineligibleContactEmail),
],
},
{
name: 'selfContact',
label: 'Self-application contact step',
type: 'group',
fields: Object.entries(homeContent.form.selfContact).map(([name, value]) => text(name, name, value)),
},
{
name: 'selfSummary',
label: 'Self-application summary step',
type: 'group',
fields: Object.entries(homeContent.form.selfSummary).map(([name, value]) => text(name, name, value)),
},
{
name: 'nominationCompany',
label: 'Nomination company step',
type: 'group',
fields: Object.entries(homeContent.form.nominationCompany).map(([name, value]) => text(name, name, value)),
},
{
name: 'nominationContact',
label: 'Nomination contact step',
type: 'group',
fields: Object.entries(homeContent.form.nominationContact).map(([name, value]) => text(name, name, value)),
},
{
name: 'nominationSummary',
label: 'Nomination summary step',
type: 'group',
fields: Object.entries(homeContent.form.nominationSummary).map(([name, value]) => text(name, name, value)),
},
{
name: 'success',
label: 'Success state copy',
type: 'group',
fields: Object.entries(homeContent.form.success).map(([name, value]) => text(name, name, value)),
},
],
},
{
name: 'videoModal',
label: '10 · Video modal',
type: 'group',
admin: sectionAdmin('Video and fallback copy shown when the hero video button is clicked.'),
fields: [
{
name: 'video',
type: 'upload',
relationTo: 'media',
label: 'Video',
filterOptions: {
mimeType: {
contains: 'video',
},
},
admin: {
description: 'Choose a video from the media collection. If empty, the frontend shows the fallback copy below.',
},
},
text('title', 'Title', homeContent.videoModal.title),
text('description', 'Description', homeContent.videoModal.description),
text('closeLabel', 'Close button label', homeContent.videoModal.closeLabel),
],
},
],
},
]