feat: add application form configuration and migration for shared application form data

- Introduced a new configuration file for the application form with various fields and validation messages.
- Created a migration script to set up the database schema for the application form, including necessary tables and default values.
- Added a migration for supporting awards copy to enhance the pages with additional columns.
- Implemented a new SPA component for the application form section, integrating form logic and UI elements.
This commit is contained in:
syntaxbullet
2026-07-13 16:07:58 +02:00
parent ee7e891a7d
commit 9ebe544987
19 changed files with 1026 additions and 259 deletions

View File

@@ -0,0 +1,205 @@
import type { Field, GlobalConfig } from 'payload'
import { authenticated } from '@/access/authenticated'
import { homeContent } from '@/spa/homeContent'
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 iconOptions = [
{ label: 'Alert circle', value: 'alertCircle' },
{ label: 'Arrow right', value: 'arrowRight' },
{ label: 'Building', value: 'building2' },
{ label: 'Check', value: 'check' },
{ label: 'Send', value: 'send' },
{ label: 'Trophy', value: 'trophy' },
{ label: 'User', value: 'user' },
]
const iconField = (): Field => ({
name: 'icon',
label: 'Icon',
type: 'select',
defaultValue: 'trophy',
options: iconOptions,
})
const formFields: Field[] = [
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: 'application_form_self_steps',
defaultValue: homeContent.form.selfSteps,
fields: [text('label', 'Label'), iconField()],
},
{
name: 'nominationSteps',
label: 'Nomination step labels',
type: 'array',
dbName: 'application_form_nom_steps',
defaultValue: homeContent.form.nominationSteps,
fields: [text('label', 'Label'), iconField()],
},
{
name: 'employeeOptions',
label: 'Employee options',
type: 'array',
dbName: 'application_form_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)),
},
]
export const ApplicationForm: GlobalConfig = {
slug: 'application-form',
label: 'Bewerbungsformular',
access: {
read: () => true,
update: authenticated,
},
admin: {
group: 'Formulare',
},
fields: [
{
name: 'section',
label: 'Gemeinsamer Formularbereich',
type: 'group',
admin: {
description: 'Gemeinsamer Bereich für Startseite und Teilnahme-Seite.',
},
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',
dbName: 'application_form_benefits',
defaultValue: homeContent.application.benefits,
fields: [text('num', 'Number'), text('label', 'Label'), text('desc', 'Description')],
},
{
name: 'awardImage',
type: 'upload',
relationTo: 'media',
label: 'Award image',
admin: {
description: `Current frontend image: /images/${homeContent.application.awardImageFilename}`,
},
},
text('awardImageAlt', 'Award image alt text', homeContent.application.awardImageAlt),
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: 'Formularfelder und Logik',
type: 'group',
admin: {
description: 'Gemeinsame Texte, Optionen und Eignungsregeln des mehrstufigen Bewerbungsformulars.',
},
fields: formFields,
},
],
}