- Created migration index file to manage database migrations. - Added script to preload participation awards and related blog posts into the CMS. - Introduced new types and default data for application phases. - Added award articles data structure for the upcoming awards.
414 lines
18 KiB
TypeScript
414 lines
18 KiB
TypeScript
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),
|
|
})
|
|
const awardCardDefaults = participationContent.awardsGrid.cards.map(({ imageFilename: _imageFilename, ...card }) => card)
|
|
|
|
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('stepPrefix', 'Step label prefix', participationContent.process.stepPrefix),
|
|
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: 'applicationWays',
|
|
label: '04 · 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),
|
|
ctaGroup(
|
|
'fallbackCta',
|
|
'Fallback CTA for path cards',
|
|
participationContent.applicationWays.fallbackCta.label,
|
|
participationContent.applicationWays.fallbackCta.url,
|
|
),
|
|
{
|
|
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', participationContent.applicationWays.fallbackCta.label, participationContent.applicationWays.fallbackCta.url),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'datesBanner',
|
|
label: '05 · 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: 'awardsGrid',
|
|
label: '06 · Awards grid',
|
|
type: 'group',
|
|
admin: sectionAdmin('Three-column awards grid rendered directly after the dates banner.'),
|
|
fields: [
|
|
text('eyebrow', 'Eyebrow', participationContent.awardsGrid.eyebrow),
|
|
textarea('heading', 'Heading', participationContent.awardsGrid.heading),
|
|
textarea('description', 'Intro paragraph', participationContent.awardsGrid.description),
|
|
{
|
|
name: 'cards',
|
|
label: 'Award cards',
|
|
type: 'array',
|
|
dbName: 'award_cards',
|
|
defaultValue: awardCardDefaults,
|
|
fields: [
|
|
text('label', 'Card label'),
|
|
uploadField('image', 'Card image'),
|
|
text('imageAlt', 'Image alt text'),
|
|
textarea('title', 'Title'),
|
|
textarea('description', 'Description'),
|
|
ctaGroup('articleCta', 'Article CTA', 'Artikel lesen', '/presse'),
|
|
ctaGroup('mailtoCta', 'Mail CTA', 'Kontakt aufnehmen', 'mailto:info@bmp-bayern.de'),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
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)),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
]
|