feat: manage about page via payload

This commit is contained in:
syntaxbullet
2026-06-22 10:54:18 +02:00
parent e1713f6d6e
commit 5944ebc62a
7 changed files with 1085 additions and 251 deletions

View File

@@ -0,0 +1,254 @@
import type { Field } from 'payload'
import { aboutContent } from '@/spa/aboutContent'
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 ctaGroup = (name: string, label: string, labelDefault: string, urlDefault: string): Field => ({
name,
label,
type: 'group',
fields: [text('label', 'Label', labelDefault), text('url', 'URL', urlDefault)],
})
const sectionAdmin = (description: string) => ({
description,
initCollapsed: true,
})
const statFields: Field[] = [text('value', 'Value'), text('label', 'Label')]
const bodyFields: Field[] = [textarea('text', 'Paragraph')]
const numberedFields: Field[] = [text('num', 'Number'), text('title', 'Title'), textarea('desc', 'Description')]
export const aboutFields: Field[] = [
{
name: 'about',
label: 'About / Der BMP page layout sections',
type: 'group',
admin: {
description:
'Edit the Der BMP 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 hero image, headline, CTAs, and stats strip.'),
fields: [
uploadField('backgroundImage', 'Background image', `Current frontend image: /images/${aboutContent.hero.backgroundImageFilename}`),
text('imageAlt', 'Image alt text', aboutContent.hero.imageAlt),
text('eyebrow', 'Eyebrow', aboutContent.hero.eyebrow),
textarea('heading', 'Heading', aboutContent.hero.heading),
text('highlight', 'Highlighted heading line', aboutContent.hero.highlight),
textarea('description', 'Intro paragraph', aboutContent.hero.description),
ctaGroup('primaryCta', 'Primary CTA', aboutContent.hero.primaryCta.label, aboutContent.hero.primaryCta.url),
ctaGroup('secondaryCta', 'Secondary CTA', aboutContent.hero.secondaryCta.label, aboutContent.hero.secondaryCta.url),
{
name: 'stats',
label: 'Stats',
type: 'array',
dbName: 'about_hero_stats',
defaultValue: aboutContent.hero.stats,
fields: statFields,
},
],
},
{
name: 'prize',
label: '02 · Was ist der Preis?',
type: 'group',
admin: sectionAdmin('Cream text/image section explaining the prize.'),
fields: [
uploadField('image', 'Image', `Current frontend image: /images/${aboutContent.prize.imageFilename}`),
text('imageAlt', 'Image alt text', aboutContent.prize.imageAlt),
text('eyebrow', 'Eyebrow', aboutContent.prize.eyebrow),
textarea('heading', 'Heading', aboutContent.prize.heading),
{
name: 'body',
label: 'Body paragraphs',
type: 'array',
dbName: 'about_prize_body',
defaultValue: aboutContent.prize.body.map((text) => ({ text })),
fields: bodyFields,
},
{
name: 'facts',
label: 'Key facts',
type: 'array',
dbName: 'about_prize_facts',
defaultValue: aboutContent.prize.facts,
fields: statFields,
},
text('imageLabel', 'Desktop image label', aboutContent.prize.imageLabel),
],
},
{
name: 'mittelstand',
label: '03 · Bedeutung für den Mittelstand',
type: 'group',
admin: sectionAdmin('Navy text/image section about relevance and goals.'),
fields: [
uploadField('image', 'Image', `Current frontend image: /images/${aboutContent.mittelstand.imageFilename}`),
text('imageAlt', 'Image alt text', aboutContent.mittelstand.imageAlt),
text('eyebrow', 'Eyebrow', aboutContent.mittelstand.eyebrow),
textarea('heading', 'Heading', aboutContent.mittelstand.heading),
{
name: 'body',
label: 'Body paragraphs',
type: 'array',
dbName: 'about_mid_body',
defaultValue: aboutContent.mittelstand.body.map((text) => ({ text })),
fields: bodyFields,
},
],
},
{
name: 'highlights',
label: '04 · Preisträger highlights',
type: 'group',
admin: sectionAdmin('Header copy and editor-selected winners for the photo grid.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.highlights.eyebrow),
textarea('heading', 'Heading', aboutContent.highlights.heading),
ctaGroup('cta', 'CTA', aboutContent.highlights.cta.label, aboutContent.highlights.cta.url),
text('hoverLabel', 'Card hover label', aboutContent.highlights.hoverLabel),
{
name: 'year',
label: 'Fallback winner year',
type: 'number',
defaultValue: aboutContent.highlights.year,
},
{
name: 'selectedWinners',
label: 'Selected winners',
type: 'relationship',
relationTo: 'preistraeger',
hasMany: true,
maxRows: 8,
admin: {
description: 'Choose up to 8 Preisträger to show. If empty, the frontend falls back to winners from the selected year.',
},
},
],
},
{
name: 'testimonials',
label: '05 · Testimonials',
type: 'group',
admin: sectionAdmin('Testimonial heading and slides. Upload images when available, or keep the migrated URL fallback.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.testimonials.eyebrow),
textarea('heading', 'Heading', aboutContent.testimonials.heading),
{
name: 'items',
label: 'Testimonials',
type: 'array',
dbName: 'about_tst_items',
defaultValue: aboutContent.testimonials.items,
fields: [
uploadField('image', 'Image'),
text('imageUrl', 'Fallback image URL'),
text('name', 'Name'),
text('role', 'Role'),
text('company', 'Company'),
text('year', 'Award year'),
textarea('quote', 'Quote'),
],
},
],
},
{
name: 'history',
label: '06 · Geschichte & Meilensteine',
type: 'group',
admin: sectionAdmin('Navy timeline section.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.history.eyebrow),
textarea('heading', 'Heading', aboutContent.history.heading),
text('highlight', 'Highlighted heading word', aboutContent.history.highlight),
text('watermark', 'Desktop watermark', aboutContent.history.watermark),
{
name: 'items',
label: 'Timeline items',
type: 'array',
dbName: 'about_hist_items',
defaultValue: aboutContent.history.items,
fields: [text('year', 'Year'), text('title', 'Title'), textarea('desc', 'Description')],
},
],
},
{
name: 'goals',
label: '07 · Zielsetzung',
type: 'group',
admin: sectionAdmin('Cream values list next to the history timeline.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.goals.eyebrow),
textarea('heading', 'Heading', aboutContent.goals.heading),
{
name: 'items',
label: 'Goal rows',
type: 'array',
dbName: 'about_goal_items',
defaultValue: aboutContent.goals.items,
fields: numberedFields,
},
],
},
{
name: 'values',
label: '08 · Vorteile & Werte',
type: 'group',
admin: sectionAdmin('Cream editorial rows before the final CTA.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.values.eyebrow),
textarea('heading', 'Heading', aboutContent.values.heading),
textarea('description', 'Description', aboutContent.values.description),
{
name: 'items',
label: 'Value rows',
type: 'array',
dbName: 'about_val_items',
defaultValue: aboutContent.values.items,
fields: [text('num', 'Number'), text('title', 'Title')],
},
],
},
{
name: 'cta',
label: '09 · Final CTA',
type: 'group',
admin: sectionAdmin('Bottom navy CTA section.'),
fields: [
text('eyebrow', 'Eyebrow', aboutContent.cta.eyebrow),
textarea('heading', 'Heading', aboutContent.cta.heading),
textarea('description', 'Description', aboutContent.cta.description),
ctaGroup('primaryCta', 'Primary CTA', aboutContent.cta.primaryCta.label, aboutContent.cta.primaryCta.url),
text('secondaryPrefix', 'Secondary prefix', aboutContent.cta.secondaryPrefix),
ctaGroup('secondaryCta', 'Secondary CTA', aboutContent.cta.secondaryCta.label, aboutContent.cta.secondaryCta.url),
],
},
],
},
]

View File

@@ -8,6 +8,7 @@ import { Content } from '../../blocks/Content/config'
import { FormBlock } from '../../blocks/Form/config'
import { MediaBlock } from '../../blocks/MediaBlock/config'
import { hero } from '@/heros/config'
import { aboutFields } from './aboutFields'
import { contactFields } from './contactFields'
import { homeFields } from './homeFields'
import { impressumFields } from './impressumFields'
@@ -49,6 +50,14 @@ const isParticipationPage = (_: unknown, siblingData?: { slug?: string; spaPath?
return spaPath === '/teilnahme' || slug === 'teilnahme' || slug === 'participation' || title === 'teilnahme' || title === 'participation'
}
const isAboutPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) => {
const slug = siblingData?.slug
const spaPath = siblingData?.spaPath
const title = siblingData?.title?.toLowerCase()
return spaPath === '/der-bmp' || slug === 'der-bmp' || slug === 'about' || title === 'der bmp' || title === 'about'
}
const isImpressumPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) => {
const slug = siblingData?.slug
const spaPath = siblingData?.spaPath
@@ -61,6 +70,7 @@ const isManagedSpaPage = (_: unknown, siblingData?: { slug?: string; spaPath?: s
isHomePage(undefined, siblingData) ||
isContactPage(undefined, siblingData) ||
isParticipationPage(undefined, siblingData) ||
isAboutPage(undefined, siblingData) ||
isImpressumPage(undefined, siblingData)
export const Pages: CollectionConfig<'pages'> = {
@@ -150,6 +160,13 @@ export const Pages: CollectionConfig<'pages'> = {
fields: participationFields,
label: 'Participation Page',
},
{
admin: {
condition: (data) => isAboutPage(undefined, data),
},
fields: aboutFields,
label: 'About Page',
},
{
admin: {
condition: (data) => isImpressumPage(undefined, data),