cms.c2sgmbh/src/collections/Certifications.ts
Martin Porwoll 05fba7f1d7 feat: add tenant-specific collections and BeforeAfterBlock
- Add Bookings Collection for porwoll.de (photography booking system)
- Add Certifications Collection for C2S (healthcare certifications)
- Add Projects Collection for gunshin.de (game development portfolio)
- Add BeforeAfterBlock for before/after image comparisons
- Add migration for 28 new database tables
- Update documentation and clean up TODO.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 01:40:17 +00:00

451 lines
11 KiB
TypeScript

import type { CollectionConfig } from 'payload'
/**
* Certifications Collection
*
* Zertifizierungen, Akkreditierungen und Qualitätssiegel (C2S)
* Für Pflegeeinrichtungen und Gesundheitsdienstleister.
*/
export const Certifications: CollectionConfig = {
slug: 'certifications',
labels: {
singular: 'Zertifizierung',
plural: 'Zertifizierungen',
},
admin: {
group: 'Qualität',
useAsTitle: 'name',
defaultColumns: ['name', 'issuer', 'type', 'validUntil', 'status'],
description: 'Zertifizierungen, Akkreditierungen und Qualitätssiegel',
},
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => Boolean(user),
delete: ({ req: { user } }) => Boolean(user?.isSuperAdmin),
},
fields: [
// Grundinformationen
{
name: 'name',
type: 'text',
required: true,
label: 'Name der Zertifizierung',
localized: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
label: 'Slug',
admin: {
description: 'URL-freundlicher Name',
},
},
{
name: 'description',
type: 'richText',
label: 'Beschreibung',
localized: true,
},
{
name: 'shortDescription',
type: 'textarea',
label: 'Kurzbeschreibung',
localized: true,
admin: {
description: 'Für Übersichten und Meta-Beschreibungen',
},
},
// Zertifizierungs-Typ
{
name: 'type',
type: 'select',
required: true,
label: 'Typ',
options: [
{ label: 'ISO-Zertifizierung', value: 'iso' },
{ label: 'DIN-Norm', value: 'din' },
{ label: 'Akkreditierung', value: 'accreditation' },
{ label: 'Qualitätssiegel', value: 'seal' },
{ label: 'Mitgliedschaft', value: 'membership' },
{ label: 'Auszeichnung', value: 'award' },
{ label: 'Lizenz', value: 'license' },
{ label: 'Genehmigung', value: 'approval' },
{ label: 'Sonstiges', value: 'other' },
],
},
// Branchen-Kategorien (speziell für Pflege/Gesundheit)
{
name: 'category',
type: 'select',
label: 'Branchenkategorie',
options: [
{ label: 'Qualitätsmanagement', value: 'quality' },
{ label: 'Pflege', value: 'care' },
{ label: 'Medizin', value: 'medical' },
{ label: 'Hygiene', value: 'hygiene' },
{ label: 'Arbeitsschutz', value: 'safety' },
{ label: 'Datenschutz', value: 'privacy' },
{ label: 'Umwelt', value: 'environment' },
{ label: 'Personal', value: 'hr' },
{ label: 'IT-Sicherheit', value: 'it-security' },
{ label: 'Barrierefreiheit', value: 'accessibility' },
{ label: 'Sonstiges', value: 'other' },
],
},
// Aussteller
{
name: 'issuer',
type: 'group',
label: 'Ausstellende Organisation',
fields: [
{
name: 'name',
type: 'text',
required: true,
label: 'Name',
},
{
name: 'logo',
type: 'upload',
relationTo: 'media',
label: 'Logo',
},
{
name: 'website',
type: 'text',
label: 'Website',
},
{
name: 'country',
type: 'select',
label: 'Land',
options: [
{ label: 'Deutschland', value: 'DE' },
{ label: 'Österreich', value: 'AT' },
{ label: 'Schweiz', value: 'CH' },
{ label: 'EU', value: 'EU' },
{ label: 'International', value: 'INT' },
],
},
],
},
// Zertifikat-Details
{
name: 'certNumber',
type: 'text',
label: 'Zertifikatsnummer',
},
{
type: 'row',
fields: [
{
name: 'issuedDate',
type: 'date',
label: 'Ausstellungsdatum',
admin: {
width: '50%',
date: {
pickerAppearance: 'dayOnly',
displayFormat: 'dd.MM.yyyy',
},
},
},
{
name: 'validUntil',
type: 'date',
label: 'Gültig bis',
admin: {
width: '50%',
date: {
pickerAppearance: 'dayOnly',
displayFormat: 'dd.MM.yyyy',
},
},
},
],
},
{
name: 'renewalCycle',
type: 'select',
label: 'Erneuerungszyklus',
options: [
{ label: 'Jährlich', value: 'yearly' },
{ label: 'Alle 2 Jahre', value: '2years' },
{ label: 'Alle 3 Jahre', value: '3years' },
{ label: 'Alle 5 Jahre', value: '5years' },
{ label: 'Unbefristet', value: 'unlimited' },
],
},
// Medien
{
name: 'logo',
type: 'upload',
relationTo: 'media',
label: 'Zertifikats-Logo/Siegel',
},
{
name: 'certificate',
type: 'upload',
relationTo: 'media',
label: 'Zertifikat (PDF)',
admin: {
description: 'Das offizielle Zertifikatsdokument',
},
},
{
name: 'gallery',
type: 'array',
label: 'Weitere Dokumente',
fields: [
{
name: 'document',
type: 'upload',
relationTo: 'media',
label: 'Dokument',
},
{
name: 'title',
type: 'text',
label: 'Titel',
},
],
},
// Geltungsbereich
{
name: 'scope',
type: 'group',
label: 'Geltungsbereich',
fields: [
{
name: 'description',
type: 'textarea',
label: 'Beschreibung des Geltungsbereichs',
localized: true,
},
{
name: 'locations',
type: 'relationship',
relationTo: 'locations',
hasMany: true,
label: 'Standorte',
admin: {
description: 'Für welche Standorte gilt die Zertifizierung?',
},
},
{
name: 'services',
type: 'relationship',
relationTo: 'services',
hasMany: true,
label: 'Leistungen',
admin: {
description: 'Für welche Leistungen gilt die Zertifizierung?',
},
},
],
},
// Anforderungen & Standards
{
name: 'requirements',
type: 'array',
label: 'Erfüllte Anforderungen',
fields: [
{
name: 'requirement',
type: 'text',
required: true,
label: 'Anforderung',
localized: true,
},
{
name: 'description',
type: 'textarea',
label: 'Erläuterung',
localized: true,
},
],
},
// Vorteile für Kunden
{
name: 'benefits',
type: 'array',
label: 'Vorteile / Was bedeutet das für Sie?',
fields: [
{
name: 'title',
type: 'text',
required: true,
label: 'Titel',
localized: true,
},
{
name: 'description',
type: 'textarea',
label: 'Beschreibung',
localized: true,
},
{
name: 'icon',
type: 'select',
label: 'Icon',
options: [
{ label: 'Häkchen', value: 'check' },
{ label: 'Stern', value: 'star' },
{ label: 'Schild', value: 'shield' },
{ label: 'Herz', value: 'heart' },
{ label: 'Schloss', value: 'lock' },
{ label: 'Lupe', value: 'search' },
{ label: 'Uhr', value: 'clock' },
{ label: 'Dokument', value: 'document' },
],
},
],
},
// Audit-Informationen
{
name: 'audits',
type: 'array',
label: 'Audits / Prüfungen',
admin: {
description: 'Historie der durchgeführten Audits',
},
fields: [
{
name: 'date',
type: 'date',
required: true,
label: 'Datum',
admin: {
date: {
pickerAppearance: 'dayOnly',
displayFormat: 'dd.MM.yyyy',
},
},
},
{
name: 'type',
type: 'select',
label: 'Art',
options: [
{ label: 'Erst-Zertifizierung', value: 'initial' },
{ label: 'Überwachungsaudit', value: 'surveillance' },
{ label: 'Re-Zertifizierung', value: 'recertification' },
{ label: 'Sonderaudit', value: 'special' },
],
},
{
name: 'result',
type: 'select',
label: 'Ergebnis',
options: [
{ label: 'Bestanden', value: 'passed' },
{ label: 'Mit Auflagen bestanden', value: 'conditional' },
{ label: 'Nicht bestanden', value: 'failed' },
],
},
{
name: 'notes',
type: 'textarea',
label: 'Anmerkungen',
},
],
},
// Status
{
name: 'status',
type: 'select',
required: true,
defaultValue: 'active',
label: 'Status',
options: [
{ label: 'Aktiv', value: 'active' },
{ label: 'In Bearbeitung', value: 'pending' },
{ label: 'Erneuerung fällig', value: 'renewal' },
{ label: 'Ausgesetzt', value: 'suspended' },
{ label: 'Abgelaufen', value: 'expired' },
{ label: 'Zurückgezogen', value: 'withdrawn' },
],
admin: {
position: 'sidebar',
},
},
// Sichtbarkeit
{
name: 'visibility',
type: 'select',
defaultValue: 'public',
label: 'Sichtbarkeit',
options: [
{ label: 'Öffentlich', value: 'public' },
{ label: 'Nur auf Anfrage', value: 'request' },
{ label: 'Intern', value: 'internal' },
],
admin: {
position: 'sidebar',
},
},
// Priorität für Anzeige
{
name: 'priority',
type: 'number',
defaultValue: 0,
label: 'Priorität',
admin: {
position: 'sidebar',
description: 'Höhere Zahl = höhere Priorität in der Anzeige',
},
},
// Auf Startseite anzeigen
{
name: 'showOnHomepage',
type: 'checkbox',
defaultValue: false,
label: 'Auf Startseite anzeigen',
admin: {
position: 'sidebar',
},
},
// SEO
{
name: 'seo',
type: 'group',
label: 'SEO',
fields: [
{
name: 'metaTitle',
type: 'text',
label: 'Meta-Titel',
localized: true,
},
{
name: 'metaDescription',
type: 'textarea',
label: 'Meta-Beschreibung',
localized: true,
},
],
},
],
timestamps: true,
hooks: {
beforeChange: [
({ data }) => {
// Auto-generate slug if not provided
if (data && !data.slug && data.name) {
data.slug = data.name
.toLowerCase()
.replace(/[äöüß]/g, (char: string) => {
const map: Record<string, string> = { ä: 'ae', ö: 'oe', ü: 'ue', ß: 'ss' }
return map[char] || char
})
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '')
}
return data
},
],
},
}