Files
bmp-website-2026/src/collections/Events.ts
2026-07-31 19:03:07 +02:00

292 lines
9.0 KiB
TypeScript

import type { CollectionConfig } from 'payload'
import { authenticated } from '@/access/authenticated'
import { authenticatedOrPublished } from '@/access/authenticatedOrPublished'
import { populatePublishedAt } from '@/hooks/populatePublishedAt'
import { eventDetailContent } from '@/spa/eventDetailContent'
import { generatePreviewPath } from '@/utilities/generatePreviewPath'
import { slugField } from 'payload'
import { revalidateEvent, revalidateEventDelete } from './Events/hooks/revalidateEvent'
import {
MetaDescriptionField,
MetaTitleField,
OverviewField,
PreviewField,
} from '@payloadcms/plugin-seo/fields'
export const Events: CollectionConfig = {
slug: 'events',
labels: {
singular: 'Termin / Event',
plural: 'Termine & Events',
},
access: {
create: authenticated,
delete: authenticated,
read: authenticatedOrPublished,
update: authenticated,
},
admin: {
defaultColumns: ['title', 'spaPath', 'slug', 'updatedAt'],
group: 'Inhalte',
livePreview: {
url: ({ data, req }) =>
generatePreviewPath({
slug: data?.spaPath || data?.slug,
collection: 'events',
req,
}),
},
preview: (data, { req }) =>
generatePreviewPath({
slug: ((data?.spaPath as string | undefined) || (data?.slug as string)),
collection: 'events',
req,
}),
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'subtitle',
type: 'text',
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
},
{
name: 'description',
type: 'textarea',
},
{
name: 'longDescription',
type: 'textarea',
},
{
name: 'date',
type: 'text',
},
{
name: 'time',
type: 'text',
},
{
name: 'location',
type: 'text',
},
{
name: 'venue',
type: 'text',
},
{
name: 'category',
type: 'text',
},
{
name: 'status',
type: 'select',
options: [
{ label: 'Geplant', value: 'geplant' },
{ label: 'Anmeldung offen', value: 'anmeldung-offen' },
{ label: 'Intern', value: 'intern' },
{ label: 'Abgeschlossen', value: 'abgeschlossen' },
],
defaultValue: 'geplant',
},
{
name: 'eckdaten',
type: 'array',
fields: [
{ name: 'label', type: 'text', required: true },
{ name: 'value', type: 'text', required: true },
],
},
{
name: 'updates',
type: 'array',
fields: [
{ name: 'timestamp', type: 'text', required: true },
{
name: 'type',
type: 'select',
options: ['ankündigung', 'erinnerung', 'update', 'highlight', 'ergebnis', 'wichtig'],
required: true,
},
{ name: 'title', type: 'text', required: true },
{ name: 'body', type: 'textarea', required: true },
],
},
{
name: 'detailPage',
label: 'Detail page chrome',
type: 'group',
admin: {
description:
'Shared labels, status/update labels, subscription copy, CTAs, and formatting copy for the Event detail route.',
},
fields: [
{
name: 'fallbackImage',
type: 'upload',
relationTo: 'media',
label: 'Fallback hero image',
admin: {
description: `Current frontend fallback image: /images/${eventDetailContent.fallbackImageFilename}`,
},
},
{
name: 'notFound',
label: 'Not found state',
type: 'group',
fields: [
{ name: 'title', type: 'text', defaultValue: eventDetailContent.notFound.title },
{ name: 'backLabel', type: 'text', defaultValue: eventDetailContent.notFound.backLabel },
{ name: 'backUrl', type: 'text', defaultValue: eventDetailContent.notFound.backUrl },
],
},
{
name: 'dateFormat',
label: 'Date format',
type: 'group',
fields: [
{
name: 'monthNames',
type: 'json',
defaultValue: eventDetailContent.dateFormat.monthNames,
admin: { description: 'Array of 12 month names used when formatting update timestamps.' },
},
{ name: 'timeSuffix', type: 'text', defaultValue: eventDetailContent.dateFormat.timeSuffix },
],
},
{
name: 'updateStyles',
label: 'Update type labels/styles JSON',
type: 'json',
defaultValue: eventDetailContent.updateStyles,
},
{
name: 'statusStyles',
label: 'Status labels/styles JSON',
type: 'json',
defaultValue: eventDetailContent.statusStyles,
},
{
name: 'heroBackLink',
label: 'Hero back link',
type: 'group',
fields: [
{ name: 'label', type: 'text', defaultValue: eventDetailContent.heroBackLink.label },
{ name: 'url', type: 'text', defaultValue: eventDetailContent.heroBackLink.url },
],
},
{
name: 'fallbackEvent',
label: 'Missing event field fallbacks',
type: 'group',
fields: [
{ name: 'title', type: 'text', defaultValue: eventDetailContent.fallbackEvent.title },
{ name: 'date', type: 'text', defaultValue: eventDetailContent.fallbackEvent.date },
{ name: 'time', type: 'text', defaultValue: eventDetailContent.fallbackEvent.time },
{ name: 'location', type: 'text', defaultValue: eventDetailContent.fallbackEvent.location },
{ name: 'venue', type: 'text', defaultValue: eventDetailContent.fallbackEvent.venue },
{ name: 'category', type: 'text', defaultValue: eventDetailContent.fallbackEvent.category },
{ name: 'status', type: 'text', defaultValue: eventDetailContent.fallbackEvent.status },
{ name: 'longDescription', type: 'textarea', defaultValue: eventDetailContent.fallbackEvent.longDescription },
],
},
{
name: 'sections',
label: 'Section labels and CTAs',
type: 'group',
fields: [
{ name: 'aboutEyebrow', type: 'text', defaultValue: eventDetailContent.sections.aboutEyebrow },
{ name: 'factsEyebrow', type: 'text', defaultValue: eventDetailContent.sections.factsEyebrow },
{ name: 'registerLabel', type: 'text', defaultValue: eventDetailContent.sections.registerLabel },
{ name: 'registerUrl', type: 'text', defaultValue: eventDetailContent.sections.registerUrl },
{ name: 'questionLabel', type: 'text', defaultValue: eventDetailContent.sections.questionLabel },
{ name: 'questionUrl', type: 'text', defaultValue: eventDetailContent.sections.questionUrl },
],
},
{
name: 'updatesCopy',
label: 'Updates panel',
type: 'group',
fields: [
{ name: 'heading', type: 'text', defaultValue: eventDetailContent.updates.heading },
{ name: 'subscribeLabel', type: 'text', defaultValue: eventDetailContent.updates.subscribeLabel },
{ name: 'unsubscribeLabel', type: 'text', defaultValue: eventDetailContent.updates.unsubscribeLabel },
{ name: 'subscribedMessage', type: 'text', defaultValue: eventDetailContent.updates.subscribedMessage },
],
},
{
name: 'bottomCta',
label: 'Bottom CTA',
type: 'group',
fields: [
{ name: 'eyebrow', type: 'text', defaultValue: eventDetailContent.bottomCta.eyebrow },
{ name: 'heading', type: 'text', defaultValue: eventDetailContent.bottomCta.heading },
{ name: 'label', type: 'text', defaultValue: eventDetailContent.bottomCta.label },
{ name: 'url', type: 'text', defaultValue: eventDetailContent.bottomCta.url },
],
},
],
},
{
name: 'spaPath',
type: 'text',
index: true,
admin: {
description: 'Route in the legacy SPA used for previewing the current migrated event state.',
position: 'sidebar',
},
},
{
name: 'publishedAt',
type: 'date',
admin: {
position: 'sidebar',
},
},
{
name: 'meta',
type: 'group',
fields: [
OverviewField({
titlePath: 'meta.title',
descriptionPath: 'meta.description',
}),
MetaTitleField({ hasGenerateFn: true }),
MetaDescriptionField({}),
PreviewField({
hasGenerateFn: true,
titlePath: 'meta.title',
descriptionPath: 'meta.description',
}),
],
},
slugField(),
],
hooks: {
afterChange: [revalidateEvent],
afterDelete: [revalidateEventDelete],
beforeChange: [populatePublishedAt],
},
versions: {
drafts: {
autosave: {
interval: 100,
},
schedulePublish: true,
},
maxPerDoc: 50,
},
}