cms.c2sgmbh/src/collections/Partners.ts
Martin Porwoll 9e5c741941 feat: add priority collections and advanced content blocks
New Collections:
- Events: Veranstaltungen mit Datum, Ort, Registrierung
- Jobs: Stellenangebote mit Standort und Bewerbungsfrist
- Locations: Standorte mit Adresse, Kontakt, Öffnungszeiten
- Partners: Partner/Kunden mit Logo und Beschreibung
- Downloads: Dateien mit Kategorisierung und Tracking

New Blocks:
- EventsBlock: Veranstaltungslisten mit Kalender-Ansicht
- JobsBlock: Stellenanzeigen mit Filterfunktion
- LocationsBlock: Standort-Karten und Listen
- PricingBlock: Preistabellen mit Feature-Vergleich
- TabsBlock: Tabbed Content mit verschiedenen Stilen
- AccordionBlock: FAQ/Accordion mit Animationen
- ComparisonBlock: Vergleichstabellen (Tabelle, Karten, Pro/Contra)
- StatsBlock: Statistiken mit Counter-Animation
- LogoGridBlock: Logo-Wolken und Partner-Galerien
- MapBlock: Interaktive Karten mit Markern
- DownloadsBlock: Download-Listen mit Kategorien

All collections support multi-tenant isolation and localization.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 00:58:30 +00:00

274 lines
6.3 KiB
TypeScript

import type { CollectionConfig } from 'payload'
import { authenticatedOnly, tenantScopedPublicRead } from '../lib/tenantAccess'
/**
* Partners Collection
*
* Partner, Kunden, Zertifizierungen und Referenzen mit Logos.
* Vielseitig einsetzbar für Logo-Walls, Referenzen, Zertifikate.
*/
export const Partners: CollectionConfig = {
slug: 'partners',
labels: {
singular: 'Partner',
plural: 'Partner',
},
admin: {
useAsTitle: 'name',
group: 'Content',
defaultColumns: ['name', 'type', 'isFeatured', 'isActive', 'order'],
description: 'Partner, Kunden, Referenzen und Zertifizierungen',
},
access: {
read: tenantScopedPublicRead,
create: authenticatedOnly,
update: authenticatedOnly,
delete: authenticatedOnly,
},
fields: [
{
name: 'name',
type: 'text',
required: true,
label: 'Name',
admin: {
description: 'Firmen-/Partnername',
},
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
label: 'Slug',
},
{
name: 'type',
type: 'select',
required: true,
defaultValue: 'partner',
label: 'Typ',
options: [
{ label: 'Partner', value: 'partner' },
{ label: 'Kunde', value: 'client' },
{ label: 'Referenz', value: 'reference' },
{ label: 'Sponsor', value: 'sponsor' },
{ label: 'Zertifizierung', value: 'certification' },
{ label: 'Mitgliedschaft', value: 'membership' },
{ label: 'Auszeichnung', value: 'award' },
{ label: 'Lieferant', value: 'supplier' },
{ label: 'Technologie', value: 'technology' },
],
admin: {
position: 'sidebar',
},
},
{
name: 'logo',
type: 'upload',
relationTo: 'media',
required: true,
label: 'Logo',
admin: {
description: 'Logo (empfohlen: SVG oder PNG mit transparentem Hintergrund)',
},
},
{
name: 'logoLight',
type: 'upload',
relationTo: 'media',
label: 'Logo (Hell)',
admin: {
description: 'Alternative Version für dunkle Hintergründe',
},
},
{
name: 'description',
type: 'textarea',
label: 'Beschreibung',
localized: true,
admin: {
description: 'Kurze Beschreibung der Partnerschaft',
},
},
{
name: 'website',
type: 'text',
label: 'Website',
},
// Für Referenzen/Case Studies
{
name: 'caseStudy',
type: 'group',
label: 'Case Study / Referenz',
admin: {
condition: (_, siblingData) =>
siblingData?.type === 'client' || siblingData?.type === 'reference',
},
fields: [
{
name: 'quote',
type: 'textarea',
label: 'Zitat',
localized: true,
},
{
name: 'quotePerson',
type: 'text',
label: 'Zitatgeber',
},
{
name: 'quoteRole',
type: 'text',
label: 'Position',
},
{
name: 'projectDescription',
type: 'richText',
label: 'Projektbeschreibung',
localized: true,
},
{
name: 'results',
type: 'array',
label: 'Ergebnisse/Kennzahlen',
fields: [
{
name: 'metric',
type: 'text',
required: true,
label: 'Kennzahl',
admin: {
description: 'z.B. "+50%", "10.000+"',
},
},
{
name: 'label',
type: 'text',
required: true,
label: 'Beschreibung',
localized: true,
admin: {
description: 'z.B. "Umsatzsteigerung", "Neue Kunden"',
},
},
],
},
],
},
// Für Zertifizierungen
{
name: 'certification',
type: 'group',
label: 'Zertifizierungs-Details',
admin: {
condition: (_, siblingData) =>
siblingData?.type === 'certification' ||
siblingData?.type === 'membership' ||
siblingData?.type === 'award',
},
fields: [
{
name: 'issuer',
type: 'text',
label: 'Aussteller',
},
{
name: 'validFrom',
type: 'date',
label: 'Gültig ab',
},
{
name: 'validUntil',
type: 'date',
label: 'Gültig bis',
},
{
name: 'certNumber',
type: 'text',
label: 'Zertifikatsnummer',
},
{
name: 'document',
type: 'upload',
relationTo: 'media',
label: 'Zertifikat (PDF)',
},
],
},
// Kategorisierung
{
name: 'category',
type: 'text',
label: 'Kategorie',
admin: {
description: 'z.B. "Technologie", "Finanzen", "Pflege"',
},
},
{
name: 'tags',
type: 'array',
label: 'Tags',
fields: [
{
name: 'tag',
type: 'text',
required: true,
},
],
},
// Status
{
name: 'isFeatured',
type: 'checkbox',
defaultValue: false,
label: 'Hervorgehoben',
admin: {
position: 'sidebar',
},
},
{
name: 'isActive',
type: 'checkbox',
defaultValue: true,
label: 'Aktiv',
admin: {
position: 'sidebar',
},
},
{
name: 'order',
type: 'number',
defaultValue: 0,
label: 'Sortierung',
admin: {
position: 'sidebar',
},
},
{
name: 'since',
type: 'date',
label: 'Partner seit',
admin: {
position: 'sidebar',
},
},
],
hooks: {
beforeChange: [
({ data }) => {
if (data && !data.slug && data.name) {
data.slug = data.name
.toLowerCase()
.replace(/[äöüß]/g, (match: string) => {
const map: Record<string, string> = { ä: 'ae', ö: 'oe', ü: 'ue', ß: 'ss' }
return map[match] || match
})
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
}
return data
},
],
},
}