feat: convert SiteSettings and Navigation from Globals to tenant-specific Collections

- SiteSettings is now a Collection with multi-tenant support
- Navigation is now Navigations Collection with multi-tenant support
- Both added to multiTenantPlugin collections config
- Allows each tenant to have their own site settings and navigation
- API endpoints change from /api/globals/* to /api/site-settings and /api/navigations

BREAKING CHANGE: Frontends need to update API calls from globals to collections

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Martin Porwoll 2025-12-20 22:24:05 +00:00
parent 9ceef478fa
commit 6aed5a39d3
3 changed files with 77 additions and 13 deletions

View file

@ -1,13 +1,33 @@
import type { GlobalConfig } from 'payload' import type { CollectionConfig } from 'payload'
export const Navigation: GlobalConfig = { export const Navigations: CollectionConfig = {
slug: 'navigation', slug: 'navigations',
label: 'Navigation', labels: {
singular: 'Navigation',
plural: 'Navigationen',
},
admin: {
useAsTitle: 'title',
group: 'Einstellungen',
description: 'Navigationsmenüs pro Tenant',
},
access: { access: {
read: () => true, read: () => true,
update: ({ req }) => !!req.user, create: ({ req }) => Boolean(req.user?.isSuperAdmin),
update: ({ req }) => Boolean(req.user),
delete: ({ req }) => Boolean(req.user?.isSuperAdmin),
}, },
fields: [ fields: [
{
name: 'title',
type: 'text',
label: 'Titel',
required: true,
defaultValue: 'Hauptnavigation',
admin: {
description: 'Interner Name zur Identifikation',
},
},
{ {
name: 'mainMenu', name: 'mainMenu',
type: 'array', type: 'array',
@ -16,12 +36,14 @@ export const Navigation: GlobalConfig = {
{ {
name: 'label', name: 'label',
type: 'text', type: 'text',
label: 'Bezeichnung',
required: true, required: true,
localized: true, localized: true,
}, },
{ {
name: 'type', name: 'type',
type: 'select', type: 'select',
label: 'Typ',
defaultValue: 'page', defaultValue: 'page',
options: [ options: [
{ label: 'Seite', value: 'page' }, { label: 'Seite', value: 'page' },
@ -32,6 +54,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'page', name: 'page',
type: 'relationship', type: 'relationship',
label: 'Seite',
relationTo: 'pages', relationTo: 'pages',
admin: { admin: {
condition: (data, siblingData) => siblingData?.type === 'page', condition: (data, siblingData) => siblingData?.type === 'page',
@ -40,6 +63,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'url', name: 'url',
type: 'text', type: 'text',
label: 'URL',
admin: { admin: {
condition: (data, siblingData) => siblingData?.type === 'custom', condition: (data, siblingData) => siblingData?.type === 'custom',
}, },
@ -47,11 +71,13 @@ export const Navigation: GlobalConfig = {
{ {
name: 'openInNewTab', name: 'openInNewTab',
type: 'checkbox', type: 'checkbox',
label: 'In neuem Tab öffnen',
defaultValue: false, defaultValue: false,
}, },
{ {
name: 'submenu', name: 'submenu',
type: 'array', type: 'array',
label: 'Untermenü',
admin: { admin: {
condition: (data, siblingData) => siblingData?.type === 'submenu', condition: (data, siblingData) => siblingData?.type === 'submenu',
}, },
@ -59,12 +85,14 @@ export const Navigation: GlobalConfig = {
{ {
name: 'label', name: 'label',
type: 'text', type: 'text',
label: 'Bezeichnung',
required: true, required: true,
localized: true, localized: true,
}, },
{ {
name: 'linkType', name: 'linkType',
type: 'select', type: 'select',
label: 'Link-Typ',
defaultValue: 'page', defaultValue: 'page',
options: [ options: [
{ label: 'Seite', value: 'page' }, { label: 'Seite', value: 'page' },
@ -74,6 +102,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'page', name: 'page',
type: 'relationship', type: 'relationship',
label: 'Seite',
relationTo: 'pages', relationTo: 'pages',
admin: { admin: {
condition: (data, siblingData) => siblingData?.linkType === 'page', condition: (data, siblingData) => siblingData?.linkType === 'page',
@ -82,6 +111,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'url', name: 'url',
type: 'text', type: 'text',
label: 'URL',
admin: { admin: {
condition: (data, siblingData) => siblingData?.linkType === 'custom', condition: (data, siblingData) => siblingData?.linkType === 'custom',
}, },
@ -98,12 +128,14 @@ export const Navigation: GlobalConfig = {
{ {
name: 'label', name: 'label',
type: 'text', type: 'text',
label: 'Bezeichnung',
required: true, required: true,
localized: true, localized: true,
}, },
{ {
name: 'linkType', name: 'linkType',
type: 'select', type: 'select',
label: 'Link-Typ',
defaultValue: 'page', defaultValue: 'page',
options: [ options: [
{ label: 'Seite', value: 'page' }, { label: 'Seite', value: 'page' },
@ -113,6 +145,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'page', name: 'page',
type: 'relationship', type: 'relationship',
label: 'Seite',
relationTo: 'pages', relationTo: 'pages',
admin: { admin: {
condition: (data, siblingData) => siblingData?.linkType === 'page', condition: (data, siblingData) => siblingData?.linkType === 'page',
@ -121,6 +154,7 @@ export const Navigation: GlobalConfig = {
{ {
name: 'url', name: 'url',
type: 'text', type: 'text',
label: 'URL',
admin: { admin: {
condition: (data, siblingData) => siblingData?.linkType === 'custom', condition: (data, siblingData) => siblingData?.linkType === 'custom',
}, },

View file

@ -1,64 +1,85 @@
import type { GlobalConfig } from 'payload' import type { CollectionConfig } from 'payload'
export const SiteSettings: GlobalConfig = { export const SiteSettings: CollectionConfig = {
slug: 'site-settings', slug: 'site-settings',
label: 'Site Settings', labels: {
singular: 'Site Settings',
plural: 'Site Settings',
},
admin: {
useAsTitle: 'siteName',
group: 'Einstellungen',
description: 'Allgemeine Website-Einstellungen pro Tenant',
},
access: { access: {
read: () => true, read: () => true,
update: ({ req }) => !!req.user, create: ({ req }) => Boolean(req.user?.isSuperAdmin),
update: ({ req }) => Boolean(req.user),
delete: ({ req }) => Boolean(req.user?.isSuperAdmin),
}, },
fields: [ fields: [
{ {
name: 'siteName', name: 'siteName',
type: 'text', type: 'text',
defaultValue: 'porwoll.de', label: 'Website-Name',
required: true,
localized: true, localized: true,
}, },
{ {
name: 'siteTagline', name: 'siteTagline',
type: 'text', type: 'text',
label: 'Tagline',
localized: true, localized: true,
}, },
{ {
name: 'logo', name: 'logo',
type: 'upload', type: 'upload',
label: 'Logo',
relationTo: 'media', relationTo: 'media',
}, },
{ {
name: 'favicon', name: 'favicon',
type: 'upload', type: 'upload',
label: 'Favicon',
relationTo: 'media', relationTo: 'media',
}, },
{ {
name: 'contact', name: 'contact',
type: 'group', type: 'group',
label: 'Kontakt',
fields: [ fields: [
{ {
name: 'email', name: 'email',
type: 'email', type: 'email',
label: 'E-Mail',
}, },
{ {
name: 'phone', name: 'phone',
type: 'text', type: 'text',
label: 'Telefon',
}, },
{ {
name: 'address', name: 'address',
type: 'textarea', type: 'textarea',
label: 'Adresse',
}, },
], ],
}, },
{ {
name: 'footer', name: 'footer',
type: 'group', type: 'group',
label: 'Footer',
fields: [ fields: [
{ {
name: 'copyrightText', name: 'copyrightText',
type: 'text', type: 'text',
label: 'Copyright-Text',
localized: true, localized: true,
}, },
{ {
name: 'showSocialLinks', name: 'showSocialLinks',
type: 'checkbox', type: 'checkbox',
label: 'Social Links anzeigen',
defaultValue: true, defaultValue: true,
}, },
], ],

View file

@ -70,8 +70,11 @@ import { CookieInventory } from './collections/CookieInventory'
import { ConsentLogs } from './collections/ConsentLogs' import { ConsentLogs } from './collections/ConsentLogs'
import { PrivacyPolicySettings } from './collections/PrivacyPolicySettings' import { PrivacyPolicySettings } from './collections/PrivacyPolicySettings'
import { SiteSettings } from './globals/SiteSettings' // Tenant-specific Settings Collections (converted from Globals)
import { Navigation } from './globals/Navigation' import { SiteSettings } from './collections/SiteSettings'
import { Navigations } from './collections/Navigations'
// Global Settings (system-wide, not tenant-specific)
import { SEOSettings } from './globals/SEOSettings' import { SEOSettings } from './globals/SEOSettings'
// Hooks // Hooks
@ -202,8 +205,11 @@ export default buildConfig({
// System // System
EmailLogs, EmailLogs,
AuditLogs, AuditLogs,
// Tenant-specific Settings (converted from Globals)
SiteSettings,
Navigations,
], ],
globals: [SiteSettings, Navigation, SEOSettings], globals: [SEOSettings],
editor: lexicalEditor(), editor: lexicalEditor(),
secret: env.PAYLOAD_SECRET, secret: env.PAYLOAD_SECRET,
typescript: { typescript: {
@ -265,6 +271,9 @@ export default buildConfig({
'cookie-inventory': { customTenantField: true }, 'cookie-inventory': { customTenantField: true },
'consent-logs': { customTenantField: true }, 'consent-logs': { customTenantField: true },
'privacy-policy-settings': { customTenantField: true }, 'privacy-policy-settings': { customTenantField: true },
// Tenant-specific Settings (converted from Globals)
'site-settings': {},
navigations: {},
} as Record<string, { customTenantField?: boolean }>), } as Record<string, { customTenantField?: boolean }>),
}, },
// Super Admins haben Zugriff auf alle Tenants // Super Admins haben Zugriff auf alle Tenants