import type { CollectionBeforeChangeHook, CollectionConfig } from "payload"; import { isAdmin, publishedOrAdmin } from "@/access"; import { slugField } from "@/fields/slug"; import { seoFields } from "@/fields/seo"; import { generateMeetingPassword } from "@/lib/meeting/password"; export const EVENT_CATEGORIES = [ { label: "Prozessbegleitung", value: "prozessbegleitung" }, { label: "Doula", value: "doula" }, { label: "Erdenkinder", value: "erdenkinder" }, { label: "Mädchenkreis", value: "maedchenkreis" }, { label: "Singen im Kreis", value: "singen-im-kreis" }, { label: "Singen für Schwangere", value: "singen-fuer-schwangere" }, { label: "Singen für Mamas mit Baby", value: "singen-fuer-mamas-mit-baby" }, { label: "Sonstiges", value: "sonstiges" }, ] as const; // Auto-generates the meeting password for new online events, and // regenerates it whenever the admin ticks "Neues Passwort generieren". // Never derived from the event id/slug — always a fresh random value. const setMeetingPassword: CollectionBeforeChangeHook = ({ data }) => { if (!data?.isOnline) return data; if (!data.meetingPassword || data.regeneratePassword) { data.meetingPassword = generateMeetingPassword(); } data.regeneratePassword = false; return data; }; export const Events: CollectionConfig = { slug: "events", labels: { singular: "Termin", plural: "Termine", }, admin: { useAsTitle: "title", defaultColumns: ["title", "date", "category", "_status"], group: "Inhalte", description: "Termine und Veranstaltungen — erscheinen automatisch unter /termine.", }, versions: { drafts: { autosave: { interval: 1500 }, }, }, access: { read: publishedOrAdmin, create: isAdmin, update: isAdmin, delete: isAdmin, }, hooks: { beforeChange: [setMeetingPassword], }, fields: [ { name: "title", type: "text", label: "Titel", required: true, }, slugField(), { name: "description", type: "richText", label: "Beschreibung", }, { type: "row", fields: [ { name: "date", type: "date", label: "Datum", required: true, admin: { date: { pickerAppearance: "dayOnly", displayFormat: "dd.MM.yyyy" }, width: "34%", }, }, { name: "startTime", type: "date", label: "Startzeit", admin: { date: { pickerAppearance: "timeOnly", displayFormat: "HH:mm" }, width: "33%", }, }, { name: "endTime", type: "date", label: "Endzeit", admin: { date: { pickerAppearance: "timeOnly", displayFormat: "HH:mm" }, width: "33%", }, }, ], }, { name: "location", type: "text", label: "Ort", }, { name: "category", type: "select", label: "Kategorie", required: true, defaultValue: "sonstiges", options: [...EVENT_CATEGORIES], }, { name: "image", type: "upload", relationTo: "media", label: "Bild", }, { type: "row", fields: [ { name: "maxParticipants", type: "number", label: "Maximale Teilnehmerzahl", min: 1, admin: { width: "50%" }, }, { name: "registrationRequired", type: "checkbox", label: "Anmeldung erforderlich", defaultValue: false, admin: { width: "50%" }, }, ], }, { name: "registrationInfo", type: "text", label: "Anmeldelink bzw. Kontaktmöglichkeit", admin: { description: "z. B. eine E-Mail-Adresse, ein Link oder ein Hinweistext.", }, }, { type: "collapsible", label: "Online-Termin (Video-Call)", fields: [ { name: "isOnline", type: "checkbox", label: "Online-Termin", defaultValue: false, admin: { description: "Aktiviert Meeting-Passwort, Beitrittsseite und Erinnerungs-E-Mails.", }, }, { name: "meetingPassword", type: "text", label: "Meeting-Passwort", admin: { readOnly: true, condition: (data) => Boolean(data?.isOnline), description: "Wird automatisch erzeugt. Ist nicht Teil des Meeting-Links.", }, }, { name: "regeneratePassword", type: "checkbox", label: "Neues Passwort generieren (beim Speichern)", defaultValue: false, admin: { condition: (data) => Boolean(data?.isOnline), }, }, { type: "row", admin: { condition: (data) => Boolean(data?.isOnline) }, fields: [ { name: "reminder60Enabled", type: "checkbox", label: "Erinnerung 60 Minuten vorher", defaultValue: true, admin: { width: "34%" }, }, { name: "reminder30Enabled", type: "checkbox", label: "Erinnerung 30 Minuten vorher", defaultValue: true, admin: { width: "33%" }, }, { name: "hostReminderEnabled", type: "checkbox", label: "Auch Host per E-Mail erinnern", defaultValue: true, admin: { width: "33%" }, }, ], }, { type: "row", admin: { condition: (data) => Boolean(data?.isOnline) }, fields: [ { name: "hostReminder60Sent", type: "checkbox", label: "Host-Reminder 60 Min. gesendet", defaultValue: false, admin: { readOnly: true, width: "50%" }, }, { name: "hostReminder30Sent", type: "checkbox", label: "Host-Reminder 30 Min. gesendet", defaultValue: false, admin: { readOnly: true, width: "50%" }, }, ], }, ], }, { name: "isPrivateBooking", type: "checkbox", label: "Aus privater Buchungsanfrage erzeugt", defaultValue: false, admin: { position: "sidebar", readOnly: true, description: "Wird automatisch gesetzt, wenn dieser Termin aus einer bestätigten Einzelbuchung entstanden ist. Solche Termine bleiben unveröffentlicht.", }, }, { name: "bookingRequest", type: "relationship", relationTo: "booking-requests", label: "Zugehörige Buchungsanfrage", admin: { position: "sidebar", readOnly: true }, }, seoFields(), ], };