mirror of
https://github.com/complexcaresolutions/frontend.blogwoman.de.git
synced 2026-03-17 13:53:59 +00:00
Add documentation and BlogWoman frontend development prompt
- Add API documentation (API_ANLEITUNG.md) - Add architecture docs (UNIVERSAL_FEATURES.md, Analytics.md) - Add guides (FRONTEND.md, SEO_ERWEITERUNG.md, styleguide.md) - Add Planungs-KI prompt template (ANLEITUNG-PLANUNGS-KI-FRONTEND.md) - Add BlogWoman frontend development prompt with: - Tenant-ID 9 configuration - Design system based on styleguide - BlogWoman-specific blocks (Favorites, Series, VideoEmbed) - API patterns with tenant isolation - SEO and Analytics integration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4311a1960d
commit
dcfc48f5ce
10 changed files with 21510 additions and 0 deletions
1289
docs/api/API_ANLEITUNG.md
Normal file
1289
docs/api/API_ANLEITUNG.md
Normal file
File diff suppressed because it is too large
Load diff
981
docs/architecture/Analytics.md
Normal file
981
docs/architecture/Analytics.md
Normal file
|
|
@ -0,0 +1,981 @@
|
|||
# ANALYTICS-LÖSUNG: Implementierungsübersicht für Payload CMS
|
||||
|
||||
*Letzte Aktualisierung: 18. Dezember 2025*
|
||||
|
||||
## Kontext
|
||||
|
||||
Du entwickelst das Multi-Tenant Payload CMS Backend und Next.js Frontend für 4 Websites. Diese Dokumentation beschreibt die Analytics-Lösung, die in das Frontend integriert werden muss.
|
||||
|
||||
**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de) und **Production-Analytics** (analytics.c2sgmbh.de).
|
||||
|
||||
---
|
||||
|
||||
## Architektur-Übersicht
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ANALYTICS ARCHITEKTUR │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ OHNE CONSENT (immer aktiv) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ UMAMI ANALYTICS │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ Server: sv-analytics (10.10.181.103:3000) │ │ │
|
||||
│ │ │ Dashboard: http://10.10.181.103:3000 │ │ │
|
||||
│ │ │ Script: /custom.js (Anti-Adblock) │ │ │
|
||||
│ │ │ Endpoint: /api/send │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ Features: │ │ │
|
||||
│ │ │ • Cookieless Tracking (DSGVO-konform ohne Einwilligung) │ │ │
|
||||
│ │ │ • Pageviews, Sessions, Referrer, UTM-Parameter │ │ │
|
||||
│ │ │ • Custom Events (Newsletter, Formulare, CTAs, Downloads) │ │ │
|
||||
│ │ │ • 100% Erfassung aller Besucher │ │ │
|
||||
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ MIT CONSENT (Kategorie: "marketing") │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ GOOGLE ADS CONVERSION │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ Client-Side (bei Consent): │ │ │
|
||||
│ │ │ • Google Ads Tag (gtag.js) │ │ │
|
||||
│ │ │ • Conversion Tracking │ │ │
|
||||
│ │ │ • Remarketing Audiences │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ Server-Side (immer, anonymisiert): │ │ │
|
||||
│ │ │ • Google Ads Conversion API │ │ │
|
||||
│ │ │ • Enhanced Conversions (gehashte E-Mail) │ │ │
|
||||
│ │ │ • GCLID-basierte Attribution │ │ │
|
||||
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ GOOGLE CONSENT MODE v2 │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ Integration mit bestehendem Orestbida Consent-Banner │ │ │
|
||||
│ │ │ Kategorie "marketing" steuert: │ │ │
|
||||
│ │ │ • ad_storage │ │ │
|
||||
│ │ │ • ad_user_data │ │ │
|
||||
│ │ │ • ad_personalization │ │ │
|
||||
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Infrastruktur
|
||||
|
||||
### Umami Server
|
||||
|
||||
| Umgebung | Server | URL | Dashboard |
|
||||
|----------|--------|-----|-----------|
|
||||
| **Production** | Hetzner 3 | https://analytics.c2sgmbh.de | https://analytics.c2sgmbh.de |
|
||||
| **Development** | sv-analytics (LXC 703) | https://umami.porwoll.tech | https://umami.porwoll.tech |
|
||||
|
||||
| Eigenschaft | Production | Development |
|
||||
|-------------|------------|-------------|
|
||||
| **Tracking Script** | https://analytics.c2sgmbh.de/script.js | https://umami.porwoll.tech/script.js |
|
||||
| **API Endpoint** | https://analytics.c2sgmbh.de/api/send | https://umami.porwoll.tech/api/send |
|
||||
| **Datenbank** | umami_db auf Hetzner 3 | umami_db auf sv-postgres |
|
||||
|
||||
### Website-IDs (Multi-Tenant)
|
||||
|
||||
Die Website-IDs werden in Umami für jeden Tenant erstellt:
|
||||
|
||||
```typescript
|
||||
// src/config/analytics.ts
|
||||
|
||||
export const UMAMI_WEBSITE_IDS: Record<string, string> = {
|
||||
'porwoll.de': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'complexcaresolutions.de': 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy',
|
||||
'gunshin.de': 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz',
|
||||
'zweitmeinu.ng': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
}
|
||||
|
||||
// Production URL für Frontends
|
||||
export const UMAMI_HOST = process.env.NEXT_PUBLIC_UMAMI_HOST || 'https://analytics.c2sgmbh.de'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend-Integration
|
||||
|
||||
### 1. Umami Script Komponente
|
||||
|
||||
```typescript
|
||||
// src/components/analytics/UmamiScript.tsx
|
||||
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
interface UmamiScriptProps {
|
||||
websiteId: string
|
||||
host?: string
|
||||
}
|
||||
|
||||
export function UmamiScript({
|
||||
websiteId,
|
||||
host = 'https://analytics.c2sgmbh.de' // Production default
|
||||
}: UmamiScriptProps) {
|
||||
if (!websiteId) return null
|
||||
|
||||
return (
|
||||
<Script
|
||||
defer
|
||||
src={`${host}/script.js`}
|
||||
data-website-id={websiteId}
|
||||
data-host-url={host}
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Layout Integration (Multi-Tenant)
|
||||
|
||||
```typescript
|
||||
// src/app/layout.tsx
|
||||
|
||||
import { UmamiScript } from '@/components/analytics/UmamiScript'
|
||||
import { UMAMI_WEBSITE_IDS, UMAMI_HOST } from '@/config/analytics'
|
||||
import { getCurrentTenant } from '@/lib/tenant'
|
||||
|
||||
export default async function RootLayout({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const tenant = await getCurrentTenant()
|
||||
const umamiWebsiteId = UMAMI_WEBSITE_IDS[tenant.domain]
|
||||
|
||||
return (
|
||||
<html lang="de">
|
||||
<body>
|
||||
{children}
|
||||
|
||||
{/* Umami Analytics - Läuft OHNE Consent (cookieless) */}
|
||||
{umamiWebsiteId && (
|
||||
<UmamiScript
|
||||
websiteId={umamiWebsiteId}
|
||||
host={UMAMI_HOST}
|
||||
/>
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Analytics Hook für Custom Events
|
||||
|
||||
```typescript
|
||||
// src/hooks/useAnalytics.ts
|
||||
|
||||
'use client'
|
||||
|
||||
import { useCallback } from 'react'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
umami?: {
|
||||
track: (eventName: string, eventData?: Record<string, unknown>) => void
|
||||
}
|
||||
gtag?: (...args: unknown[]) => void
|
||||
CookieConsent?: {
|
||||
acceptedCategory: (category: string) => boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useAnalytics() {
|
||||
/**
|
||||
* Generisches Event-Tracking
|
||||
*/
|
||||
const trackEvent = useCallback((
|
||||
eventName: string,
|
||||
eventData?: Record<string, unknown>
|
||||
) => {
|
||||
if (typeof window !== 'undefined' && window.umami) {
|
||||
window.umami.track(eventName, eventData)
|
||||
}
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Newsletter Anmeldung
|
||||
*/
|
||||
const trackNewsletterSubscribe = useCallback((source: string = 'unknown') => {
|
||||
trackEvent('newsletter_subscribe', { source })
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Newsletter Bestätigung (Double Opt-In)
|
||||
*/
|
||||
const trackNewsletterConfirm = useCallback(() => {
|
||||
trackEvent('newsletter_confirm')
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Kontaktformular abgesendet
|
||||
*/
|
||||
const trackContactFormSubmit = useCallback((formType: string = 'contact') => {
|
||||
trackEvent('contact_form_submit', { form_type: formType })
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* CTA Klick
|
||||
*/
|
||||
const trackCtaClick = useCallback((
|
||||
ctaName: string,
|
||||
ctaLocation: string = 'unknown'
|
||||
) => {
|
||||
trackEvent('cta_click', { cta_name: ctaName, location: ctaLocation })
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Download
|
||||
*/
|
||||
const trackDownload = useCallback((
|
||||
fileName: string,
|
||||
fileType: string = 'unknown'
|
||||
) => {
|
||||
trackEvent('download', { file_name: fileName, file_type: fileType })
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Funnel-Step (für Conversion-Funnels)
|
||||
*/
|
||||
const trackFunnelStep = useCallback((
|
||||
funnelName: string,
|
||||
stepNumber: number,
|
||||
stepName: string
|
||||
) => {
|
||||
trackEvent('funnel_step', {
|
||||
funnel: funnelName,
|
||||
step: stepNumber,
|
||||
step_name: stepName
|
||||
})
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Scroll-Tiefe
|
||||
*/
|
||||
const trackScrollDepth = useCallback((depth: number) => {
|
||||
trackEvent('scroll_depth', { depth_percent: depth })
|
||||
}, [trackEvent])
|
||||
|
||||
/**
|
||||
* Externer Link Klick
|
||||
*/
|
||||
const trackExternalLink = useCallback((url: string) => {
|
||||
trackEvent('external_link', { url })
|
||||
}, [trackEvent])
|
||||
|
||||
return {
|
||||
trackEvent,
|
||||
trackNewsletterSubscribe,
|
||||
trackNewsletterConfirm,
|
||||
trackContactFormSubmit,
|
||||
trackCtaClick,
|
||||
trackDownload,
|
||||
trackFunnelStep,
|
||||
trackScrollDepth,
|
||||
trackExternalLink,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Server-Side Event Tracking
|
||||
|
||||
```typescript
|
||||
// src/lib/analytics.server.ts
|
||||
|
||||
const UMAMI_HOST = process.env.UMAMI_HOST || 'https://analytics.c2sgmbh.de'
|
||||
const UMAMI_WEBSITE_ID = process.env.UMAMI_WEBSITE_ID
|
||||
|
||||
interface ServerEventParams {
|
||||
event: string
|
||||
url: string
|
||||
websiteId?: string
|
||||
referrer?: string
|
||||
data?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-Side Event an Umami senden
|
||||
* Für Events die im Backend passieren (z.B. Newsletter-Bestätigung)
|
||||
*/
|
||||
export async function trackServerEvent(params: ServerEventParams) {
|
||||
const websiteId = params.websiteId || UMAMI_WEBSITE_ID
|
||||
|
||||
if (!websiteId) {
|
||||
console.warn('[Analytics] No websiteId configured for server-side tracking')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${UMAMI_HOST}/api/send`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'Payload-CMS-Server/1.0',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'event',
|
||||
payload: {
|
||||
website: websiteId,
|
||||
url: params.url,
|
||||
referrer: params.referrer || '',
|
||||
name: params.event,
|
||||
data: params.data,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('[Analytics] Server event failed:', response.status)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Analytics] Server event error:', error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Komponenten-Beispiele
|
||||
|
||||
### Newsletter-Formular mit Tracking
|
||||
|
||||
```typescript
|
||||
// src/components/forms/NewsletterForm.tsx
|
||||
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useAnalytics } from '@/hooks/useAnalytics'
|
||||
|
||||
interface NewsletterFormProps {
|
||||
source?: string // z.B. 'footer', 'hero', 'popup'
|
||||
}
|
||||
|
||||
export function NewsletterForm({ source = 'unknown' }: NewsletterFormProps) {
|
||||
const [email, setEmail] = useState('')
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
|
||||
const { trackNewsletterSubscribe, trackFunnelStep } = useAnalytics()
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault()
|
||||
setStatus('loading')
|
||||
|
||||
// Funnel-Step tracken
|
||||
trackFunnelStep('newsletter', 1, 'form_submitted')
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/newsletter/subscribe', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, source }),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
setStatus('success')
|
||||
// Erfolg tracken
|
||||
trackNewsletterSubscribe(source)
|
||||
trackFunnelStep('newsletter', 2, 'subscription_pending')
|
||||
} else {
|
||||
setStatus('error')
|
||||
}
|
||||
} catch {
|
||||
setStatus('error')
|
||||
}
|
||||
}
|
||||
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<div className="p-4 bg-green-50 text-green-800 rounded">
|
||||
Bitte bestätige deine E-Mail-Adresse.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="flex gap-2">
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="E-Mail-Adresse"
|
||||
required
|
||||
className="flex-1 px-4 py-2 border rounded"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === 'loading'}
|
||||
className="px-6 py-2 bg-primary text-white rounded"
|
||||
>
|
||||
{status === 'loading' ? 'Lädt...' : 'Anmelden'}
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### CTA Button mit Tracking
|
||||
|
||||
```typescript
|
||||
// src/components/ui/TrackedButton.tsx
|
||||
|
||||
'use client'
|
||||
|
||||
import { useAnalytics } from '@/hooks/useAnalytics'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface TrackedButtonProps {
|
||||
href: string
|
||||
ctaName: string
|
||||
location?: string
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
external?: boolean
|
||||
}
|
||||
|
||||
export function TrackedButton({
|
||||
href,
|
||||
ctaName,
|
||||
location = 'unknown',
|
||||
children,
|
||||
className,
|
||||
external = false,
|
||||
}: TrackedButtonProps) {
|
||||
const { trackCtaClick, trackExternalLink } = useAnalytics()
|
||||
|
||||
function handleClick() {
|
||||
trackCtaClick(ctaName, location)
|
||||
if (external) {
|
||||
trackExternalLink(href)
|
||||
}
|
||||
}
|
||||
|
||||
if (external) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={handleClick}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Link href={href} onClick={handleClick} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Download-Link mit Tracking
|
||||
|
||||
```typescript
|
||||
// src/components/ui/TrackedDownload.tsx
|
||||
|
||||
'use client'
|
||||
|
||||
import { useAnalytics } from '@/hooks/useAnalytics'
|
||||
|
||||
interface TrackedDownloadProps {
|
||||
href: string
|
||||
fileName: string
|
||||
fileType?: string
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function TrackedDownload({
|
||||
href,
|
||||
fileName,
|
||||
fileType = 'document',
|
||||
children,
|
||||
className,
|
||||
}: TrackedDownloadProps) {
|
||||
const { trackDownload } = useAnalytics()
|
||||
|
||||
function handleClick() {
|
||||
trackDownload(fileName, fileType)
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
download
|
||||
onClick={handleClick}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Google Ads Integration
|
||||
|
||||
### Consent Mode v2 Komponente
|
||||
|
||||
```typescript
|
||||
// src/components/analytics/GoogleConsentMode.tsx
|
||||
|
||||
'use client'
|
||||
|
||||
import Script from 'next/script'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
interface GoogleConsentModeProps {
|
||||
googleAdsId: string
|
||||
}
|
||||
|
||||
export function GoogleConsentMode({ googleAdsId }: GoogleConsentModeProps) {
|
||||
useEffect(() => {
|
||||
// Consent-Änderungen von Orestbida abonnieren
|
||||
window.addEventListener('cc:onConsent', handleConsentChange)
|
||||
window.addEventListener('cc:onChange', handleConsentChange)
|
||||
|
||||
// Initial setzen
|
||||
updateGoogleConsent()
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('cc:onConsent', handleConsentChange)
|
||||
window.removeEventListener('cc:onChange', handleConsentChange)
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleConsentChange() {
|
||||
updateGoogleConsent()
|
||||
}
|
||||
|
||||
function updateGoogleConsent() {
|
||||
if (typeof window.gtag !== 'function') return
|
||||
|
||||
const cc = window.CookieConsent
|
||||
if (!cc) return
|
||||
|
||||
const hasMarketing = cc.acceptedCategory('marketing')
|
||||
|
||||
window.gtag('consent', 'update', {
|
||||
'ad_storage': hasMarketing ? 'granted' : 'denied',
|
||||
'ad_user_data': hasMarketing ? 'granted' : 'denied',
|
||||
'ad_personalization': hasMarketing ? 'granted' : 'denied',
|
||||
'analytics_storage': 'denied', // Wir nutzen Umami
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Consent Default (vor gtag.js) */}
|
||||
<Script
|
||||
id="google-consent-default"
|
||||
strategy="beforeInteractive"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
|
||||
gtag('consent', 'default', {
|
||||
'ad_storage': 'denied',
|
||||
'ad_user_data': 'denied',
|
||||
'ad_personalization': 'denied',
|
||||
'analytics_storage': 'denied',
|
||||
'wait_for_update': 500
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Google Ads Tag */}
|
||||
<Script
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${googleAdsId}`}
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
|
||||
<Script
|
||||
id="google-ads-config"
|
||||
strategy="afterInteractive"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${googleAdsId}', {
|
||||
'allow_enhanced_conversions': true
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### GCLID Hook (für Conversion Attribution)
|
||||
|
||||
```typescript
|
||||
// src/hooks/useGclid.ts
|
||||
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
const GCLID_STORAGE_KEY = 'gclid'
|
||||
const GCLID_EXPIRY_DAYS = 90
|
||||
|
||||
/**
|
||||
* GCLID aus URL erfassen und speichern
|
||||
*/
|
||||
export function useGclid() {
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
useEffect(() => {
|
||||
const gclid = searchParams.get('gclid')
|
||||
|
||||
if (gclid) {
|
||||
const data = {
|
||||
value: gclid,
|
||||
expires: Date.now() + (GCLID_EXPIRY_DAYS * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
localStorage.setItem(GCLID_STORAGE_KEY, JSON.stringify(data))
|
||||
}
|
||||
}, [searchParams])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gespeicherte GCLID abrufen
|
||||
*/
|
||||
export function getStoredGclid(): string | null {
|
||||
if (typeof window === 'undefined') return null
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(GCLID_STORAGE_KEY)
|
||||
if (!stored) return null
|
||||
|
||||
const data = JSON.parse(stored)
|
||||
|
||||
if (Date.now() > data.expires) {
|
||||
localStorage.removeItem(GCLID_STORAGE_KEY)
|
||||
return null
|
||||
}
|
||||
|
||||
return data.value
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Client-Side Conversion Tracking
|
||||
|
||||
```typescript
|
||||
// src/lib/google-ads.ts
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag: (...args: unknown[]) => void
|
||||
CookieConsent: {
|
||||
acceptedCategory: (category: string) => boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ConversionParams {
|
||||
conversionId: string
|
||||
value?: number
|
||||
currency?: string
|
||||
transactionId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-Side Conversion (nur bei Marketing-Consent)
|
||||
*/
|
||||
export function trackConversion(params: ConversionParams) {
|
||||
if (typeof window === 'undefined') return
|
||||
if (typeof window.gtag !== 'function') return
|
||||
|
||||
const hasConsent = window.CookieConsent?.acceptedCategory('marketing')
|
||||
if (!hasConsent) return
|
||||
|
||||
window.gtag('event', 'conversion', {
|
||||
'send_to': params.conversionId,
|
||||
'value': params.value || 1.0,
|
||||
'currency': params.currency || 'EUR',
|
||||
'transaction_id': params.transactionId,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced Conversion Data setzen
|
||||
*/
|
||||
export function setEnhancedConversionData(data: {
|
||||
email?: string
|
||||
phone?: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
}) {
|
||||
if (typeof window === 'undefined') return
|
||||
if (typeof window.gtag !== 'function') return
|
||||
|
||||
const hasConsent = window.CookieConsent?.acceptedCategory('marketing')
|
||||
if (!hasConsent) return
|
||||
|
||||
window.gtag('set', 'user_data', {
|
||||
'email': data.email,
|
||||
'phone_number': data.phone,
|
||||
'address': {
|
||||
'first_name': data.firstName,
|
||||
'last_name': data.lastName,
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Server-Side Conversion API
|
||||
|
||||
```typescript
|
||||
// src/lib/google-ads.server.ts
|
||||
|
||||
import crypto from 'crypto'
|
||||
|
||||
const GOOGLE_ADS_CUSTOMER_ID = process.env.GOOGLE_ADS_CUSTOMER_ID
|
||||
const GOOGLE_ADS_CONVERSION_ACTION_ID = process.env.GOOGLE_ADS_CONVERSION_ACTION_ID
|
||||
const GOOGLE_ADS_API_TOKEN = process.env.GOOGLE_ADS_API_TOKEN
|
||||
const GOOGLE_ADS_DEVELOPER_TOKEN = process.env.GOOGLE_ADS_DEVELOPER_TOKEN
|
||||
|
||||
interface ServerConversionParams {
|
||||
conversionAction: string
|
||||
email?: string
|
||||
phone?: string
|
||||
value?: number
|
||||
currency?: string
|
||||
gclid?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-Side Conversion Upload
|
||||
* DSGVO-konform: Nur gehashte Daten
|
||||
*/
|
||||
export async function trackServerConversion(params: ServerConversionParams) {
|
||||
if (!GOOGLE_ADS_CUSTOMER_ID || !GOOGLE_ADS_API_TOKEN) {
|
||||
console.log('[Google Ads] Server-Side nicht konfiguriert')
|
||||
return
|
||||
}
|
||||
|
||||
const conversion: Record<string, unknown> = {
|
||||
conversionAction: `customers/${GOOGLE_ADS_CUSTOMER_ID}/conversionActions/${GOOGLE_ADS_CONVERSION_ACTION_ID}`,
|
||||
conversionDateTime: new Date().toISOString().replace('Z', '+00:00'),
|
||||
conversionValue: params.value || 1.0,
|
||||
currencyCode: params.currency || 'EUR',
|
||||
}
|
||||
|
||||
if (params.gclid) {
|
||||
conversion.gclid = params.gclid
|
||||
}
|
||||
|
||||
if (params.email || params.phone) {
|
||||
conversion.userIdentifiers = []
|
||||
|
||||
if (params.email) {
|
||||
(conversion.userIdentifiers as Array<unknown>).push({
|
||||
hashedEmail: hashForGoogle(params.email.toLowerCase().trim())
|
||||
})
|
||||
}
|
||||
|
||||
if (params.phone) {
|
||||
(conversion.userIdentifiers as Array<unknown>).push({
|
||||
hashedPhoneNumber: hashForGoogle(params.phone.replace(/[^0-9+]/g, ''))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://googleads.googleapis.com/v15/customers/${GOOGLE_ADS_CUSTOMER_ID}:uploadConversionAdjustments`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${GOOGLE_ADS_API_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
'developer-token': GOOGLE_ADS_DEVELOPER_TOKEN!,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
conversions: [conversion],
|
||||
partialFailure: true,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('[Google Ads] Upload failed:', await response.text())
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Google Ads] Error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
function hashForGoogle(value: string): string {
|
||||
return crypto.createHash('sha256').update(value).digest('hex')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CookieInventory Erweiterung
|
||||
|
||||
Füge diese Cookies zur `cookie-inventory` Collection hinzu:
|
||||
|
||||
```typescript
|
||||
// Über Payload Admin oder Seed-Script
|
||||
|
||||
const googleAdsCookies = [
|
||||
{
|
||||
name: '_gcl_au',
|
||||
provider: 'Google Ads',
|
||||
category: 'marketing',
|
||||
purpose: 'Conversion-Tracking und Anzeigenmessung',
|
||||
duration: '90 Tage',
|
||||
type: 'first-party',
|
||||
},
|
||||
{
|
||||
name: '_gcl_aw',
|
||||
provider: 'Google Ads',
|
||||
category: 'marketing',
|
||||
purpose: 'Speichert GCLID für Conversion-Attribution',
|
||||
duration: '90 Tage',
|
||||
type: 'first-party',
|
||||
},
|
||||
{
|
||||
name: 'IDE',
|
||||
provider: 'Google (doubleclick.net)',
|
||||
category: 'marketing',
|
||||
purpose: 'Remarketing und Anzeigenpersonalisierung',
|
||||
duration: '1 Jahr',
|
||||
type: 'third-party',
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Frontend (.env.local)
|
||||
|
||||
```env
|
||||
# Payload CMS API (Production)
|
||||
NEXT_PUBLIC_PAYLOAD_URL=https://cms.c2sgmbh.de
|
||||
NEXT_PUBLIC_API_URL=https://cms.c2sgmbh.de/api
|
||||
|
||||
# Umami Analytics (Production)
|
||||
NEXT_PUBLIC_UMAMI_HOST=https://analytics.c2sgmbh.de
|
||||
|
||||
# Tenant-Konfiguration (je nach Projekt)
|
||||
NEXT_PUBLIC_TENANT_ID=4
|
||||
NEXT_PUBLIC_TENANT_SLUG=c2s
|
||||
|
||||
# Google Ads (pro Tenant unterschiedlich)
|
||||
NEXT_PUBLIC_GOOGLE_ADS_ID=AW-XXXXXXXXX
|
||||
```
|
||||
|
||||
### Backend (.env)
|
||||
|
||||
```env
|
||||
# Umami Server-Side Tracking
|
||||
UMAMI_HOST=https://analytics.c2sgmbh.de
|
||||
UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
||||
# Google Ads Server-Side API
|
||||
GOOGLE_ADS_CUSTOMER_ID=1234567890
|
||||
GOOGLE_ADS_CONVERSION_ACTION_ID=987654321
|
||||
GOOGLE_ADS_API_TOKEN=ya29.xxx
|
||||
GOOGLE_ADS_DEVELOPER_TOKEN=xxx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event-Naming-Konvention
|
||||
|
||||
| Event | Name | Data |
|
||||
|-------|------|------|
|
||||
| Newsletter Anmeldung | `newsletter_subscribe` | `{ source: string }` |
|
||||
| Newsletter Bestätigung | `newsletter_confirm` | - |
|
||||
| Kontaktformular | `contact_form_submit` | `{ form_type: string }` |
|
||||
| CTA Klick | `cta_click` | `{ cta_name: string, location: string }` |
|
||||
| Download | `download` | `{ file_name: string, file_type: string }` |
|
||||
| Funnel-Step | `funnel_step` | `{ funnel: string, step: number, step_name: string }` |
|
||||
| Scroll-Tiefe | `scroll_depth` | `{ depth_percent: number }` |
|
||||
| Externer Link | `external_link` | `{ url: string }` |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ WAS IMPLEMENTIERT WERDEN MUSS │
|
||||
│ │
|
||||
│ 1. UMAMI (ohne Consent) │
|
||||
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||||
│ • UmamiScript Komponente in Layout einbinden │
|
||||
│ • useAnalytics Hook in Formulare/CTAs integrieren │
|
||||
│ • Server-Side Events für Backend-Actions │
|
||||
│ │
|
||||
│ 2. GOOGLE ADS (mit Consent) │
|
||||
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||||
│ • GoogleConsentMode Komponente (integriert mit Orestbida) │
|
||||
│ • useGclid Hook für Attribution │
|
||||
│ • Client-Side Conversions bei Consent │
|
||||
│ • Server-Side Conversions immer (gehashte Daten) │
|
||||
│ │
|
||||
│ 3. COOKIE INVENTORY │
|
||||
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||||
│ • Google Ads Cookies dokumentieren │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dateien zu erstellen
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ ├── analytics/
|
||||
│ │ ├── UmamiScript.tsx # Umami Tracking Script
|
||||
│ │ └── GoogleConsentMode.tsx # Google Consent Mode v2
|
||||
│ ├── forms/
|
||||
│ │ └── NewsletterForm.tsx # Mit Analytics-Integration
|
||||
│ └── ui/
|
||||
│ ├── TrackedButton.tsx # CTA mit Tracking
|
||||
│ └── TrackedDownload.tsx # Download mit Tracking
|
||||
├── config/
|
||||
│ └── analytics.ts # Website-IDs, Config
|
||||
├── hooks/
|
||||
│ ├── useAnalytics.ts # Client-Side Event Tracking
|
||||
│ └── useGclid.ts # GCLID Erfassung
|
||||
└── lib/
|
||||
├── analytics.server.ts # Umami Server-Side
|
||||
├── google-ads.ts # Google Ads Client
|
||||
└── google-ads.server.ts # Google Ads Server API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Letzte Aktualisierung: 18. Dezember 2025*
|
||||
758
docs/architecture/UNIVERSAL_FEATURES.md
Normal file
758
docs/architecture/UNIVERSAL_FEATURES.md
Normal file
|
|
@ -0,0 +1,758 @@
|
|||
# Universal Features - Dokumentation
|
||||
|
||||
*Letzte Aktualisierung: 08. Januar 2026*
|
||||
|
||||
## Übersicht
|
||||
|
||||
Die Universal Features erweitern das Payload CMS um wiederverwendbare Collections und Blocks für Blog, News, Testimonials, Newsletter, FAQ, Team und Prozess-Darstellungen. Alle Features sind Multi-Tenant-fähig.
|
||||
|
||||
**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de).
|
||||
|
||||
---
|
||||
|
||||
## API-Endpoints für Frontend
|
||||
|
||||
### Collections
|
||||
|
||||
| Collection | Endpoint | Beschreibung |
|
||||
|------------|----------|--------------|
|
||||
| Posts | `GET /api/posts` | Blog, News, Presse |
|
||||
| Testimonials | `GET /api/testimonials` | Kundenbewertungen |
|
||||
| FAQs | `GET /api/faqs` | FAQ-Einträge |
|
||||
| Team | `GET /api/team` | Team-Mitglieder |
|
||||
| Services | `GET /api/services` | Leistungen |
|
||||
| Timelines | `GET /api/timelines` | Chronologische Events |
|
||||
| Workflows | `GET /api/workflows` | Prozess-Darstellungen |
|
||||
|
||||
### Beispiel: Posts laden
|
||||
|
||||
```typescript
|
||||
// Featured Blog-Posts für Tenant C2S laden
|
||||
const posts = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/posts?where[tenant][equals]=4&where[type][equals]=blog&where[isFeatured][equals]=true&locale=de&limit=6'
|
||||
).then(r => r.json())
|
||||
```
|
||||
|
||||
### Newsletter API
|
||||
|
||||
| Endpoint | Methode | Beschreibung |
|
||||
|----------|---------|--------------|
|
||||
| `/api/newsletter/subscribe` | POST | Newsletter-Anmeldung |
|
||||
| `/api/newsletter/confirm` | GET/POST | Double Opt-In Bestätigung |
|
||||
| `/api/newsletter/unsubscribe` | GET/POST | Abmeldung |
|
||||
|
||||
```typescript
|
||||
// Newsletter-Anmeldung
|
||||
const response = await fetch('https://cms.c2sgmbh.de/api/newsletter/subscribe', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: 'user@example.com',
|
||||
firstName: 'Max',
|
||||
tenantId: 4,
|
||||
source: 'footer'
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collections
|
||||
|
||||
### 1. Posts Collection
|
||||
|
||||
**Pfad:** `src/collections/Posts.ts`
|
||||
**Slug:** `posts`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Die Posts Collection dient zur Verwaltung von Blog-Artikeln, News, Pressemitteilungen und Ankündigungen.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `title` | Text | Titel des Beitrags | Ja |
|
||||
| `slug` | Text | URL-Pfad (unique) | Ja |
|
||||
| `type` | Select | Art des Beitrags | Ja |
|
||||
| `isFeatured` | Checkbox | Hervorgehobener Beitrag | Nein |
|
||||
| `excerpt` | Textarea | Kurzfassung (max. 300 Zeichen) | Nein |
|
||||
| `featuredImage` | Upload | Beitragsbild | Nein |
|
||||
| `content` | RichText | Hauptinhalt | Ja |
|
||||
| `categories` | Relationship | Kategorien (hasMany) | Nein |
|
||||
| `author` | Text | Autorname | Nein |
|
||||
| `status` | Select | draft/published/archived | Nein |
|
||||
| `publishedAt` | Date | Veröffentlichungsdatum | Nein |
|
||||
| `meta` | Group | SEO-Einstellungen | Nein |
|
||||
|
||||
#### Post-Typen
|
||||
|
||||
- `blog` - Blog-Artikel (Standard)
|
||||
- `news` - News/Aktuelles
|
||||
- `press` - Pressemitteilung
|
||||
- `announcement` - Ankündigung
|
||||
|
||||
---
|
||||
|
||||
### 2. Testimonials Collection
|
||||
|
||||
**Pfad:** `src/collections/Testimonials.ts`
|
||||
**Slug:** `testimonials`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Kundenstimmen und Bewertungen für Referenz-Seiten.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `quote` | Textarea | Zitat/Bewertungstext | Ja |
|
||||
| `author` | Text | Name des Kunden | Ja |
|
||||
| `role` | Text | Position/Rolle | Nein |
|
||||
| `company` | Text | Unternehmen | Nein |
|
||||
| `image` | Upload | Portrait-Foto | Nein |
|
||||
| `rating` | Number | Bewertung 1-5 Sterne | Nein |
|
||||
| `source` | Text | Quelle (z.B. "Google Reviews") | Nein |
|
||||
| `sourceUrl` | Text | Link zur Original-Bewertung | Nein |
|
||||
| `date` | Date | Datum der Bewertung | Nein |
|
||||
| `isActive` | Checkbox | Sichtbarkeit | Nein |
|
||||
| `order` | Number | Sortierung | Nein |
|
||||
|
||||
---
|
||||
|
||||
### 3. Newsletter Subscribers Collection
|
||||
|
||||
**Pfad:** `src/collections/NewsletterSubscribers.ts`
|
||||
**Slug:** `newsletter-subscribers`
|
||||
**Admin-Gruppe:** Marketing
|
||||
|
||||
DSGVO-konforme Speicherung von Newsletter-Anmeldungen mit Double Opt-In.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `email` | Email | E-Mail-Adresse | Ja |
|
||||
| `firstName` | Text | Vorname | Nein |
|
||||
| `lastName` | Text | Nachname | Nein |
|
||||
| `status` | Select | Anmeldestatus | Ja |
|
||||
| `interests` | Select (hasMany) | Interessengebiete | Nein |
|
||||
| `source` | Text | Anmeldequelle | Nein |
|
||||
| `subscribedAt` | Date | Anmeldedatum (auto) | Nein |
|
||||
| `confirmedAt` | Date | Bestätigungsdatum (auto) | Nein |
|
||||
| `unsubscribedAt` | Date | Abmeldedatum (auto) | Nein |
|
||||
| `confirmationToken` | Text | Token für Double Opt-In (auto) | Nein |
|
||||
| `ipAddress` | Text | IP-Adresse (DSGVO) | Nein |
|
||||
| `userAgent` | Text | Browser-Info | Nein |
|
||||
|
||||
#### Double Opt-In Flow
|
||||
|
||||
1. User meldet sich an → Status: `pending`, Token generiert
|
||||
2. E-Mail mit Bestätigungs-Link wird automatisch gesendet
|
||||
3. User klickt Link → Status: `confirmed`
|
||||
4. Willkommens-E-Mail wird gesendet
|
||||
5. Abmeldung jederzeit über Link möglich
|
||||
|
||||
#### Status-Werte
|
||||
|
||||
- `pending` - Ausstehend (Double Opt-In)
|
||||
- `confirmed` - Bestätigt
|
||||
- `unsubscribed` - Abgemeldet
|
||||
- `bounced` - E-Mail nicht zustellbar
|
||||
|
||||
---
|
||||
|
||||
### 4. FAQs Collection
|
||||
|
||||
**Pfad:** `src/collections/FAQs.ts`
|
||||
**Slug:** `faqs`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Häufig gestellte Fragen mit Kategorisierung.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `question` | Text | Frage | Ja |
|
||||
| `answer` | RichText | Antwort | Ja |
|
||||
| `category` | Text | Kategorie | Nein |
|
||||
| `order` | Number | Sortierung | Nein |
|
||||
| `isActive` | Checkbox | Sichtbarkeit | Nein |
|
||||
|
||||
---
|
||||
|
||||
### 5. Team Collection
|
||||
|
||||
**Pfad:** `src/collections/Team.ts`
|
||||
**Slug:** `team`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Team-Mitglieder und Mitarbeiter.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `name` | Text | Name | Ja |
|
||||
| `role` | Text | Position/Rolle | Nein |
|
||||
| `image` | Upload | Portrait-Foto | Nein |
|
||||
| `bio` | Textarea | Kurzbiografie | Nein |
|
||||
| `email` | Email | E-Mail-Adresse | Nein |
|
||||
| `phone` | Text | Telefon | Nein |
|
||||
| `socialLinks` | Array | Social Media Links | Nein |
|
||||
| `order` | Number | Sortierung | Nein |
|
||||
| `isActive` | Checkbox | Sichtbarkeit | Nein |
|
||||
|
||||
---
|
||||
|
||||
### 6. Services Collection
|
||||
|
||||
**Pfad:** `src/collections/Services.ts`
|
||||
**Slug:** `services`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Leistungen und Dienstleistungen.
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `title` | Text | Leistungsname | Ja |
|
||||
| `slug` | Text | URL-Pfad | Ja |
|
||||
| `excerpt` | Textarea | Kurzbeschreibung | Nein |
|
||||
| `content` | RichText | Detailbeschreibung | Nein |
|
||||
| `icon` | Text | Icon-Name | Nein |
|
||||
| `image` | Upload | Bild | Nein |
|
||||
| `category` | Relationship | Service-Kategorie | Nein |
|
||||
| `order` | Number | Sortierung | Nein |
|
||||
|
||||
---
|
||||
|
||||
### 7. Timelines Collection
|
||||
|
||||
**Pfad:** `src/collections/Timelines.ts`
|
||||
**Slug:** `timelines`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Chronologische Events für Geschichte, Meilensteine, Releases.
|
||||
|
||||
#### API
|
||||
|
||||
```typescript
|
||||
// Timeline laden
|
||||
fetch('https://cms.c2sgmbh.de/api/timelines?tenant=4&slug=company-history&locale=de')
|
||||
```
|
||||
|
||||
#### Timeline-Typen
|
||||
|
||||
- `history` - Unternehmensgeschichte
|
||||
- `milestones` - Projektmeilensteine
|
||||
- `releases` - Produkt-Releases
|
||||
- `career` - Karriere/Lebenslauf
|
||||
- `events` - Ereignisse
|
||||
- `process` - Prozess/Ablauf
|
||||
|
||||
---
|
||||
|
||||
### 8. Workflows Collection
|
||||
|
||||
**Pfad:** `src/collections/Workflows.ts`
|
||||
**Slug:** `workflows`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Komplexe Prozesse mit Phasen, Abhängigkeiten und Status-Tracking.
|
||||
|
||||
#### API
|
||||
|
||||
```typescript
|
||||
// Workflow laden
|
||||
fetch('https://cms.c2sgmbh.de/api/workflows?tenant=4&type=project&locale=de')
|
||||
```
|
||||
|
||||
#### Workflow-Typen
|
||||
|
||||
- `project` - Projektabläufe
|
||||
- `business` - Geschäftsprozesse
|
||||
- `approval` - Genehmigungs-Workflows
|
||||
- `onboarding` - Mitarbeiter-/Kundeneinführung
|
||||
- `support` - Support/Service-Prozesse
|
||||
- `development` - Entwicklungsprozesse
|
||||
|
||||
---
|
||||
|
||||
### 9. Favorites Collection (BlogWoman)
|
||||
|
||||
**Pfad:** `src/collections/Favorites.ts`
|
||||
**Slug:** `favorites`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
Affiliate-Produkte für Blogger/Influencer mit Kategorien und Badges.
|
||||
|
||||
#### API
|
||||
|
||||
```typescript
|
||||
// Alle Favorites eines Tenants
|
||||
fetch('https://cms.c2sgmbh.de/api/favorites?where[tenant][equals]=4&locale=de')
|
||||
|
||||
// Nach Kategorie filtern
|
||||
fetch('https://cms.c2sgmbh.de/api/favorites?where[tenant][equals]=4&where[category][equals]=fashion')
|
||||
```
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `title` | Text | Produktname | Ja |
|
||||
| `slug` | Text | URL-Pfad (auto) | Ja |
|
||||
| `description` | RichText | Beschreibung (lokalisiert) | Nein |
|
||||
| `image` | Upload | Produktbild | Nein |
|
||||
| `affiliateUrl` | Text | Affiliate-Link | Ja |
|
||||
| `price` | Text | Preis (Freitext) | Nein |
|
||||
| `category` | Select | fashion, beauty, travel, tech, home | Nein |
|
||||
| `badge` | Select | investment-piece, daily-driver, grfi-approved, new, bestseller | Nein |
|
||||
| `priceRange` | Select | budget, mid, premium, luxury | Nein |
|
||||
| `isActive` | Checkbox | Sichtbarkeit | Nein |
|
||||
| `order` | Number | Sortierung | Nein |
|
||||
|
||||
---
|
||||
|
||||
### 10. Series Collection (BlogWoman)
|
||||
|
||||
**Pfad:** `src/collections/Series.ts`
|
||||
**Slug:** `series`
|
||||
**Admin-Gruppe:** Content
|
||||
|
||||
YouTube-Serien mit eigenem Branding für Content-Reihen.
|
||||
|
||||
#### API
|
||||
|
||||
```typescript
|
||||
// Alle Serien eines Tenants
|
||||
fetch('https://cms.c2sgmbh.de/api/series?where[tenant][equals]=4&locale=de')
|
||||
|
||||
// Einzelne Serie nach Slug
|
||||
fetch('https://cms.c2sgmbh.de/api/series?where[tenant][equals]=4&where[slug][equals]=grfi')
|
||||
```
|
||||
|
||||
#### Felder
|
||||
|
||||
| Feld | Typ | Beschreibung | Pflicht |
|
||||
|------|-----|--------------|---------|
|
||||
| `title` | Text | Serienname (lokalisiert) | Ja |
|
||||
| `slug` | Text | URL-Pfad (auto) | Ja |
|
||||
| `description` | RichText | Beschreibung (lokalisiert) | Nein |
|
||||
| `logo` | Upload | Serien-Logo | Nein |
|
||||
| `coverImage` | Upload | Cover-Bild für Hero | Nein |
|
||||
| `brandColor` | Text | Hex-Farbcode (#RRGGBB) | Nein |
|
||||
| `youtubePlaylistId` | Text | YouTube Playlist ID | Nein |
|
||||
| `isActive` | Checkbox | Sichtbarkeit | Nein |
|
||||
|
||||
---
|
||||
|
||||
## Blocks
|
||||
|
||||
Alle Blocks können in der Pages Collection verwendet werden.
|
||||
|
||||
### Block-Übersicht
|
||||
|
||||
#### Content Blocks
|
||||
|
||||
| Block | Slug | Beschreibung |
|
||||
|-------|------|--------------|
|
||||
| Posts List | `posts-list-block` | Blog/News-Liste |
|
||||
| Testimonials | `testimonials-block` | Kundenstimmen |
|
||||
| Newsletter | `newsletter-block` | Anmeldeformular |
|
||||
| Process Steps | `process-steps-block` | Prozess-Schritte |
|
||||
| Timeline | `timeline-block` | Chronologie |
|
||||
| FAQ | `faq-block` | FAQ-Akkordeon |
|
||||
| Team | `team-block` | Team-Mitglieder |
|
||||
| Services | `services-block` | Leistungen |
|
||||
|
||||
#### BlogWoman Blocks (NEU)
|
||||
|
||||
| Block | Slug | Beschreibung |
|
||||
|-------|------|--------------|
|
||||
| Favorites | `favorites-block` | Affiliate-Produkte Grid/Liste/Karussell |
|
||||
| Series | `series-block` | YouTube-Serien Übersicht |
|
||||
| Series Detail | `series-detail-block` | Serien-Einzelseite mit Hero |
|
||||
| Video Embed | `video-embed-block` | YouTube/Vimeo Embed mit Privacy Mode |
|
||||
| Featured Content | `featured-content-block` | Kuratierte Mixed-Content Sammlung |
|
||||
|
||||
### 1. Posts List Block
|
||||
|
||||
**Slug:** `posts-list-block`
|
||||
|
||||
Zeigt eine Liste von Blog-Artikeln, News oder anderen Post-Typen an.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Standard | Beschreibung |
|
||||
|--------|-----|----------|--------------|
|
||||
| `title` | Text | - | Überschrift |
|
||||
| `subtitle` | Text | - | Untertitel |
|
||||
| `postType` | Select | blog | Beitragstyp-Filter |
|
||||
| `layout` | Select | grid | Darstellung |
|
||||
| `columns` | Select | 3 | Spaltenanzahl (bei Grid) |
|
||||
| `limit` | Number | 6 | Anzahl Beiträge |
|
||||
| `showFeaturedOnly` | Checkbox | false | Nur hervorgehobene |
|
||||
| `filterByCategory` | Relationship | - | Kategorie-Filter |
|
||||
| `showExcerpt` | Checkbox | true | Kurzfassung anzeigen |
|
||||
| `showDate` | Checkbox | true | Datum anzeigen |
|
||||
| `showAuthor` | Checkbox | false | Autor anzeigen |
|
||||
| `showCategory` | Checkbox | true | Kategorie anzeigen |
|
||||
| `showPagination` | Checkbox | false | Pagination |
|
||||
| `showReadMore` | Checkbox | true | "Alle anzeigen" Link |
|
||||
| `backgroundColor` | Select | white | Hintergrundfarbe |
|
||||
|
||||
#### Layout-Optionen
|
||||
|
||||
- `grid` - Karten im Grid
|
||||
- `list` - Listenansicht
|
||||
- `featured` - Featured + Grid
|
||||
- `compact` - Kompakt (für Sidebar)
|
||||
- `masonry` - Masonry-Layout
|
||||
|
||||
---
|
||||
|
||||
### 2. Testimonials Block
|
||||
|
||||
**Slug:** `testimonials-block`
|
||||
|
||||
Zeigt Kundenstimmen aus der Testimonials Collection.
|
||||
|
||||
#### Layout-Optionen
|
||||
|
||||
- `slider` - Karussell
|
||||
- `grid` - Karten im Grid
|
||||
- `single` - Einzeln (Featured)
|
||||
- `masonry` - Masonry-Layout
|
||||
- `list` - Listenansicht
|
||||
|
||||
---
|
||||
|
||||
### 3. Newsletter Block
|
||||
|
||||
**Slug:** `newsletter-block`
|
||||
|
||||
Anmeldeformular für Newsletter mit DSGVO-Hinweis.
|
||||
|
||||
#### Layout-Optionen
|
||||
|
||||
- `inline` - Eingabe + Button nebeneinander
|
||||
- `stacked` - Untereinander
|
||||
- `with-image` - Mit Bild (50/50)
|
||||
- `minimal` - Nur Input
|
||||
- `card` - Karten-Design
|
||||
|
||||
---
|
||||
|
||||
### 4. FAQ Block
|
||||
|
||||
**Slug:** `faq-block`
|
||||
|
||||
FAQ-Akkordeon mit Schema.org Markup.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `title` | Text | Überschrift |
|
||||
| `subtitle` | Text | Untertitel |
|
||||
| `displayMode` | Select | all / selected / byCategory |
|
||||
| `selectedFaqs` | Relationship | Handverlesene FAQs |
|
||||
| `filterCategory` | Text | Kategorie-Filter |
|
||||
| `layout` | Select | accordion / list / grid |
|
||||
| `expandFirst` | Checkbox | Erste FAQ offen |
|
||||
| `showSchema` | Checkbox | JSON-LD generieren |
|
||||
|
||||
---
|
||||
|
||||
### 5. Team Block
|
||||
|
||||
**Slug:** `team-block`
|
||||
|
||||
Team-Mitglieder aus der Team Collection.
|
||||
|
||||
#### Layout-Optionen
|
||||
|
||||
- `grid` - Karten im Grid
|
||||
- `list` - Listenansicht
|
||||
- `carousel` - Karussell
|
||||
- `compact` - Kompakt
|
||||
|
||||
---
|
||||
|
||||
### 6. Services Block
|
||||
|
||||
**Slug:** `services-block`
|
||||
|
||||
Leistungen aus der Services Collection.
|
||||
|
||||
#### Layout-Optionen
|
||||
|
||||
- `grid` - Karten im Grid
|
||||
- `list` - Listenansicht mit Details
|
||||
- `icons` - Icon-Grid
|
||||
- `tabs` - Tab-Navigation
|
||||
|
||||
---
|
||||
|
||||
### 7. Favorites Block (BlogWoman)
|
||||
|
||||
**Slug:** `favorites-block`
|
||||
|
||||
Zeigt Affiliate-Produkte aus der Favorites Collection.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `title` | Text | Überschrift |
|
||||
| `subtitle` | Text | Untertitel |
|
||||
| `displayMode` | Select | all / selected / byCategory |
|
||||
| `selectedFavorites` | Relationship | Handverlesene Favorites |
|
||||
| `filterCategory` | Select | Kategorie-Filter |
|
||||
| `layout` | Select | grid / list / carousel |
|
||||
| `columns` | Select | 2 / 3 / 4 Spalten |
|
||||
| `limit` | Number | Anzahl Produkte |
|
||||
| `showPrice` | Checkbox | Preis anzeigen |
|
||||
| `showBadge` | Checkbox | Badge anzeigen |
|
||||
|
||||
---
|
||||
|
||||
### 8. Series Block (BlogWoman)
|
||||
|
||||
**Slug:** `series-block`
|
||||
|
||||
Übersicht aller YouTube-Serien mit Filteroptionen.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `title` | Text | Überschrift |
|
||||
| `subtitle` | Text | Untertitel |
|
||||
| `displayMode` | Select | all / selected |
|
||||
| `selectedSeries` | Relationship | Handverlesene Serien |
|
||||
| `layout` | Select | grid / list / featured |
|
||||
| `showDescription` | Checkbox | Beschreibung anzeigen |
|
||||
|
||||
---
|
||||
|
||||
### 9. Series Detail Block (BlogWoman)
|
||||
|
||||
**Slug:** `series-detail-block`
|
||||
|
||||
Hero-Bereich für eine einzelne Serie mit Branding.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `series` | Relationship | Serien-Referenz |
|
||||
| `layout` | Select | hero / compact / sidebar |
|
||||
| `showLogo` | Checkbox | Logo anzeigen |
|
||||
| `showPlaylistLink` | Checkbox | YouTube-Link anzeigen |
|
||||
| `useBrandColor` | Checkbox | Markenfarbe verwenden |
|
||||
|
||||
---
|
||||
|
||||
### 10. Video Embed Block (BlogWoman)
|
||||
|
||||
**Slug:** `video-embed-block`
|
||||
|
||||
Datenschutzfreundliches Video-Embedding für YouTube/Vimeo.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `videoUrl` | Text | YouTube/Vimeo URL |
|
||||
| `title` | Text | Video-Titel |
|
||||
| `aspectRatio` | Select | 16:9 / 4:3 / 1:1 / 9:16 |
|
||||
| `privacyMode` | Checkbox | youtube-nocookie.com verwenden |
|
||||
| `autoplay` | Checkbox | Automatisch abspielen |
|
||||
| `showControls` | Checkbox | Player-Controls anzeigen |
|
||||
| `thumbnailImage` | Upload | Custom Thumbnail |
|
||||
|
||||
---
|
||||
|
||||
### 11. Featured Content Block (BlogWoman)
|
||||
|
||||
**Slug:** `featured-content-block`
|
||||
|
||||
Kuratierte Sammlung aus verschiedenen Content-Typen.
|
||||
|
||||
#### Konfigurationsoptionen
|
||||
|
||||
| Option | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `title` | Text | Überschrift |
|
||||
| `subtitle` | Text | Untertitel |
|
||||
| `items` | Array | Gemischte Content-Items |
|
||||
| `layout` | Select | grid / masonry / featured |
|
||||
|
||||
**Item-Typen:**
|
||||
- `post` - Blog-Artikel
|
||||
- `video` - Video
|
||||
- `favorite` - Affiliate-Produkt
|
||||
- `series` - YouTube-Serie
|
||||
- `external` - Externer Link
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenant Konfiguration
|
||||
|
||||
Alle Collections sind für Multi-Tenant konfiguriert:
|
||||
|
||||
```typescript
|
||||
// payload.config.ts
|
||||
multiTenantPlugin({
|
||||
tenantsSlug: 'tenants',
|
||||
collections: {
|
||||
posts: {},
|
||||
testimonials: {},
|
||||
'newsletter-subscribers': {},
|
||||
faqs: {},
|
||||
team: {},
|
||||
services: {},
|
||||
timelines: {},
|
||||
workflows: {},
|
||||
favorites: {}, // BlogWoman
|
||||
series: {}, // BlogWoman
|
||||
// ... weitere Collections
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Tenant-Zuordnung
|
||||
|
||||
Jedes Dokument enthält ein `tenant`-Feld. Die Access-Control sorgt für Isolation:
|
||||
|
||||
- **Authentifizierte Admins:** Sehen alle Dokumente
|
||||
- **Anonyme Requests:** Nur Dokumente des passenden Tenants
|
||||
|
||||
### Tenant-IDs
|
||||
|
||||
| ID | Name | Slug |
|
||||
|----|------|------|
|
||||
| 1 | porwoll.de | porwoll |
|
||||
| 4 | Complex Care Solutions GmbH | c2s |
|
||||
| 5 | Gunshin | gunshin |
|
||||
|
||||
---
|
||||
|
||||
## Dateien-Übersicht
|
||||
|
||||
```
|
||||
src/
|
||||
├── collections/
|
||||
│ ├── Posts.ts
|
||||
│ ├── Categories.ts
|
||||
│ ├── Testimonials.ts
|
||||
│ ├── NewsletterSubscribers.ts
|
||||
│ ├── FAQs.ts
|
||||
│ ├── Team.ts
|
||||
│ ├── Services.ts
|
||||
│ ├── ServiceCategories.ts
|
||||
│ ├── Timelines.ts
|
||||
│ ├── Workflows.ts
|
||||
│ ├── Favorites.ts # BlogWoman
|
||||
│ └── Series.ts # BlogWoman
|
||||
├── blocks/
|
||||
│ ├── PostsListBlock.ts
|
||||
│ ├── TestimonialsBlock.ts
|
||||
│ ├── NewsletterBlock.ts
|
||||
│ ├── ProcessStepsBlock.ts
|
||||
│ ├── TimelineBlock.ts
|
||||
│ ├── FAQBlock.ts
|
||||
│ ├── TeamBlock.ts
|
||||
│ ├── ServicesBlock.ts
|
||||
│ ├── FavoritesBlock.ts # BlogWoman
|
||||
│ ├── SeriesBlock.ts # BlogWoman
|
||||
│ ├── SeriesDetailBlock.ts # BlogWoman
|
||||
│ ├── VideoEmbedBlock.ts # BlogWoman
|
||||
│ ├── FeaturedContentBlock.ts # BlogWoman
|
||||
│ └── index.ts
|
||||
├── lib/
|
||||
│ ├── tenantAccess.ts
|
||||
│ └── email/
|
||||
│ ├── newsletter-service.ts
|
||||
│ └── newsletter-templates.ts
|
||||
├── hooks/
|
||||
│ └── sendNewsletterConfirmation.ts
|
||||
└── app/(payload)/api/newsletter/
|
||||
├── subscribe/route.ts
|
||||
├── confirm/route.ts
|
||||
└── unsubscribe/route.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URLs
|
||||
|
||||
### Production (für Frontends)
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| Posts API | https://cms.c2sgmbh.de/api/posts |
|
||||
| Testimonials API | https://cms.c2sgmbh.de/api/testimonials |
|
||||
| FAQs API | https://cms.c2sgmbh.de/api/faqs |
|
||||
| Team API | https://cms.c2sgmbh.de/api/team |
|
||||
| Services API | https://cms.c2sgmbh.de/api/services |
|
||||
| Timelines API | https://cms.c2sgmbh.de/api/timelines |
|
||||
| Workflows API | https://cms.c2sgmbh.de/api/workflows |
|
||||
| Newsletter Subscribe | https://cms.c2sgmbh.de/api/newsletter/subscribe |
|
||||
|
||||
### Development
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| API Base | https://pl.porwoll.tech/api |
|
||||
| Admin Panel | https://pl.porwoll.tech/admin |
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.3 (08.01.2026)
|
||||
|
||||
- **BlogWoman Collections hinzugefügt:**
|
||||
- Favorites Collection (Affiliate-Produkte)
|
||||
- Series Collection (YouTube-Serien)
|
||||
- **BlogWoman Blocks hinzugefügt:**
|
||||
- FavoritesBlock (Grid/Liste/Karussell)
|
||||
- SeriesBlock (Serien-Übersicht)
|
||||
- SeriesDetailBlock (Serien-Hero)
|
||||
- VideoEmbedBlock (Privacy-Mode)
|
||||
- FeaturedContentBlock (Mixed-Content)
|
||||
- Dateien-Übersicht aktualisiert
|
||||
- Multi-Tenant Konfiguration erweitert
|
||||
|
||||
### Version 1.2 (18.12.2025)
|
||||
|
||||
- Dokumentation auf Production-URLs aktualisiert
|
||||
- API-Endpoints für Frontend hinzugefügt
|
||||
- Newsletter Double Opt-In Flow dokumentiert
|
||||
- Timelines und Workflows Collections hinzugefügt
|
||||
- FAQ, Team, Services Blocks dokumentiert
|
||||
|
||||
### Version 1.1 (14.12.2025)
|
||||
|
||||
- Timelines Collection für chronologische Darstellungen
|
||||
- Workflows Collection für Prozesse
|
||||
- FAQs Collection mit Kategorisierung
|
||||
- Team Collection mit Social Links
|
||||
- Services Collection mit Kategorien
|
||||
|
||||
### Version 1.0 (30.11.2025)
|
||||
|
||||
- Posts Collection um `type`, `isFeatured`, `excerpt` erweitert
|
||||
- Testimonials Collection erstellt
|
||||
- NewsletterSubscribers Collection erstellt (DSGVO-konform)
|
||||
- 5 neue Blocks für Pages implementiert
|
||||
- Multi-Tenant Integration für alle Collections
|
||||
|
||||
---
|
||||
|
||||
*Letzte Aktualisierung: 08. Januar 2026*
|
||||
12784
docs/components/payload-types.ts
Normal file
12784
docs/components/payload-types.ts
Normal file
File diff suppressed because it is too large
Load diff
526
docs/guides/FRONTEND.md
Normal file
526
docs/guides/FRONTEND.md
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
# Frontend-Entwicklung - Payload CMS Multi-Tenant
|
||||
|
||||
> **Server:** sv-frontend (LXC 704) - 10.10.181.104
|
||||
> **Backend API:** https://cms.c2sgmbh.de/api (Production)
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Frontend wird als separates Next.js-Projekt entwickelt und nutzt Payload CMS als Headless CMS über die REST-API.
|
||||
|
||||
**Wichtig:** Die Frontend-Entwicklung verwendet die **Produktions-API und -Datenbank**, um mit echten Inhalten zu arbeiten. SEO-Einstellungen und Cookie-Consent-Konfigurationen werden ebenfalls aus der Produktionsumgebung geladen.
|
||||
|
||||
---
|
||||
|
||||
## Umgebungskonfiguration
|
||||
|
||||
### Environment Variables (.env.local)
|
||||
|
||||
```env
|
||||
# API-Endpunkte (PRODUKTION)
|
||||
NEXT_PUBLIC_PAYLOAD_URL=https://cms.c2sgmbh.de
|
||||
NEXT_PUBLIC_API_URL=https://cms.c2sgmbh.de/api
|
||||
|
||||
# Analytics (optional)
|
||||
NEXT_PUBLIC_UMAMI_HOST=https://analytics.c2sgmbh.de
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=<website-id>
|
||||
|
||||
# Tenant-Konfiguration (je nach Projekt)
|
||||
NEXT_PUBLIC_TENANT_ID=4
|
||||
NEXT_PUBLIC_TENANT_SLUG=c2s
|
||||
```
|
||||
|
||||
### Warum Production-Daten?
|
||||
|
||||
| Aspekt | Grund |
|
||||
|--------|-------|
|
||||
| **Content** | Echte Inhalte für realistische Entwicklung |
|
||||
| **SEO** | Produktions-Meta-Tags und Structured Data |
|
||||
| **Cookie-Consent** | Live Cookie-Konfigurationen (DSGVO-relevant) |
|
||||
| **Media** | Produktions-Bilder mit allen Größen |
|
||||
| **Consistency** | Keine Sync-Probleme zwischen Dev/Prod |
|
||||
|
||||
---
|
||||
|
||||
## API-Dokumentation
|
||||
|
||||
| Ressource | URL |
|
||||
|-----------|-----|
|
||||
| **Swagger UI** | https://cms.c2sgmbh.de/api/docs |
|
||||
| **OpenAPI JSON** | https://cms.c2sgmbh.de/api/openapi.json |
|
||||
| **REST API Base** | https://cms.c2sgmbh.de/api |
|
||||
|
||||
---
|
||||
|
||||
## Offene Frontend-Tasks
|
||||
|
||||
### Hohe Priorität
|
||||
|
||||
- [ ] **Block-Komponenten entwickeln**
|
||||
- [ ] Hero Block
|
||||
- [ ] Hero Slider Block
|
||||
- [ ] Text Block
|
||||
- [ ] Image Text Block
|
||||
- [ ] Card Grid Block
|
||||
- [ ] Quote Block
|
||||
- [ ] CTA Block
|
||||
- [ ] Contact Form Block
|
||||
- [ ] Video Block
|
||||
- [ ] Divider Block
|
||||
- [ ] Timeline Block
|
||||
- [ ] Posts List Block
|
||||
- [ ] Testimonials Block
|
||||
- [ ] Newsletter Block
|
||||
- [ ] Process Steps Block
|
||||
- [ ] FAQ Block
|
||||
- [ ] Team Block
|
||||
- [ ] Services Block
|
||||
|
||||
- [ ] **Newsletter-Anmelde-Formular**
|
||||
- API: `POST /api/newsletter/subscribe`
|
||||
- Double Opt-In Flow bereits im Backend implementiert
|
||||
- Felder: email, firstName (optional), tenantId, source
|
||||
|
||||
- [ ] **Cookie-Banner implementieren**
|
||||
- Cookie Configurations aus Production-API laden
|
||||
- Consent-Logs an Backend senden
|
||||
- DSGVO-konform mit Opt-In
|
||||
|
||||
### Mittlere Priorität
|
||||
|
||||
- [ ] **Multi-Tenant Routing**
|
||||
- Domain-basierte Tenant-Erkennung
|
||||
- Locale-Routing (`/[locale]/...`)
|
||||
- Unterstützte Locales: `de` (default), `en`
|
||||
|
||||
- [ ] **SEO-Integration**
|
||||
- Meta-Tags aus Pages/Posts (Production)
|
||||
- Structured Data (JSON-LD)
|
||||
- Sitemap: https://cms.c2sgmbh.de/sitemap.xml
|
||||
|
||||
- [ ] **Suche implementieren**
|
||||
- API: `GET /api/search?q=...&locale=de`
|
||||
- Auto-Complete: `GET /api/search/suggestions?q=...`
|
||||
- Rate-Limit: 30 Requests/Minute
|
||||
|
||||
### Tenant-spezifische Features
|
||||
|
||||
#### porwoll.de
|
||||
- [ ] Portfolio-Galerie (Fotografie)
|
||||
- [ ] Buchungsformular
|
||||
- [ ] Before/After Bildvergleich
|
||||
|
||||
#### complexcaresolutions.de (C2S)
|
||||
- [ ] Team-Übersicht
|
||||
- [ ] Leistungs-Seiten
|
||||
- [ ] Zertifizierungen
|
||||
- [ ] Karriere-Seite mit Stellenangeboten
|
||||
|
||||
#### gunshin.de (Game Development)
|
||||
- [ ] Projekt-Galerie
|
||||
- API: `GET /api/projects?where[tenant][equals]=5`
|
||||
- [ ] Portfolio-Seiten
|
||||
- [ ] Referenzen-Slider
|
||||
|
||||
#### zweitmein.ng
|
||||
- [ ] FAQ-Sektion
|
||||
- [ ] Preistabellen
|
||||
- [ ] Kontaktformular
|
||||
|
||||
---
|
||||
|
||||
## API-Endpoints
|
||||
|
||||
### Collections
|
||||
|
||||
| Collection | Endpoint | Beschreibung |
|
||||
|------------|----------|--------------|
|
||||
| Pages | `GET /api/pages` | Seiten mit Blocks |
|
||||
| Posts | `GET /api/posts` | Blog, News, Presse |
|
||||
| Categories | `GET /api/categories` | Post-Kategorien |
|
||||
| Testimonials | `GET /api/testimonials` | Kundenbewertungen |
|
||||
| Team | `GET /api/team` | Team-Mitglieder |
|
||||
| Services | `GET /api/services` | Leistungen |
|
||||
| FAQs | `GET /api/faqs` | FAQ-Einträge |
|
||||
| Portfolios | `GET /api/portfolios` | Portfolio-Projekte |
|
||||
| Media | `GET /api/media` | Medien/Bilder |
|
||||
| Videos | `GET /api/videos` | Video-Bibliothek |
|
||||
| Timelines | `GET /api/timelines` | Chronologische Events |
|
||||
| Workflows | `GET /api/workflows` | Prozess-Darstellungen |
|
||||
| Favorites | `GET /api/favorites` | Affiliate-Produkte (BlogWoman) |
|
||||
| Series | `GET /api/series` | YouTube-Serien (BlogWoman) |
|
||||
|
||||
### Community Management (YouTube/Meta)
|
||||
|
||||
| Collection | Endpoint | Beschreibung |
|
||||
|------------|----------|--------------|
|
||||
| YouTube Channels | `GET /api/youtube-channels` | Multi-Kanal-Verwaltung |
|
||||
| YouTube Content | `GET /api/youtube-content` | Videos + Shorts mit Kommentaren |
|
||||
| YT Series | `GET /api/yt-series` | Serien mit Branding (Logo, Farben) |
|
||||
| YT Notifications | `GET /api/yt-notifications` | Handlungsbedarf-System |
|
||||
| Social Platforms | `GET /api/social-platforms` | Plattform-Konfiguration |
|
||||
| Social Accounts | `GET /api/social-accounts` | OAuth-Verbindungen |
|
||||
| Community Interactions | `GET /api/community-interactions` | Kommentare/Nachrichten |
|
||||
|
||||
### Site Settings & Navigation (Tenant-isolierte Collections)
|
||||
|
||||
> **Hinweis:** SiteSettings und Navigations wurden zu tenant-spezifischen Collections umgewandelt.
|
||||
|
||||
| Collection | Endpoint | Beschreibung |
|
||||
|------------|----------|--------------|
|
||||
| Site Settings | `GET /api/site-settings?where[tenant][equals]=4` | Logo, Name, Kontakt, Adresse |
|
||||
| Navigation | `GET /api/navigations?where[tenant][equals]=4` | Menü-Struktur |
|
||||
| Privacy Policy | `GET /api/privacy-policy-settings?where[tenant][equals]=4` | Datenschutz |
|
||||
|
||||
### Globals (Systemweit)
|
||||
|
||||
| Global | Endpoint | Beschreibung |
|
||||
|--------|----------|--------------|
|
||||
| SEO Settings | `GET /api/globals/seo-settings` | Default SEO (Production) |
|
||||
|
||||
### Spezielle Endpoints
|
||||
|
||||
| Endpoint | Methode | Beschreibung |
|
||||
|----------|---------|--------------|
|
||||
| `/api/search` | GET | Volltextsuche |
|
||||
| `/api/search/suggestions` | GET | Auto-Complete |
|
||||
| `/api/newsletter/subscribe` | POST | Newsletter-Anmeldung |
|
||||
| `/api/timelines` | GET | Timeline-Daten |
|
||||
| `/api/workflows` | GET | Workflow-Daten |
|
||||
|
||||
---
|
||||
|
||||
## Tenant-Filterung
|
||||
|
||||
Alle Collection-Anfragen sollten nach Tenant gefiltert werden:
|
||||
|
||||
```typescript
|
||||
// Beispiel: Posts für Tenant "c2s" (ID: 4)
|
||||
fetch('https://cms.c2sgmbh.de/api/posts?where[tenant][equals]=4&locale=de')
|
||||
|
||||
// Beispiel: Pages für Tenant "gunshin" (ID: 5)
|
||||
fetch('https://cms.c2sgmbh.de/api/pages?where[tenant][equals]=5&locale=de')
|
||||
|
||||
// Beispiel: SEO-Settings (Global, kein Tenant-Filter)
|
||||
fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
|
||||
```
|
||||
|
||||
### Tenant-IDs
|
||||
|
||||
| ID | Name | Slug | Domain |
|
||||
|----|------|------|--------|
|
||||
| 1 | porwoll.de | porwoll | porwoll.de |
|
||||
| 4 | Complex Care Solutions GmbH | c2s | complexcaresolutions.de |
|
||||
| 5 | Gunshin | gunshin | gunshin.de |
|
||||
|
||||
---
|
||||
|
||||
## Bild-Optimierung
|
||||
|
||||
Media-Objekte enthalten mehrere Größen:
|
||||
|
||||
```typescript
|
||||
interface Media {
|
||||
url: string // Original
|
||||
sizes: {
|
||||
thumbnail: { url, width, height } // 150x150
|
||||
small: { url, width, height } // 300x300
|
||||
medium: { url, width, height } // 600x600
|
||||
large: { url, width, height } // 1200x1200
|
||||
xlarge: { url, width, height } // 1920x1920
|
||||
'2k': { url, width, height } // 2560x2560
|
||||
og: { url, width, height } // 1200x630 (Social)
|
||||
// + AVIF-Varianten
|
||||
thumbnail_avif: { url, width, height }
|
||||
small_avif: { url, width, height }
|
||||
// ...
|
||||
}
|
||||
focalX?: number // Fokuspunkt X (0-100)
|
||||
focalY?: number // Fokuspunkt Y (0-100)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lokalisierung
|
||||
|
||||
Unterstützte Locales: `de` (default), `en`
|
||||
|
||||
```typescript
|
||||
// Deutsch (default)
|
||||
fetch('https://cms.c2sgmbh.de/api/posts?locale=de')
|
||||
|
||||
// Englisch
|
||||
fetch('https://cms.c2sgmbh.de/api/posts?locale=en')
|
||||
|
||||
// Fallback: Wenn EN nicht vorhanden, wird DE zurückgegeben
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Newsletter-Integration
|
||||
|
||||
### Anmeldung
|
||||
|
||||
```typescript
|
||||
const response = await fetch('https://cms.c2sgmbh.de/api/newsletter/subscribe', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: 'user@example.com',
|
||||
firstName: 'Max', // optional
|
||||
tenantId: 4, // Pflicht
|
||||
source: 'footer' // optional: Herkunft
|
||||
})
|
||||
})
|
||||
|
||||
// Response: { success: true, message: '...' }
|
||||
```
|
||||
|
||||
### Flow
|
||||
1. User gibt E-Mail ein → `POST /api/newsletter/subscribe`
|
||||
2. Backend sendet Double Opt-In E-Mail
|
||||
3. User klickt Bestätigungs-Link
|
||||
4. Backend sendet Willkommens-E-Mail
|
||||
5. User kann sich über Link in E-Mails abmelden
|
||||
|
||||
---
|
||||
|
||||
## Cookie-Consent (Production-Daten)
|
||||
|
||||
### Konfiguration laden
|
||||
|
||||
```typescript
|
||||
// Cookie-Konfiguration aus Production laden
|
||||
const config = await fetch('https://cms.c2sgmbh.de/api/cookie-configurations?where[tenant][equals]=4')
|
||||
.then(r => r.json())
|
||||
|
||||
// config.docs enthält:
|
||||
// - Kategorien (necessary, analytics, marketing, etc.)
|
||||
// - Cookie-Details pro Kategorie
|
||||
// - Texte für Banner (lokalisiert)
|
||||
```
|
||||
|
||||
### Consent loggen
|
||||
|
||||
```typescript
|
||||
await fetch('https://cms.c2sgmbh.de/api/consent-logs', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tenant: 4,
|
||||
consentGiven: true,
|
||||
categories: ['necessary', 'analytics'],
|
||||
ipAddress: '...', // Optional, für DSGVO
|
||||
userAgent: navigator.userAgent
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SEO-Integration (Production-Daten)
|
||||
|
||||
### Global SEO-Settings
|
||||
|
||||
```typescript
|
||||
// SEO-Defaults aus Production laden
|
||||
const seoSettings = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
|
||||
.then(r => r.json())
|
||||
|
||||
// Enthält:
|
||||
// - defaultTitle, titleTemplate
|
||||
// - defaultDescription
|
||||
// - defaultImage (OG-Image)
|
||||
// - robotsDefault
|
||||
```
|
||||
|
||||
### Page-spezifische SEO
|
||||
|
||||
```typescript
|
||||
// SEO-Daten aus Page laden
|
||||
const page = await fetch('https://cms.c2sgmbh.de/api/pages?where[slug][equals]=about&where[tenant][equals]=4')
|
||||
.then(r => r.json())
|
||||
|
||||
// page.docs[0].meta enthält:
|
||||
// - title, description
|
||||
// - image (OG-Image Override)
|
||||
// - noIndex, noFollow
|
||||
```
|
||||
|
||||
### Sitemap
|
||||
|
||||
Die Sitemap wird automatisch von Payload generiert:
|
||||
- **URL:** https://cms.c2sgmbh.de/sitemap.xml
|
||||
- Enthält alle publizierten Pages und Posts
|
||||
|
||||
---
|
||||
|
||||
## Kontaktformular
|
||||
|
||||
Formular-Submissions werden über die Forms-Collection verarbeitet:
|
||||
|
||||
```typescript
|
||||
await fetch('https://cms.c2sgmbh.de/api/form-submissions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
form: 1, // Form-ID
|
||||
submissionData: [
|
||||
{ field: 'name', value: 'Max Mustermann' },
|
||||
{ field: 'email', value: 'max@example.com' },
|
||||
{ field: 'message', value: 'Ihre Nachricht...' }
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentifizierung (optional)
|
||||
|
||||
Falls User-Authentifizierung benötigt wird:
|
||||
|
||||
```typescript
|
||||
// Login
|
||||
const { token, user } = await fetch('https://cms.c2sgmbh.de/api/users/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password })
|
||||
}).then(r => r.json())
|
||||
|
||||
// Authentifizierte Requests
|
||||
fetch('https://cms.c2sgmbh.de/api/...', {
|
||||
headers: { 'Authorization': `JWT ${token}` }
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entwicklungshinweise
|
||||
|
||||
### TypeScript-Typen
|
||||
|
||||
Die Payload-Typen können aus dem Backend exportiert werden:
|
||||
|
||||
```bash
|
||||
# Auf dem Payload-Server (Production)
|
||||
ssh payload@162.55.85.18
|
||||
cd ~/payload-cms
|
||||
pnpm payload generate:types
|
||||
|
||||
# Datei: src/payload-types.ts
|
||||
```
|
||||
|
||||
Diese Datei kann ins Frontend-Projekt kopiert werden für typsichere API-Calls.
|
||||
|
||||
### Rate-Limits
|
||||
|
||||
| Endpoint | Limit |
|
||||
|----------|-------|
|
||||
| Öffentliche API | 60/min |
|
||||
| Suche | 30/min |
|
||||
| Newsletter | 5/10min |
|
||||
| Formulare | 5/10min |
|
||||
|
||||
### Caching
|
||||
|
||||
- API-Responses werden serverseitig gecacht (Redis)
|
||||
- TTL: 60 Sekunden für Suche
|
||||
- Cache wird bei Content-Änderungen invalidiert
|
||||
|
||||
### CORS
|
||||
|
||||
Die Production-API erlaubt Requests von:
|
||||
- `*.porwoll.tech` (Development)
|
||||
- `porwoll.de`, `complexcaresolutions.de`, `gunshin.de` (Production)
|
||||
|
||||
---
|
||||
|
||||
## Development Server (sv-frontend)
|
||||
|
||||
### SSH-Zugang
|
||||
|
||||
```bash
|
||||
ssh frontend@10.10.181.104
|
||||
```
|
||||
|
||||
### Projekt starten
|
||||
|
||||
```bash
|
||||
cd ~/frontend.porwoll.de
|
||||
pnpm dev
|
||||
# Läuft auf Port 3000 → https://porwoll-dev.porwoll.tech
|
||||
```
|
||||
|
||||
### AI-Tools
|
||||
|
||||
```bash
|
||||
claude # Claude Code CLI
|
||||
codex # Codex CLI
|
||||
gemini # Gemini CLI
|
||||
```
|
||||
|
||||
### Service-Management
|
||||
|
||||
```bash
|
||||
# Systemd Service starten
|
||||
systemctl start frontend-porwoll
|
||||
|
||||
# Service stoppen
|
||||
systemctl stop frontend-porwoll
|
||||
|
||||
# Logs anzeigen
|
||||
journalctl -u frontend-porwoll -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Community Management Integration
|
||||
|
||||
### YouTube-Inhalte abrufen
|
||||
|
||||
```typescript
|
||||
// Alle Videos eines Kanals
|
||||
const videos = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/youtube-content?where[channel][equals]=1&where[contentType][equals]=video'
|
||||
).then(r => r.json())
|
||||
|
||||
// Serien eines Kanals
|
||||
const series = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/yt-series?where[channel][equals]=1&where[isActive][equals]=true'
|
||||
).then(r => r.json())
|
||||
|
||||
// Videos einer Serie
|
||||
const seriesVideos = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/youtube-content?where[series][equals]=1'
|
||||
).then(r => r.json())
|
||||
```
|
||||
|
||||
### Kommentare/Interaktionen (Auth erforderlich)
|
||||
|
||||
```typescript
|
||||
// Kommentare eines Videos
|
||||
const comments = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/community-interactions?where[contentId][equals]=VIDEO_ID',
|
||||
{ headers: { 'Authorization': `JWT ${token}` } }
|
||||
).then(r => r.json())
|
||||
|
||||
// Ungelesene Benachrichtigungen
|
||||
const notifications = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/yt-notifications?where[status][equals]=unread',
|
||||
{ headers: { 'Authorization': `JWT ${token}` } }
|
||||
).then(r => r.json())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ressourcen
|
||||
|
||||
- **Payload CMS Docs:** https://payloadcms.com/docs
|
||||
- **API-Dokumentation:** https://cms.c2sgmbh.de/api/docs
|
||||
- **Backend-Repository:** https://github.com/complexcaresolutions/cms.c2sgmbh.git
|
||||
- **Analytics:** https://analytics.c2sgmbh.de
|
||||
|
||||
---
|
||||
|
||||
*Letzte Aktualisierung: 17.01.2026*
|
||||
805
docs/guides/KONZEPT-KI-ANLEITUNG.md
Normal file
805
docs/guides/KONZEPT-KI-ANLEITUNG.md
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
# Anleitung: Prompt-Erstellung für Tenant-Setup in Payload CMS
|
||||
|
||||
Diese Anleitung erklärt, wie du als Konzept-KI einen strukturierten Prompt für Claude Code erstellen kannst, um einen neuen Tenant im Payload CMS anzulegen und mit Inhalten zu befüllen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Projektkontext
|
||||
|
||||
Das Payload CMS ist ein Multi-Tenant-fähiges Headless CMS mit folgenden Eigenschaften:
|
||||
|
||||
- **Framework:** Payload 3.69.0 + Next.js 15.5.9
|
||||
- **Datenbank:** PostgreSQL 17
|
||||
- **Sprachen:** Deutsch (de, Standard) und Englisch (en)
|
||||
- **Tenant-Isolation:** Jede Collection ist automatisch tenant-spezifisch
|
||||
|
||||
---
|
||||
|
||||
## 2. Prompt-Struktur
|
||||
|
||||
Erstelle deinen Prompt nach folgendem Schema:
|
||||
|
||||
```markdown
|
||||
## Tenant-Informationen
|
||||
|
||||
**Name:** [Firmenname]
|
||||
**Slug:** [url-freundlicher-name]
|
||||
**Domain(s):** [domain1.de, domain2.com]
|
||||
|
||||
### E-Mail-Konfiguration (optional)
|
||||
- From-Adresse: [email]
|
||||
- From-Name: [Name]
|
||||
- Reply-To: [email]
|
||||
- Eigener SMTP: [ja/nein]
|
||||
- Host: [smtp.example.com]
|
||||
- Port: [587]
|
||||
- User: [user]
|
||||
- Passwort: [pass]
|
||||
|
||||
---
|
||||
|
||||
## Inhalte
|
||||
|
||||
### Site-Settings
|
||||
- Logo: [Beschreibung/URL]
|
||||
- Favicon: [Beschreibung/URL]
|
||||
- Primärfarbe: [#hex]
|
||||
- Sekundärfarbe: [#hex]
|
||||
- Footer-Text: [Text]
|
||||
|
||||
### Navigation
|
||||
[Liste der Menüpunkte mit Links]
|
||||
|
||||
### Seiten
|
||||
[Für jede Seite: Titel, Slug, Blocks]
|
||||
|
||||
### Blog-Posts (optional)
|
||||
[Für jeden Post: Titel, Kategorie, Inhalt]
|
||||
|
||||
### Weitere Inhalte
|
||||
[Services, Team, Testimonials, etc.]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Verfügbare Collections
|
||||
|
||||
### Kern-Collections
|
||||
| Collection | Beschreibung | Wichtige Felder |
|
||||
|------------|--------------|-----------------|
|
||||
| `tenants` | Tenant-Konfiguration | name, slug, domains, email |
|
||||
| `site-settings` | Website-Einstellungen | logo, colors, footer, contact |
|
||||
| `navigations` | Menü-Strukturen | items (array mit label, link, children) |
|
||||
| `pages` | Website-Seiten | title, slug, blocks (array) |
|
||||
| `media` | Bilder/Dateien | file, alt, caption |
|
||||
|
||||
### Content-Collections
|
||||
| Collection | Beschreibung | Wichtige Felder |
|
||||
|------------|--------------|-----------------|
|
||||
| `posts` | Blog-Artikel | title, content, categories, tags, authors, type |
|
||||
| `categories` | Post-Kategorien | name, slug |
|
||||
| `tags` | Post-Tags | name, slug |
|
||||
| `authors` | Autoren | name, bio, image |
|
||||
| `testimonials` | Kundenstimmen | quote, author, company, rating |
|
||||
| `faqs` | FAQ-Einträge | question, answer |
|
||||
| `team` | Team-Mitglieder | name, position, bio, image |
|
||||
| `services` | Dienstleistungen | title, description, icon |
|
||||
| `service-categories` | Service-Kategorien | name, slug |
|
||||
|
||||
### Erweiterte Collections
|
||||
| Collection | Beschreibung | Wichtige Felder |
|
||||
|------------|--------------|-----------------|
|
||||
| `portfolios` | Portfolio-Einträge | title, images, description, category |
|
||||
| `products` | Produkte | name, price, description, images |
|
||||
| `events` | Veranstaltungen | title, date, location, description |
|
||||
| `jobs` | Stellenangebote | title, department, description, requirements |
|
||||
| `locations` | Standorte | name, address, coordinates, hours |
|
||||
| `partners` | Partner/Logos | name, logo, website |
|
||||
| `downloads` | Downloads | title, file, description |
|
||||
| `videos` | Video-Bibliothek | title, source (youtube/vimeo/upload), url |
|
||||
| `timelines` | Zeitstrahlen | title, type, entries |
|
||||
| `workflows` | Prozesse | title, phases, steps |
|
||||
|
||||
### Formular & Newsletter
|
||||
| Collection | Beschreibung |
|
||||
|------------|--------------|
|
||||
| `forms` | Formular-Builder |
|
||||
| `newsletter-subscribers` | Newsletter-Abonnenten |
|
||||
|
||||
### Spezial-Collections (tenant-spezifisch)
|
||||
| Collection | Tenant | Beschreibung |
|
||||
|------------|--------|--------------|
|
||||
| `bookings` | porwoll.de | Fotografie-Buchungen |
|
||||
| `certifications` | c2s | Zertifizierungen |
|
||||
| `projects` | gunshin | Spieleprojekte |
|
||||
| `favorites` | BlogWoman | Affiliate-Produkte |
|
||||
| `series` | BlogWoman | YouTube-Serien |
|
||||
|
||||
---
|
||||
|
||||
## 4. Verfügbare Blocks (42 Stück)
|
||||
|
||||
### Layout-Blocks
|
||||
```
|
||||
hero-block - Hero-Banner mit Bild
|
||||
hero-slider-block - Hero-Slider mit mehreren Slides
|
||||
image-slider-block - Bild-Karussell
|
||||
text-block - Rich-Text-Inhalt
|
||||
image-text-block - Bild + Text nebeneinander
|
||||
card-grid-block - Karten-Raster
|
||||
quote-block - Zitat/Blockquote
|
||||
cta-block - Call-to-Action Button
|
||||
divider-block - Visueller Trenner
|
||||
```
|
||||
|
||||
### Media-Blocks
|
||||
```
|
||||
video-block - Video (YouTube/Vimeo/Upload)
|
||||
video-embed-block - Video-Einbettung mit Privacy-Mode
|
||||
```
|
||||
|
||||
### Content-Blocks
|
||||
```
|
||||
posts-list-block - Blog-Post-Liste mit Pagination
|
||||
testimonials-block - Testimonial-Karussell
|
||||
newsletter-block - Newsletter-Anmeldung
|
||||
process-steps-block - Schritt-für-Schritt-Prozess
|
||||
faq-block - FAQ-Akkordeon
|
||||
team-block - Team-Mitglieder-Grid
|
||||
services-block - Service-Auflistung
|
||||
```
|
||||
|
||||
### Blog-Blocks
|
||||
```
|
||||
author-bio-block - Autoren-Info
|
||||
related-posts-block - Verwandte Artikel
|
||||
share-buttons-block - Social-Share-Buttons
|
||||
table-of-contents-block - Inhaltsverzeichnis
|
||||
```
|
||||
|
||||
### Team-Blocks
|
||||
```
|
||||
team-filter-block - Team mit Filter
|
||||
org-chart-block - Organigramm
|
||||
```
|
||||
|
||||
### Feature-Blocks
|
||||
```
|
||||
locations-block - Standorte mit Karte
|
||||
logo-grid-block - Partner-/Kunden-Logos
|
||||
stats-block - Statistiken/Zahlen
|
||||
jobs-block - Stellenangebote
|
||||
downloads-block - Download-Bereich
|
||||
map-block - Karten-Einbettung
|
||||
events-block - Veranstaltungs-Kalender
|
||||
pricing-block - Preis-Tabellen
|
||||
tabs-block - Tab-Inhalte
|
||||
accordion-block - Akkordeon-Sektionen
|
||||
comparison-block - Vergleichs-Tabellen
|
||||
timeline-block - Zeitleisten-Visualisierung
|
||||
before-after-block - Vorher/Nachher-Vergleich
|
||||
```
|
||||
|
||||
### BlogWoman-Blocks
|
||||
```
|
||||
favorites-block - Affiliate-Produkte
|
||||
series-block - YouTube-Serien-Übersicht
|
||||
series-detail-block - Einzelne Serie mit Hero
|
||||
featured-content-block - Kuratierte Inhalte
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Block-Konfiguration (Details)
|
||||
|
||||
### hero-block
|
||||
```json
|
||||
{
|
||||
"blockType": "hero-block",
|
||||
"heading": "Willkommen",
|
||||
"subheading": "Untertitel",
|
||||
"backgroundImage": "<media-id>",
|
||||
"ctaText": "Mehr erfahren",
|
||||
"ctaLink": "/ueber-uns",
|
||||
"alignment": "center",
|
||||
"overlay": true,
|
||||
"overlayOpacity": 0.5
|
||||
}
|
||||
```
|
||||
|
||||
### hero-slider-block
|
||||
```json
|
||||
{
|
||||
"blockType": "hero-slider-block",
|
||||
"slides": [
|
||||
{
|
||||
"heading": "Slide 1",
|
||||
"subheading": "Text",
|
||||
"backgroundImage": "<media-id>",
|
||||
"ctaText": "Button",
|
||||
"ctaLink": "/link"
|
||||
}
|
||||
],
|
||||
"autoplay": true,
|
||||
"autoplaySpeed": 5000,
|
||||
"showDots": true,
|
||||
"showArrows": true
|
||||
}
|
||||
```
|
||||
|
||||
### text-block
|
||||
```json
|
||||
{
|
||||
"blockType": "text-block",
|
||||
"content": {
|
||||
"root": {
|
||||
"type": "root",
|
||||
"children": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"children": [{"text": "Ihr Text hier..."}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### image-text-block
|
||||
```json
|
||||
{
|
||||
"blockType": "image-text-block",
|
||||
"image": "<media-id>",
|
||||
"heading": "Überschrift",
|
||||
"content": "<rich-text>",
|
||||
"imagePosition": "left",
|
||||
"ctaText": "Button",
|
||||
"ctaLink": "/link"
|
||||
}
|
||||
```
|
||||
|
||||
### card-grid-block
|
||||
```json
|
||||
{
|
||||
"blockType": "card-grid-block",
|
||||
"cards": [
|
||||
{
|
||||
"title": "Karte 1",
|
||||
"description": "Beschreibung",
|
||||
"image": "<media-id>",
|
||||
"link": "/link"
|
||||
}
|
||||
],
|
||||
"columns": 3
|
||||
}
|
||||
```
|
||||
|
||||
### faq-block
|
||||
```json
|
||||
{
|
||||
"blockType": "faq-block",
|
||||
"heading": "Häufige Fragen",
|
||||
"faqs": ["<faq-id-1>", "<faq-id-2>"]
|
||||
}
|
||||
```
|
||||
|
||||
### testimonials-block
|
||||
```json
|
||||
{
|
||||
"blockType": "testimonials-block",
|
||||
"heading": "Kundenstimmen",
|
||||
"testimonials": ["<testimonial-id-1>", "<testimonial-id-2>"],
|
||||
"layout": "carousel"
|
||||
}
|
||||
```
|
||||
|
||||
### team-block
|
||||
```json
|
||||
{
|
||||
"blockType": "team-block",
|
||||
"heading": "Unser Team",
|
||||
"teamMembers": ["<team-id-1>", "<team-id-2>"],
|
||||
"showBio": true,
|
||||
"columns": 4
|
||||
}
|
||||
```
|
||||
|
||||
### services-block
|
||||
```json
|
||||
{
|
||||
"blockType": "services-block",
|
||||
"heading": "Unsere Leistungen",
|
||||
"services": ["<service-id-1>", "<service-id-2>"],
|
||||
"layout": "grid"
|
||||
}
|
||||
```
|
||||
|
||||
### posts-list-block
|
||||
```json
|
||||
{
|
||||
"blockType": "posts-list-block",
|
||||
"heading": "Neueste Artikel",
|
||||
"categories": ["<category-id>"],
|
||||
"limit": 6,
|
||||
"showPagination": true
|
||||
}
|
||||
```
|
||||
|
||||
### cta-block
|
||||
```json
|
||||
{
|
||||
"blockType": "cta-block",
|
||||
"heading": "Jetzt starten",
|
||||
"text": "Kontaktieren Sie uns noch heute.",
|
||||
"buttonText": "Kontakt aufnehmen",
|
||||
"buttonLink": "/kontakt",
|
||||
"backgroundColor": "#1a1a1a"
|
||||
}
|
||||
```
|
||||
|
||||
### stats-block
|
||||
```json
|
||||
{
|
||||
"blockType": "stats-block",
|
||||
"stats": [
|
||||
{"number": "500+", "label": "Kunden"},
|
||||
{"number": "10", "label": "Jahre Erfahrung"},
|
||||
{"number": "24/7", "label": "Support"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### pricing-block
|
||||
```json
|
||||
{
|
||||
"blockType": "pricing-block",
|
||||
"heading": "Unsere Preise",
|
||||
"plans": [
|
||||
{
|
||||
"name": "Basic",
|
||||
"price": "29",
|
||||
"period": "monatlich",
|
||||
"features": ["Feature 1", "Feature 2"],
|
||||
"ctaText": "Auswählen",
|
||||
"ctaLink": "/checkout/basic",
|
||||
"highlighted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Beispiel-Prompt (Vollständig)
|
||||
|
||||
```markdown
|
||||
# Tenant anlegen: Musterfirma GmbH
|
||||
|
||||
## Tenant-Informationen
|
||||
|
||||
**Name:** Musterfirma GmbH
|
||||
**Slug:** musterfirma
|
||||
**Domains:** musterfirma.de, www.musterfirma.de
|
||||
|
||||
### E-Mail-Konfiguration
|
||||
- From-Adresse: info@musterfirma.de
|
||||
- From-Name: Musterfirma GmbH
|
||||
- Reply-To: kontakt@musterfirma.de
|
||||
- Eigener SMTP: nein (globalen SMTP verwenden)
|
||||
|
||||
---
|
||||
|
||||
## Site-Settings
|
||||
|
||||
- **Logo:** Musterfirma-Logo (blauer Kreis mit weißem "M")
|
||||
- **Favicon:** Kleines "M" auf blauem Hintergrund
|
||||
- **Primärfarbe:** #2563eb (Blau)
|
||||
- **Sekundärfarbe:** #1e40af (Dunkelblau)
|
||||
- **Akzentfarbe:** #f59e0b (Orange)
|
||||
- **Footer-Text:** "© 2026 Musterfirma GmbH. Alle Rechte vorbehalten."
|
||||
- **Kontakt-E-Mail:** info@musterfirma.de
|
||||
- **Telefon:** +49 123 456789
|
||||
- **Adresse:** Musterstraße 1, 12345 Musterstadt
|
||||
|
||||
---
|
||||
|
||||
## Navigation
|
||||
|
||||
### Hauptmenü
|
||||
1. Startseite → /
|
||||
2. Über uns → /ueber-uns
|
||||
3. Leistungen → /leistungen
|
||||
- Beratung → /leistungen/beratung
|
||||
- Entwicklung → /leistungen/entwicklung
|
||||
- Support → /leistungen/support
|
||||
4. Referenzen → /referenzen
|
||||
5. Blog → /blog
|
||||
6. Kontakt → /kontakt
|
||||
|
||||
### Footer-Navigation
|
||||
1. Impressum → /impressum
|
||||
2. Datenschutz → /datenschutz
|
||||
3. AGB → /agb
|
||||
|
||||
---
|
||||
|
||||
## Seiten
|
||||
|
||||
### Startseite (/)
|
||||
**Blocks:**
|
||||
1. hero-slider-block
|
||||
- Slide 1: "Willkommen bei Musterfirma" / "Ihr Partner für digitale Lösungen" / CTA: "Jetzt beraten lassen" → /kontakt
|
||||
- Slide 2: "Innovation trifft Expertise" / "Seit 10 Jahren erfolgreich" / CTA: "Mehr erfahren" → /ueber-uns
|
||||
|
||||
2. stats-block
|
||||
- 500+ zufriedene Kunden
|
||||
- 10 Jahre Erfahrung
|
||||
- 50 Mitarbeiter
|
||||
- 24/7 Support
|
||||
|
||||
3. services-block
|
||||
- Überschrift: "Unsere Leistungen"
|
||||
- Zeige alle Services
|
||||
|
||||
4. testimonials-block
|
||||
- Überschrift: "Das sagen unsere Kunden"
|
||||
- 3 Testimonials im Karussell
|
||||
|
||||
5. cta-block
|
||||
- "Bereit für Ihr nächstes Projekt?"
|
||||
- Button: "Kostenloses Erstgespräch" → /kontakt
|
||||
|
||||
### Über uns (/ueber-uns)
|
||||
**Blocks:**
|
||||
1. hero-block
|
||||
- "Über Musterfirma"
|
||||
- "Lernen Sie uns kennen"
|
||||
|
||||
2. image-text-block
|
||||
- Bild: Team-Foto
|
||||
- Text: Firmengeschichte und Vision
|
||||
- Bild links
|
||||
|
||||
3. timeline-block
|
||||
- Firmengeschichte als Zeitstrahl
|
||||
- 2016: Gründung
|
||||
- 2018: Erster Großkunde
|
||||
- 2020: 25 Mitarbeiter
|
||||
- 2024: Expansion
|
||||
|
||||
4. team-block
|
||||
- "Unser Team"
|
||||
- Alle Team-Mitglieder
|
||||
|
||||
### Leistungen (/leistungen)
|
||||
**Blocks:**
|
||||
1. hero-block
|
||||
- "Unsere Leistungen"
|
||||
- "Maßgeschneiderte Lösungen für Ihren Erfolg"
|
||||
|
||||
2. services-block
|
||||
- Alle Services mit Icons
|
||||
|
||||
3. process-steps-block
|
||||
- "So arbeiten wir"
|
||||
- Schritt 1: Analyse
|
||||
- Schritt 2: Konzept
|
||||
- Schritt 3: Umsetzung
|
||||
- Schritt 4: Betreuung
|
||||
|
||||
4. cta-block
|
||||
- "Interesse geweckt?"
|
||||
- Button: "Jetzt anfragen"
|
||||
|
||||
### Kontakt (/kontakt)
|
||||
**Blocks:**
|
||||
1. hero-block
|
||||
- "Kontakt"
|
||||
- "Wir freuen uns auf Ihre Nachricht"
|
||||
|
||||
2. image-text-block
|
||||
- Kontaktinformationen
|
||||
- Bild: Büro-Foto
|
||||
|
||||
3. contact-form-block
|
||||
- Kontaktformular
|
||||
|
||||
4. map-block
|
||||
- Standort auf Karte
|
||||
|
||||
### Blog (/blog)
|
||||
**Blocks:**
|
||||
1. hero-block
|
||||
- "Unser Blog"
|
||||
- "Insights und Neuigkeiten"
|
||||
|
||||
2. posts-list-block
|
||||
- Alle Blog-Posts
|
||||
- 6 pro Seite
|
||||
- Mit Pagination
|
||||
|
||||
---
|
||||
|
||||
## Kategorien
|
||||
|
||||
1. **Technologie** (slug: technologie)
|
||||
2. **Trends** (slug: trends)
|
||||
3. **Case Studies** (slug: case-studies)
|
||||
4. **Tipps & Tricks** (slug: tipps-tricks)
|
||||
|
||||
---
|
||||
|
||||
## Tags
|
||||
|
||||
1. Digitalisierung
|
||||
2. Innovation
|
||||
3. KI
|
||||
4. Cloud
|
||||
5. Sicherheit
|
||||
|
||||
---
|
||||
|
||||
## Autoren
|
||||
|
||||
### Max Mustermann
|
||||
- **Position:** CEO & Gründer
|
||||
- **Bio:** Max ist Gründer und CEO der Musterfirma GmbH. Mit über 15 Jahren Erfahrung in der IT-Branche...
|
||||
- **Bild:** Professionelles Porträt
|
||||
|
||||
### Anna Schmidt
|
||||
- **Position:** Head of Content
|
||||
- **Bio:** Anna leitet das Content-Team und ist verantwortlich für alle redaktionellen Inhalte...
|
||||
- **Bild:** Professionelles Porträt
|
||||
|
||||
---
|
||||
|
||||
## Team-Mitglieder
|
||||
|
||||
### Max Mustermann
|
||||
- **Position:** CEO & Gründer
|
||||
- **Bio:** Gründer mit Vision für digitale Transformation
|
||||
- **E-Mail:** max@musterfirma.de
|
||||
- **LinkedIn:** linkedin.com/in/maxmustermann
|
||||
|
||||
### Anna Schmidt
|
||||
- **Position:** Head of Content
|
||||
- **Bio:** Content-Strategin mit Leidenschaft für Storytelling
|
||||
- **E-Mail:** anna@musterfirma.de
|
||||
|
||||
### Tim Weber
|
||||
- **Position:** Lead Developer
|
||||
- **Bio:** Full-Stack-Entwickler mit Fokus auf skalierbare Lösungen
|
||||
- **E-Mail:** tim@musterfirma.de
|
||||
|
||||
### Lisa Müller
|
||||
- **Position:** UX Designer
|
||||
- **Bio:** Kreiert nutzerzentrierte Designs für digitale Produkte
|
||||
- **E-Mail:** lisa@musterfirma.de
|
||||
|
||||
---
|
||||
|
||||
## Services
|
||||
|
||||
### Beratung
|
||||
- **Icon:** lightbulb
|
||||
- **Kurzbeschreibung:** Strategische IT-Beratung für Ihr Unternehmen
|
||||
- **Beschreibung:** Wir analysieren Ihre Geschäftsprozesse und entwickeln maßgeschneiderte Digitalisierungsstrategien...
|
||||
|
||||
### Entwicklung
|
||||
- **Icon:** code
|
||||
- **Kurzbeschreibung:** Individuelle Softwareentwicklung
|
||||
- **Beschreibung:** Unser Entwicklerteam setzt Ihre Ideen in leistungsstarke Anwendungen um...
|
||||
|
||||
### Support
|
||||
- **Icon:** headset
|
||||
- **Kurzbeschreibung:** Zuverlässiger 24/7 Support
|
||||
- **Beschreibung:** Unser Support-Team steht Ihnen rund um die Uhr zur Verfügung...
|
||||
|
||||
---
|
||||
|
||||
## Testimonials
|
||||
|
||||
### Testimonial 1
|
||||
- **Zitat:** "Die Zusammenarbeit mit Musterfirma hat unsere digitale Transformation beschleunigt. Hervorragende Arbeit!"
|
||||
- **Name:** Dr. Peter Schneider
|
||||
- **Position:** CTO
|
||||
- **Unternehmen:** TechCorp AG
|
||||
- **Rating:** 5
|
||||
|
||||
### Testimonial 2
|
||||
- **Zitat:** "Professionell, zuverlässig und innovativ. Genau der Partner, den wir gesucht haben."
|
||||
- **Name:** Maria Hofmann
|
||||
- **Position:** Geschäftsführerin
|
||||
- **Unternehmen:** Digital Solutions GmbH
|
||||
- **Rating:** 5
|
||||
|
||||
### Testimonial 3
|
||||
- **Zitat:** "Das Team von Musterfirma versteht es, komplexe Anforderungen in elegante Lösungen zu verwandeln."
|
||||
- **Name:** Thomas Klein
|
||||
- **Position:** IT-Leiter
|
||||
- **Unternehmen:** InnoTech AG
|
||||
- **Rating:** 5
|
||||
|
||||
---
|
||||
|
||||
## FAQs
|
||||
|
||||
### Wie lange dauert ein typisches Projekt?
|
||||
Die Projektdauer hängt vom Umfang ab. Kleine Projekte dauern 2-4 Wochen, größere Projekte 3-6 Monate.
|
||||
|
||||
### Bieten Sie auch Wartung an?
|
||||
Ja, wir bieten verschiedene Wartungs- und Support-Pakete an, die auf Ihre Bedürfnisse zugeschnitten sind.
|
||||
|
||||
### Wie läuft die Zusammenarbeit ab?
|
||||
Nach einem kostenlosen Erstgespräch erstellen wir ein Angebot. Bei Beauftragung starten wir mit einer Analysephase.
|
||||
|
||||
### Arbeiten Sie auch mit kleinen Unternehmen?
|
||||
Ja, wir betreuen Unternehmen jeder Größe, vom Startup bis zum Konzern.
|
||||
|
||||
---
|
||||
|
||||
## Blog-Posts
|
||||
|
||||
### Post 1: "Die Zukunft der KI im Mittelstand"
|
||||
- **Autor:** Max Mustermann
|
||||
- **Kategorie:** Technologie
|
||||
- **Tags:** KI, Innovation, Digitalisierung
|
||||
- **Type:** blog
|
||||
- **Excerpt:** Wie mittelständische Unternehmen von künstlicher Intelligenz profitieren können...
|
||||
- **Content:**
|
||||
- Einleitung zur KI-Revolution
|
||||
- 3 Anwendungsbeispiele
|
||||
- Implementierungstipps
|
||||
- Fazit und Ausblick
|
||||
- **Status:** published
|
||||
|
||||
### Post 2: "5 Tipps für erfolgreiche Digitalisierung"
|
||||
- **Autor:** Anna Schmidt
|
||||
- **Kategorie:** Tipps & Tricks
|
||||
- **Tags:** Digitalisierung, Tipps
|
||||
- **Type:** blog
|
||||
- **Excerpt:** Praktische Ratschläge für Unternehmen auf dem Weg zur Digitalisierung...
|
||||
- **Content:**
|
||||
- Einleitung
|
||||
- Tipp 1-5 mit Erklärungen
|
||||
- Zusammenfassung
|
||||
- **Status:** published
|
||||
|
||||
### Post 3: "Case Study: TechCorp digitalisiert Prozesse"
|
||||
- **Autor:** Max Mustermann
|
||||
- **Kategorie:** Case Studies
|
||||
- **Tags:** Case Study, Digitalisierung
|
||||
- **Type:** blog
|
||||
- **Excerpt:** Wie wir TechCorp bei der Prozessdigitalisierung unterstützt haben...
|
||||
- **Content:**
|
||||
- Ausgangslage
|
||||
- Herausforderungen
|
||||
- Lösung
|
||||
- Ergebnisse
|
||||
- **Status:** published
|
||||
|
||||
---
|
||||
|
||||
## Rechtliche Seiten
|
||||
|
||||
### Impressum (/impressum)
|
||||
- Angaben gemäß § 5 TMG
|
||||
- Kontaktdaten
|
||||
- Geschäftsführer
|
||||
- Handelsregister
|
||||
- USt-IdNr.
|
||||
|
||||
### Datenschutz (/datenschutz)
|
||||
- DSGVO-konforme Datenschutzerklärung
|
||||
- Verantwortlicher
|
||||
- Datenerfassung
|
||||
- Cookies
|
||||
- Rechte der Betroffenen
|
||||
|
||||
### AGB (/agb)
|
||||
- Allgemeine Geschäftsbedingungen
|
||||
|
||||
---
|
||||
|
||||
## Formulare
|
||||
|
||||
### Kontaktformular
|
||||
- Felder: Name, E-Mail, Telefon (optional), Betreff, Nachricht
|
||||
- Bestätigungs-E-Mail an Absender
|
||||
- Benachrichtigungs-E-Mail an info@musterfirma.de
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Wichtige Hinweise für den Prompt
|
||||
|
||||
### Pflichtangaben
|
||||
- [ ] Tenant-Name und Slug
|
||||
- [ ] Mindestens eine Domain
|
||||
- [ ] Site-Settings (Logo, Farben)
|
||||
- [ ] Navigation (Header, Footer)
|
||||
- [ ] Mindestens eine Seite mit Blocks
|
||||
|
||||
### Empfohlene Angaben
|
||||
- [ ] E-Mail-Konfiguration
|
||||
- [ ] Team-Mitglieder
|
||||
- [ ] Services/Leistungen
|
||||
- [ ] Testimonials
|
||||
- [ ] FAQ-Einträge
|
||||
- [ ] Blog-Kategorien und Posts
|
||||
- [ ] Rechtliche Seiten
|
||||
|
||||
### Formatierung
|
||||
- Verwende Markdown für Strukturierung
|
||||
- Nutze klare Überschriften und Listen
|
||||
- Gib Block-Typen explizit an
|
||||
- Beschreibe Inhalte so detailliert wie möglich
|
||||
- Gib Beziehungen zwischen Inhalten an (z.B. Post → Kategorie)
|
||||
|
||||
### Lokalisierung
|
||||
- Standard-Sprache ist Deutsch (de)
|
||||
- Gib bei Bedarf englische Übersetzungen an:
|
||||
```
|
||||
**Titel (DE):** Über uns
|
||||
**Titel (EN):** About Us
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Ausgabe-Format für Claude
|
||||
|
||||
Dein Prompt sollte Claude bitten, folgende Aufgaben auszuführen:
|
||||
|
||||
1. **Tenant erstellen** via Payload API
|
||||
2. **Site-Settings anlegen** mit allen Konfigurationen
|
||||
3. **Navigationen erstellen** (Header, Footer)
|
||||
4. **Media-Assets hochladen** (falls URLs bereitgestellt)
|
||||
5. **Kategorien/Tags anlegen** für Blog
|
||||
6. **Autoren erstellen** für Blog
|
||||
7. **Team-Mitglieder anlegen**
|
||||
8. **Services erstellen**
|
||||
9. **Testimonials anlegen**
|
||||
10. **FAQs erstellen**
|
||||
11. **Seiten mit Blocks erstellen**
|
||||
12. **Blog-Posts anlegen**
|
||||
13. **Formulare konfigurieren**
|
||||
|
||||
---
|
||||
|
||||
## 9. Beispiel-Einleitung für deinen Prompt
|
||||
|
||||
```markdown
|
||||
# Auftrag: Neuen Tenant anlegen und befüllen
|
||||
|
||||
Bitte lege im Payload CMS einen neuen Tenant mit folgenden Daten an.
|
||||
Erstelle alle notwendigen Inhalte (Collections, Seiten, Posts) gemäß
|
||||
der Spezifikation unten.
|
||||
|
||||
Nutze die Payload Local API oder REST API für die Datenerstellung.
|
||||
Stelle sicher, dass alle Beziehungen korrekt verknüpft werden
|
||||
(z.B. Posts → Categories, Pages → Blocks).
|
||||
|
||||
## Technische Hinweise
|
||||
- Tenant-ID wird automatisch generiert
|
||||
- Media-Uploads: Erstelle Platzhalter oder nutze bereitgestellte URLs
|
||||
- Lexical-Format für Rich-Text-Inhalte verwenden
|
||||
- Status: 'published' für alle Live-Inhalte
|
||||
|
||||
---
|
||||
|
||||
[Hier folgen deine Tenant-Daten...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Checkliste vor Prompt-Übergabe
|
||||
|
||||
- [ ] Alle Pflichtfelder ausgefüllt?
|
||||
- [ ] Block-Typen korrekt angegeben?
|
||||
- [ ] Beziehungen zwischen Inhalten definiert?
|
||||
- [ ] Bilder/Media beschrieben oder URLs bereitgestellt?
|
||||
- [ ] Texte vollständig formuliert (nicht nur Platzhalter)?
|
||||
- [ ] Navigation logisch strukturiert?
|
||||
- [ ] Rechtliche Seiten berücksichtigt?
|
||||
- [ ] E-Mail-Adressen korrekt formatiert?
|
||||
- [ ] Farben als Hex-Werte angegeben?
|
||||
378
docs/guides/SEO_ERWEITERUNG.md
Normal file
378
docs/guides/SEO_ERWEITERUNG.md
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
# SEO-Erweiterung
|
||||
|
||||
*Letzte Aktualisierung: 18. Dezember 2025*
|
||||
|
||||
## Übersicht
|
||||
|
||||
Diese Dokumentation beschreibt die implementierten SEO-Features für das Payload CMS Multi-Tenant System.
|
||||
|
||||
**Wichtig:** Frontends verwenden die **Production-API** (cms.c2sgmbh.de) für SEO-Daten, um konsistente Meta-Tags und Structured Data zu gewährleisten.
|
||||
|
||||
---
|
||||
|
||||
## API-Endpoints für Frontend
|
||||
|
||||
### SEO-Daten abrufen
|
||||
|
||||
| Endpoint | Beschreibung |
|
||||
|----------|--------------|
|
||||
| `GET /api/globals/seo-settings` | Globale SEO-Konfiguration |
|
||||
| `GET /api/pages?where[slug][equals]=...` | Page-spezifische SEO (meta-Feld) |
|
||||
| `GET /api/posts?where[slug][equals]=...` | Post-spezifische SEO |
|
||||
|
||||
### Beispiel: SEO-Settings laden
|
||||
|
||||
```typescript
|
||||
// Frontend: SEO-Defaults aus Production laden
|
||||
const seoSettings = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings')
|
||||
.then(r => r.json())
|
||||
|
||||
// Enthält:
|
||||
// - metaDefaults (titleSuffix, defaultDescription, defaultImage)
|
||||
// - organization (name, legalName, logo, foundingDate)
|
||||
// - contact (email, phone, fax)
|
||||
// - address (street, city, country, etc.)
|
||||
// - socialProfiles (Array)
|
||||
// - localBusiness (type, priceRange, openingHours)
|
||||
// - robots (indexing, additionalDisallow)
|
||||
// - verification (google, bing, yandex)
|
||||
```
|
||||
|
||||
### Beispiel: Page-SEO laden
|
||||
|
||||
```typescript
|
||||
// Page mit SEO-Daten laden
|
||||
const page = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/pages?where[slug][equals]=about&where[tenant][equals]=4&locale=de'
|
||||
).then(r => r.json())
|
||||
|
||||
// page.docs[0].meta enthält:
|
||||
// - title (Page-spezifischer Titel)
|
||||
// - description (Meta-Description)
|
||||
// - image (OG-Image Override)
|
||||
// - noIndex, noFollow (Indexierungssteuerung)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementierte Features
|
||||
|
||||
### 1. Dynamische Sitemap (`/sitemap.xml`)
|
||||
|
||||
**Datei:** `/src/app/sitemap.ts`
|
||||
|
||||
Die Sitemap wird dynamisch aus der Datenbank generiert und enthält:
|
||||
- Startseite (Priorität: 1.0, Änderungshäufigkeit: täglich)
|
||||
- Alle veröffentlichten Seiten (Priorität: 0.8, Änderungshäufigkeit: wöchentlich)
|
||||
- Alle veröffentlichten Posts mit typ-basierter URL (Priorität: 0.6, Änderungshäufigkeit: monatlich)
|
||||
|
||||
**URL-Schema für Posts:**
|
||||
|
||||
| Post-Typ | URL-Prefix |
|
||||
|----------|------------|
|
||||
| blog | `/blog/{slug}` |
|
||||
| news | `/news/{slug}` |
|
||||
| press | `/presse/{slug}` |
|
||||
| announcement | `/aktuelles/{slug}` |
|
||||
|
||||
### 2. Robots.txt (`/robots.txt`)
|
||||
|
||||
**Datei:** `/src/app/robots.ts`
|
||||
|
||||
Konfiguriert Crawler-Zugriff:
|
||||
```
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
Disallow: /admin
|
||||
Disallow: /admin/*
|
||||
Disallow: /api/*
|
||||
Disallow: /_next/*
|
||||
Disallow: /media/*
|
||||
|
||||
User-Agent: Googlebot
|
||||
Allow: /
|
||||
Disallow: /admin
|
||||
Disallow: /api
|
||||
|
||||
Host: https://cms.c2sgmbh.de
|
||||
Sitemap: https://cms.c2sgmbh.de/sitemap.xml
|
||||
```
|
||||
|
||||
### 3. Structured Data (JSON-LD)
|
||||
|
||||
**Datei:** `/src/lib/structuredData.ts`
|
||||
|
||||
Bietet Helper-Funktionen für Schema.org-konforme JSON-LD Daten:
|
||||
|
||||
#### Verfügbare Funktionen
|
||||
|
||||
| Funktion | Beschreibung |
|
||||
|----------|--------------|
|
||||
| `generateOrganizationSchema()` | Organization Schema |
|
||||
| `generateArticleSchema()` | Article Schema für Blog-Posts |
|
||||
| `generateNewsArticleSchema()` | NewsArticle Schema |
|
||||
| `generateWebPageSchema()` | WebPage Schema |
|
||||
| `generateBreadcrumbSchema()` | BreadcrumbList Schema |
|
||||
| `generateFAQSchema()` | FAQPage Schema |
|
||||
| `generateReviewSchema()` | Review/Testimonial Schema |
|
||||
| `generateAggregateRatingSchema()` | AggregateRating Schema |
|
||||
| `generateLocalBusinessSchema()` | LocalBusiness Schema |
|
||||
| `generateWebSiteSchema()` | WebSite Schema mit SearchAction |
|
||||
| `combineSchemas()` | Kombiniert mehrere Schemas |
|
||||
| `renderJsonLd()` | Sicheres Rendering von JSON-LD |
|
||||
|
||||
#### Verwendungsbeispiel (Frontend)
|
||||
|
||||
```tsx
|
||||
// src/components/seo/JsonLd.tsx
|
||||
import { generateArticleSchema, renderJsonLd } from '@/lib/structuredData'
|
||||
|
||||
interface BlogPostProps {
|
||||
post: {
|
||||
title: string
|
||||
excerpt: string
|
||||
slug: string
|
||||
publishedAt: string
|
||||
updatedAt: string
|
||||
author?: { name: string }
|
||||
featuredImage?: { url: string }
|
||||
categories?: Array<{ title: string }>
|
||||
}
|
||||
}
|
||||
|
||||
export default function BlogPost({ post }: BlogPostProps) {
|
||||
const schema = generateArticleSchema({
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
slug: post.slug,
|
||||
publishedAt: post.publishedAt,
|
||||
updatedAt: post.updatedAt,
|
||||
author: post.author,
|
||||
featuredImage: post.featuredImage,
|
||||
categories: post.categories,
|
||||
}, 'https://complexcaresolutions.de') // Tenant-Domain
|
||||
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: renderJsonLd(schema) }}
|
||||
/>
|
||||
<article>...</article>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. SEO Settings Global
|
||||
|
||||
**Datei:** `/src/globals/SEOSettings.ts`
|
||||
|
||||
Globale SEO-Konfiguration im Admin-Panel unter "Einstellungen > SEO Einstellungen":
|
||||
|
||||
#### Meta-Defaults
|
||||
- Titel-Suffix (z.B. "| Firmenname")
|
||||
- Standard Meta-Beschreibung
|
||||
- Standard Social Media Bild
|
||||
- Standard Keywords
|
||||
|
||||
#### Organisation (Schema.org)
|
||||
- Firmenname & rechtlicher Name
|
||||
- Unternehmensbeschreibung
|
||||
- Logo
|
||||
- Gründungsdatum
|
||||
|
||||
#### Kontaktdaten
|
||||
- E-Mail
|
||||
- Telefon
|
||||
- Fax
|
||||
|
||||
#### Adresse
|
||||
- Straße & Hausnummer
|
||||
- PLZ, Stadt, Region
|
||||
- Land & Ländercode
|
||||
|
||||
#### Geo-Koordinaten
|
||||
- Breitengrad
|
||||
- Längengrad
|
||||
|
||||
#### Social Media Profile
|
||||
- Plattform (Facebook, Instagram, Twitter, LinkedIn, YouTube, etc.)
|
||||
- Profil-URL
|
||||
|
||||
#### Local Business
|
||||
- Schema aktivieren/deaktivieren
|
||||
- Geschäftstyp (Arztpraxis, Anwaltskanzlei, Restaurant, etc.)
|
||||
- Preiskategorie (€ bis €€€€)
|
||||
- Öffnungszeiten
|
||||
|
||||
#### Robots & Indexierung
|
||||
- Indexierung erlauben/verbieten
|
||||
- Zusätzliche Pfade ausschließen
|
||||
|
||||
#### Verifizierungscodes
|
||||
- Google Search Console
|
||||
- Bing Webmaster Tools
|
||||
- Yandex Webmaster
|
||||
|
||||
---
|
||||
|
||||
## Frontend-Integration
|
||||
|
||||
### Next.js Metadata API
|
||||
|
||||
```typescript
|
||||
// src/app/[locale]/page.tsx
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
async function getSeoSettings() {
|
||||
const res = await fetch('https://cms.c2sgmbh.de/api/globals/seo-settings', {
|
||||
next: { revalidate: 3600 } // 1 Stunde Cache
|
||||
})
|
||||
return res.json()
|
||||
}
|
||||
|
||||
async function getPage(slug: string, tenantId: number, locale: string) {
|
||||
const res = await fetch(
|
||||
`https://cms.c2sgmbh.de/api/pages?where[slug][equals]=${slug}&where[tenant][equals]=${tenantId}&locale=${locale}`,
|
||||
{ next: { revalidate: 60 } }
|
||||
)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }): Promise<Metadata> {
|
||||
const seoSettings = await getSeoSettings()
|
||||
const pageData = await getPage(params.slug || 'home', 4, params.locale)
|
||||
const page = pageData.docs[0]
|
||||
|
||||
const title = page?.meta?.title
|
||||
? `${page.meta.title} ${seoSettings.metaDefaults?.titleSuffix || ''}`
|
||||
: seoSettings.metaDefaults?.titleSuffix
|
||||
|
||||
const description = page?.meta?.description
|
||||
|| seoSettings.metaDefaults?.defaultDescription
|
||||
|
||||
const image = page?.meta?.image?.url
|
||||
|| seoSettings.metaDefaults?.defaultImage?.url
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
images: image ? [{ url: image }] : [],
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title,
|
||||
description,
|
||||
images: image ? [image] : [],
|
||||
},
|
||||
robots: {
|
||||
index: !page?.meta?.noIndex,
|
||||
follow: !page?.meta?.noFollow,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Verification Meta Tags
|
||||
|
||||
```typescript
|
||||
// src/app/layout.tsx
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const seoSettings = await getSeoSettings()
|
||||
|
||||
return {
|
||||
verification: {
|
||||
google: seoSettings.verification?.google,
|
||||
// Bing und Yandex als other
|
||||
other: {
|
||||
'msvalidate.01': seoSettings.verification?.bing,
|
||||
'yandex-verification': seoSettings.verification?.yandex,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenant SEO
|
||||
|
||||
Jeder Tenant hat eigene SEO-Konfigurationen. Die SEO Settings Global gilt pro Installation, aber Page/Post-SEO ist tenant-spezifisch.
|
||||
|
||||
### Tenant-spezifische Domains
|
||||
|
||||
| Tenant ID | Domain | Sitemap |
|
||||
|-----------|--------|---------|
|
||||
| 1 | porwoll.de | https://porwoll.de/sitemap.xml |
|
||||
| 4 | complexcaresolutions.de | https://complexcaresolutions.de/sitemap.xml |
|
||||
| 5 | gunshin.de | https://gunshin.de/sitemap.xml |
|
||||
|
||||
### Lokalisierung
|
||||
|
||||
SEO-Felder sind lokalisiert (de/en):
|
||||
|
||||
```typescript
|
||||
// Deutscher Content
|
||||
fetch('https://cms.c2sgmbh.de/api/pages?slug=about&tenant=4&locale=de')
|
||||
|
||||
// Englischer Content
|
||||
fetch('https://cms.c2sgmbh.de/api/pages?slug=about&tenant=4&locale=en')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenbank-Tabellen
|
||||
|
||||
Die Migration `20251130_150000_blocks_tables.ts` erstellt:
|
||||
|
||||
- `seo_settings` - Haupttabelle für SEO-Einstellungen
|
||||
- `seo_settings_meta_defaults_keywords` - Keywords Array
|
||||
- `seo_settings_social_profiles` - Social Media Profile
|
||||
- `seo_settings_local_business_opening_hours` - Öffnungszeiten
|
||||
- `seo_settings_robots_additional_disallow` - Ausgeschlossene Pfade
|
||||
|
||||
---
|
||||
|
||||
## URLs
|
||||
|
||||
### Production (für Frontends)
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| **API Base** | https://cms.c2sgmbh.de/api |
|
||||
| **SEO Settings** | https://cms.c2sgmbh.de/api/globals/seo-settings |
|
||||
| **Sitemap** | https://cms.c2sgmbh.de/sitemap.xml |
|
||||
| **Robots** | https://cms.c2sgmbh.de/robots.txt |
|
||||
| **Admin Panel** | https://cms.c2sgmbh.de/admin/globals/seo-settings |
|
||||
|
||||
### Development
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| **API Base** | https://pl.porwoll.tech/api |
|
||||
| **SEO Settings** | https://pl.porwoll.tech/api/globals/seo-settings |
|
||||
| **Admin Panel** | https://pl.porwoll.tech/admin/globals/seo-settings |
|
||||
|
||||
---
|
||||
|
||||
## Checkliste: SEO-Setup pro Tenant
|
||||
|
||||
- [ ] SEO Settings im Admin-Panel konfigurieren
|
||||
- [ ] Organisation (Name, Logo, Beschreibung)
|
||||
- [ ] Kontaktdaten und Adresse
|
||||
- [ ] Social Media Profile hinzufügen
|
||||
- [ ] Local Business aktivieren (falls relevant)
|
||||
- [ ] Google Search Console Code eintragen
|
||||
- [ ] JSON-LD in Frontend-Templates einbinden
|
||||
- [ ] Meta-Tags in Layout integrieren
|
||||
- [ ] Sitemap bei Google Search Console einreichen
|
||||
- [ ] robots.txt prüfen
|
||||
|
||||
---
|
||||
|
||||
*Letzte Aktualisierung: 18. Dezember 2025*
|
||||
2145
docs/guides/styleguide.md
Normal file
2145
docs/guides/styleguide.md
Normal file
File diff suppressed because it is too large
Load diff
1114
prompts/2026-01-20_blogwoman-frontend-entwicklung.md
Normal file
1114
prompts/2026-01-20_blogwoman-frontend-entwicklung.md
Normal file
File diff suppressed because it is too large
Load diff
730
prompts/ANLEITUNG-PLANUNGS-KI-FRONTEND.md
Normal file
730
prompts/ANLEITUNG-PLANUNGS-KI-FRONTEND.md
Normal file
|
|
@ -0,0 +1,730 @@
|
|||
# Anleitung: Frontend-Prompt-Erstellung für Payload CMS
|
||||
|
||||
*Für Planungs-KIs zur Erstellung von Entwicklungs-Prompts*
|
||||
|
||||
---
|
||||
|
||||
## Übersicht
|
||||
|
||||
Diese Anleitung erklärt, wie du als Planungs-KI einen strukturierten Prompt für die Entwicklung eines Next.js-Frontends erstellst, das Daten aus einem Payload CMS über die REST-API bezieht.
|
||||
|
||||
### Architektur-Kontext
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ARCHITEKTUR │
|
||||
│ │
|
||||
│ ┌──────────────────┐ ┌──────────────────────────────┐ │
|
||||
│ │ PAYLOAD CMS │ │ NEXT.JS FRONTEND │ │
|
||||
│ │ (Headless) │ REST │ │ │
|
||||
│ │ │◄────────►│ • Server Components │ │
|
||||
│ │ cms.c2sgmbh.de │ API │ • Client Components │ │
|
||||
│ │ /api/* │ │ • API Routes │ │
|
||||
│ └──────────────────┘ └──────────────────────────────┘ │
|
||||
│ │
|
||||
│ Tech-Stack: │
|
||||
│ • Payload CMS 3.69.0 • Next.js 15.5.9 │
|
||||
│ • PostgreSQL 17 • React 19.2.3 │
|
||||
│ • Multi-Tenant-fähig • TypeScript │
|
||||
│ • Lokalisierung: DE/EN • Tailwind CSS │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teil 1: Benötigte Dateien für die Planungs-KI
|
||||
|
||||
### Pflicht-Dateien (unbedingt bereitstellen)
|
||||
|
||||
| Datei | Zweck | Wo erhältlich |
|
||||
|-------|-------|---------------|
|
||||
| **OpenAPI Spec** | Vollständige API-Dokumentation mit allen Endpoints, Schemas und Parametern | `https://cms.c2sgmbh.de/api/openapi.json` |
|
||||
| **FRONTEND.md** | Umgebungskonfiguration, API-URLs, Development-Workflow | Projektdokumentation |
|
||||
| **API_ANLEITUNG.md** | Authentifizierung, Rate-Limits, Collection-Übersicht | Projektdokumentation |
|
||||
|
||||
### Empfohlene Zusatz-Dateien
|
||||
|
||||
| Datei | Zweck |
|
||||
|-------|-------|
|
||||
| **UNIVERSAL_FEATURES.md** | Block-Typen, Collection-Felder, Layout-Optionen |
|
||||
| **KONZEPT-KI-ANLEITUNG.md** | Block-Konfigurationen mit JSON-Beispielen |
|
||||
| **payload-types.ts** | TypeScript-Typen für alle Collections (generiert aus Payload) |
|
||||
|
||||
### Optional für komplexe Projekte
|
||||
|
||||
| Datei | Zweck |
|
||||
|-------|-------|
|
||||
| **Analytics.md** | Umami-Integration, Event-Tracking |
|
||||
| **SEO_ERWEITERUNG.md** | SEO-Konfiguration, Meta-Tags, Structured Data |
|
||||
|
||||
---
|
||||
|
||||
## Teil 2: API-Struktur verstehen
|
||||
|
||||
### Basis-URLs
|
||||
|
||||
```
|
||||
Production API: https://cms.c2sgmbh.de/api
|
||||
Development API: https://pl.porwoll.tech/api
|
||||
Swagger UI: https://cms.c2sgmbh.de/api/docs
|
||||
OpenAPI JSON: https://cms.c2sgmbh.de/api/openapi.json
|
||||
```
|
||||
|
||||
### Tenant-System (Multi-Mandanten)
|
||||
|
||||
**Kritisch:** Jeder API-Call muss den Tenant filtern!
|
||||
|
||||
```typescript
|
||||
// RICHTIG: Mit Tenant-Filter
|
||||
const response = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/pages?where[tenant][equals]=4&where[slug][equals]=home'
|
||||
)
|
||||
|
||||
// FALSCH: Ohne Tenant → 403 Forbidden oder leere Ergebnisse
|
||||
const response = await fetch(
|
||||
'https://cms.c2sgmbh.de/api/pages?where[slug][equals]=home'
|
||||
)
|
||||
```
|
||||
|
||||
### Tenant-IDs
|
||||
|
||||
| ID | Name | Slug | Domain |
|
||||
|----|------|------|--------|
|
||||
| 1 | porwoll.de | porwoll | porwoll.de |
|
||||
| 4 | Complex Care Solutions GmbH | c2s | complexcaresolutions.de |
|
||||
| 5 | Gunshin | gunshin | gunshin.de |
|
||||
| ... | (weitere Tenants) | ... | ... |
|
||||
|
||||
### Lokalisierung
|
||||
|
||||
```typescript
|
||||
// Deutsche Inhalte (Standard)
|
||||
fetch('/api/posts?locale=de')
|
||||
|
||||
// Englische Inhalte
|
||||
fetch('/api/posts?locale=en')
|
||||
|
||||
// Alle Sprachen gleichzeitig
|
||||
fetch('/api/posts?locale=all')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teil 3: Collections-Referenz
|
||||
|
||||
### Core Collections
|
||||
|
||||
| Collection | Slug | Beschreibung | Öffentlich |
|
||||
|------------|------|--------------|------------|
|
||||
| Pages | `pages` | Seiten mit Block-Layouts | Ja |
|
||||
| Media | `media` | Bilder, Dokumente (11 responsive Sizes) | Ja |
|
||||
| Tenants | `tenants` | Mandanten-Verwaltung | Nein |
|
||||
| Users | `users` | Benutzer mit Tenant-Zuordnung | Nein |
|
||||
|
||||
### Content Collections
|
||||
|
||||
| Collection | Slug | Beschreibung |
|
||||
|------------|------|--------------|
|
||||
| Posts | `posts` | Blog, News, Presse, Ankündigungen |
|
||||
| Categories | `categories` | Kategorien für Posts |
|
||||
| Tags | `tags` | Tags für Posts |
|
||||
| Authors | `authors` | Autoren für Posts |
|
||||
| Testimonials | `testimonials` | Kundenbewertungen |
|
||||
| FAQs | `faqs` | Häufig gestellte Fragen |
|
||||
| Team | `team` | Team-Mitglieder |
|
||||
| Services | `services` | Leistungen/Dienstleistungen |
|
||||
| Service Categories | `service-categories` | Kategorien für Services |
|
||||
|
||||
### Portfolio & Media
|
||||
|
||||
| Collection | Slug | Beschreibung |
|
||||
|------------|------|--------------|
|
||||
| Portfolios | `portfolios` | Portfolio-Galerien |
|
||||
| Portfolio Categories | `portfolio-categories` | Kategorien |
|
||||
| Videos | `videos` | Video-Bibliothek (YouTube/Vimeo/Upload) |
|
||||
| Video Categories | `video-categories` | Kategorien |
|
||||
|
||||
### Kommunikation
|
||||
|
||||
| Collection | Slug | Beschreibung |
|
||||
|------------|------|--------------|
|
||||
| Newsletter Subscribers | `newsletter-subscribers` | Abonnenten (DSGVO) |
|
||||
| Form Submissions | `form-submissions` | Formular-Eingaben |
|
||||
| Contact Requests | `contact-requests` | Kontaktanfragen |
|
||||
|
||||
### Konfiguration
|
||||
|
||||
| Collection | Slug | Beschreibung |
|
||||
|------------|------|--------------|
|
||||
| Site Settings | `site-settings` | Logo, Farben, SEO-Defaults |
|
||||
| Navigations | `navigations` | Header, Footer, Mobile-Nav |
|
||||
| Cookie Configurations | `cookie-configurations` | Cookie-Banner-Settings |
|
||||
| Redirects | `redirects` | URL-Weiterleitungen |
|
||||
|
||||
---
|
||||
|
||||
## Teil 4: Block-Typen für Pages
|
||||
|
||||
Die `pages` Collection verwendet ein Block-basiertes Layout-System. Jede Page hat ein `layout`-Array mit Blocks.
|
||||
|
||||
### Verfügbare Block-Typen
|
||||
|
||||
#### Layout-Blocks
|
||||
|
||||
| Block | Slug | Beschreibung |
|
||||
|-------|------|--------------|
|
||||
| Hero | `hero-block` | Hauptbanner mit Bild, Text, CTA |
|
||||
| Hero Slider | `hero-slider-block` | Karussell mit mehreren Slides |
|
||||
| Text | `text-block` | Rich-Text-Inhalt (Lexical) |
|
||||
| Image-Text | `image-text-block` | Bild + Text nebeneinander |
|
||||
| Card Grid | `card-grid-block` | Karten im Grid-Layout |
|
||||
| CTA | `cta-block` | Call-to-Action Sektion |
|
||||
| Divider | `divider-block` | Visueller Trenner |
|
||||
|
||||
#### Content-Blocks (laden Collection-Daten)
|
||||
|
||||
| Block | Slug | Datenquelle |
|
||||
|-------|------|-------------|
|
||||
| Posts List | `posts-list-block` | Posts Collection |
|
||||
| Testimonials | `testimonials-block` | Testimonials Collection |
|
||||
| FAQ | `faq-block` | FAQs Collection |
|
||||
| Team | `team-block` | Team Collection |
|
||||
| Services | `services-block` | Services Collection |
|
||||
| Newsletter | `newsletter-block` | Newsletter-Anmeldung |
|
||||
|
||||
#### Interaktive Blocks
|
||||
|
||||
| Block | Slug | Beschreibung |
|
||||
|-------|------|--------------|
|
||||
| Contact Form | `contact-form-block` | Kontaktformular |
|
||||
| Timeline | `timeline-block` | Chronologische Darstellung |
|
||||
| Process Steps | `process-steps-block` | Prozess-Visualisierung |
|
||||
| Video | `video-block` | Video-Einbettung |
|
||||
|
||||
### Block-Konfiguration Beispiele
|
||||
|
||||
```json
|
||||
// Hero Block
|
||||
{
|
||||
"blockType": "hero-block",
|
||||
"heading": "Willkommen",
|
||||
"subheading": "Untertitel",
|
||||
"backgroundImage": "<media-id>",
|
||||
"ctaText": "Mehr erfahren",
|
||||
"ctaLink": "/ueber-uns",
|
||||
"alignment": "center",
|
||||
"overlay": true,
|
||||
"overlayOpacity": 0.5
|
||||
}
|
||||
|
||||
// Posts List Block
|
||||
{
|
||||
"blockType": "posts-list-block",
|
||||
"heading": "Neueste Artikel",
|
||||
"type": "blog",
|
||||
"categories": ["<category-id>"],
|
||||
"limit": 6,
|
||||
"layout": "grid",
|
||||
"showPagination": true
|
||||
}
|
||||
|
||||
// FAQ Block
|
||||
{
|
||||
"blockType": "faq-block",
|
||||
"heading": "Häufige Fragen",
|
||||
"displayMode": "selected",
|
||||
"selectedFaqs": ["<faq-id-1>", "<faq-id-2>"],
|
||||
"layout": "accordion",
|
||||
"expandFirst": true,
|
||||
"showSchema": true
|
||||
}
|
||||
|
||||
// Testimonials Block
|
||||
{
|
||||
"blockType": "testimonials-block",
|
||||
"heading": "Kundenstimmen",
|
||||
"displayMode": "all",
|
||||
"limit": 6,
|
||||
"layout": "slider"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teil 5: API-Patterns für Frontend
|
||||
|
||||
### Seite laden
|
||||
|
||||
```typescript
|
||||
// lib/api.ts
|
||||
const PAYLOAD_URL = process.env.NEXT_PUBLIC_PAYLOAD_URL
|
||||
const TENANT_ID = process.env.NEXT_PUBLIC_TENANT_ID
|
||||
|
||||
export async function getPage(slug: string, locale = 'de') {
|
||||
const res = await fetch(
|
||||
`${PAYLOAD_URL}/api/pages?` +
|
||||
`where[tenant][equals]=${TENANT_ID}&` +
|
||||
`where[slug][equals]=${slug}&` +
|
||||
`where[status][equals]=published&` +
|
||||
`locale=${locale}&` +
|
||||
`depth=2`,
|
||||
{ next: { revalidate: 60 } }
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.docs[0] || null
|
||||
}
|
||||
```
|
||||
|
||||
### Posts laden
|
||||
|
||||
```typescript
|
||||
export async function getPosts(options: {
|
||||
type?: 'blog' | 'news' | 'press'
|
||||
category?: string
|
||||
limit?: number
|
||||
page?: number
|
||||
locale?: string
|
||||
}) {
|
||||
const params = new URLSearchParams({
|
||||
'where[tenant][equals]': TENANT_ID,
|
||||
'where[status][equals]': 'published',
|
||||
'sort': '-publishedAt',
|
||||
'limit': String(options.limit || 10),
|
||||
'page': String(options.page || 1),
|
||||
'locale': options.locale || 'de',
|
||||
'depth': '1'
|
||||
})
|
||||
|
||||
if (options.type) {
|
||||
params.append('where[type][equals]', options.type)
|
||||
}
|
||||
if (options.category) {
|
||||
params.append('where[categories][contains]', options.category)
|
||||
}
|
||||
|
||||
const res = await fetch(`${PAYLOAD_URL}/api/posts?${params}`)
|
||||
return res.json()
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation laden
|
||||
|
||||
```typescript
|
||||
export async function getNavigation(type: 'header' | 'footer' | 'mobile') {
|
||||
const res = await fetch(
|
||||
`${PAYLOAD_URL}/api/navigations?` +
|
||||
`where[tenant][equals]=${TENANT_ID}&` +
|
||||
`where[type][equals]=${type}&` +
|
||||
`depth=2`
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.docs[0] || null
|
||||
}
|
||||
```
|
||||
|
||||
### Site Settings laden
|
||||
|
||||
```typescript
|
||||
export async function getSiteSettings() {
|
||||
const res = await fetch(
|
||||
`${PAYLOAD_URL}/api/site-settings?` +
|
||||
`where[tenant][equals]=${TENANT_ID}&` +
|
||||
`depth=2`
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.docs[0] || null
|
||||
}
|
||||
```
|
||||
|
||||
### Newsletter-Anmeldung
|
||||
|
||||
```typescript
|
||||
export async function subscribeNewsletter(email: string, firstName?: string) {
|
||||
const res = await fetch(`${PAYLOAD_URL}/api/newsletter/subscribe`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
firstName,
|
||||
tenantId: Number(TENANT_ID),
|
||||
source: 'website'
|
||||
})
|
||||
})
|
||||
return res.json()
|
||||
}
|
||||
```
|
||||
|
||||
### Kontaktformular
|
||||
|
||||
```typescript
|
||||
export async function submitContactForm(data: {
|
||||
name: string
|
||||
email: string
|
||||
phone?: string
|
||||
subject: string
|
||||
message: string
|
||||
}) {
|
||||
const res = await fetch(`${PAYLOAD_URL}/api/form-submissions`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
form: 1, // Form-ID aus Payload
|
||||
submissionData: [
|
||||
{ field: 'name', value: data.name },
|
||||
{ field: 'email', value: data.email },
|
||||
{ field: 'phone', value: data.phone || '' },
|
||||
{ field: 'subject', value: data.subject },
|
||||
{ field: 'message', value: data.message }
|
||||
]
|
||||
})
|
||||
})
|
||||
return res.json()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teil 6: Prompt-Struktur für Frontend-Entwicklung
|
||||
|
||||
### Template
|
||||
|
||||
```markdown
|
||||
# Frontend [Projektname] - Entwicklungs-Prompt
|
||||
|
||||
## Kontext
|
||||
|
||||
**Projektverzeichnis:** /pfad/zum/frontend
|
||||
**Tech-Stack:** Next.js 15.5.9, React 19.2.3, TypeScript, Tailwind CSS
|
||||
**API-Basis:** https://cms.c2sgmbh.de/api
|
||||
**Tenant-ID:** [ID]
|
||||
**Tenant-Slug:** [slug]
|
||||
**Domain:** [domain.de]
|
||||
|
||||
### Referenz-Dokumente
|
||||
- OpenAPI Spec: [Pfad oder URL zur openapi.json]
|
||||
- Payload Types: [Pfad zur payload-types.ts]
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```env
|
||||
NEXT_PUBLIC_PAYLOAD_URL=https://cms.c2sgmbh.de
|
||||
NEXT_PUBLIC_API_URL=https://cms.c2sgmbh.de/api
|
||||
NEXT_PUBLIC_TENANT_ID=[ID]
|
||||
NEXT_PUBLIC_TENANT_SLUG=[slug]
|
||||
NEXT_PUBLIC_SITE_URL=https://[domain.de]
|
||||
|
||||
# Analytics (optional)
|
||||
NEXT_PUBLIC_UMAMI_HOST=https://analytics.c2sgmbh.de
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=[website-id]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aufgaben
|
||||
|
||||
### 1. Projektstruktur
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/
|
||||
│ ├── layout.tsx # Root-Layout mit Header/Footer
|
||||
│ ├── page.tsx # Startseite
|
||||
│ ├── [slug]/
|
||||
│ │ └── page.tsx # Dynamische Seiten
|
||||
│ ├── blog/
|
||||
│ │ ├── page.tsx # Blog-Übersicht
|
||||
│ │ └── [slug]/
|
||||
│ │ └── page.tsx # Blog-Artikel
|
||||
│ └── api/ # Optional: Proxy-Routes
|
||||
├── components/
|
||||
│ ├── layout/
|
||||
│ │ ├── Header.tsx
|
||||
│ │ ├── Footer.tsx
|
||||
│ │ └── Navigation.tsx
|
||||
│ ├── blocks/ # Block-Komponenten (je Block)
|
||||
│ │ ├── HeroBlock.tsx
|
||||
│ │ ├── TextBlock.tsx
|
||||
│ │ ├── ImageTextBlock.tsx
|
||||
│ │ ├── CardGridBlock.tsx
|
||||
│ │ ├── PostsListBlock.tsx
|
||||
│ │ ├── TestimonialsBlock.tsx
|
||||
│ │ ├── FAQBlock.tsx
|
||||
│ │ ├── TeamBlock.tsx
|
||||
│ │ ├── ServicesBlock.tsx
|
||||
│ │ ├── NewsletterBlock.tsx
|
||||
│ │ ├── CTABlock.tsx
|
||||
│ │ ├── ContactFormBlock.tsx
|
||||
│ │ └── index.tsx # Block-Renderer
|
||||
│ └── ui/ # Wiederverwendbare UI-Komponenten
|
||||
├── lib/
|
||||
│ ├── api.ts # API-Funktionen
|
||||
│ ├── utils.ts # Hilfsfunktionen
|
||||
│ └── types.ts # TypeScript-Typen
|
||||
└── styles/
|
||||
└── globals.css # Tailwind + Custom Styles
|
||||
```
|
||||
|
||||
### 2. API-Integration
|
||||
|
||||
#### 2.1 API-Client erstellen
|
||||
**Datei:** `src/lib/api.ts`
|
||||
|
||||
Implementiere Funktionen für:
|
||||
- `getPage(slug, locale)` - Seite laden
|
||||
- `getPosts(options)` - Blog-Posts laden
|
||||
- `getNavigation(type)` - Navigation laden
|
||||
- `getSiteSettings()` - Site-Settings laden
|
||||
- `getCategories()` - Kategorien laden
|
||||
- `subscribeNewsletter(email, firstName)` - Newsletter
|
||||
- `submitForm(formId, data)` - Formular absenden
|
||||
|
||||
**Wichtig:**
|
||||
- Immer `where[tenant][equals]=${TENANT_ID}` mitgeben
|
||||
- Caching mit `next: { revalidate: 60 }` für statische Daten
|
||||
- Error-Handling implementieren
|
||||
|
||||
### 3. Block-Komponenten
|
||||
|
||||
Für jeden Block-Typ eine React-Komponente erstellen.
|
||||
|
||||
#### 3.1 Block-Renderer
|
||||
**Datei:** `src/components/blocks/index.tsx`
|
||||
|
||||
```typescript
|
||||
import { HeroBlock } from './HeroBlock'
|
||||
import { TextBlock } from './TextBlock'
|
||||
// ... weitere Imports
|
||||
|
||||
const blockComponents = {
|
||||
'hero-block': HeroBlock,
|
||||
'text-block': TextBlock,
|
||||
'image-text-block': ImageTextBlock,
|
||||
// ... weitere Mappings
|
||||
}
|
||||
|
||||
export function BlockRenderer({ blocks }) {
|
||||
return (
|
||||
<>
|
||||
{blocks?.map((block, index) => {
|
||||
const Component = blockComponents[block.blockType]
|
||||
if (!Component) return null
|
||||
return <Component key={index} {...block} />
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 [Block-Name] Block
|
||||
**Datei:** `src/components/blocks/[BlockName]Block.tsx`
|
||||
|
||||
[Für jeden benötigten Block beschreiben:]
|
||||
- Props-Interface (aus API-Response)
|
||||
- Tailwind-Styling
|
||||
- Responsive Verhalten
|
||||
- Interaktivität (falls Client Component)
|
||||
|
||||
### 4. Seiten
|
||||
|
||||
#### 4.1 Layout
|
||||
**Datei:** `src/app/layout.tsx`
|
||||
|
||||
- Header mit Navigation laden
|
||||
- Footer mit Navigation laden
|
||||
- Site-Settings für Meta-Tags
|
||||
- Cookie-Banner (DSGVO)
|
||||
|
||||
#### 4.2 Dynamische Seiten
|
||||
**Datei:** `src/app/[slug]/page.tsx`
|
||||
|
||||
- Seite aus API laden
|
||||
- Metadata generieren
|
||||
- BlockRenderer für Layout
|
||||
- 404 bei nicht gefundener Seite
|
||||
|
||||
### 5. SEO & Meta
|
||||
|
||||
- `generateMetadata()` für jede Seite
|
||||
- Open Graph Tags
|
||||
- JSON-LD für FAQ-Blocks (Schema.org)
|
||||
- Sitemap aus `/api/sitemap.xml`
|
||||
- robots.txt
|
||||
|
||||
### 6. Cookie-Banner
|
||||
|
||||
- Cookie-Konfiguration aus API laden
|
||||
- Consent-State in localStorage
|
||||
- Consent an Backend loggen
|
||||
- Analytics erst nach Consent laden
|
||||
|
||||
---
|
||||
|
||||
## Erfolgskriterien
|
||||
|
||||
- [ ] `pnpm lint` ohne Errors
|
||||
- [ ] `pnpm build` erfolgreich
|
||||
- [ ] Alle Block-Typen gerendert
|
||||
- [ ] Navigation funktioniert
|
||||
- [ ] Blog-Übersicht und Detailseiten
|
||||
- [ ] Kontaktformular sendet Daten
|
||||
- [ ] Newsletter-Anmeldung funktioniert
|
||||
- [ ] SEO-Meta-Tags korrekt
|
||||
- [ ] Mobile-responsive
|
||||
- [ ] Lighthouse Score > 90
|
||||
|
||||
---
|
||||
|
||||
## Escape Hatch
|
||||
|
||||
Nach 15 Iterationen ohne Fortschritt:
|
||||
- Dokumentiere Blocker in BLOCKERS.md
|
||||
- Liste versuchte Lösungen auf
|
||||
- Output <promise>BLOCKED</promise>
|
||||
|
||||
---
|
||||
|
||||
## Fertig?
|
||||
|
||||
Wenn ALLE Aufgaben erledigt sind:
|
||||
|
||||
<promise>FRONTEND_COMPLETE</promise>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Teil 7: Checkliste für Planungs-KI
|
||||
|
||||
### Vor der Prompt-Erstellung
|
||||
|
||||
- [ ] OpenAPI Spec liegt vor (`openapi.json`)
|
||||
- [ ] Tenant-ID und Slug bekannt
|
||||
- [ ] Domain(s) bekannt
|
||||
- [ ] Design-Vorgaben/Mockups vorhanden (optional)
|
||||
- [ ] Block-Typen für Seiten definiert
|
||||
|
||||
### Im Prompt enthalten
|
||||
|
||||
- [ ] Projektverzeichnis angegeben
|
||||
- [ ] Tech-Stack spezifiziert
|
||||
- [ ] Environment Variables definiert
|
||||
- [ ] API-Patterns mit Tenant-Filter
|
||||
- [ ] Alle benötigten Block-Komponenten gelistet
|
||||
- [ ] Seiten-Struktur beschrieben
|
||||
- [ ] Erfolgskriterien messbar formuliert
|
||||
- [ ] Escape Hatch für Blockaden
|
||||
- [ ] Promise-Token am Ende
|
||||
|
||||
### Dateien zur Übergabe an Entwicklungs-KI
|
||||
|
||||
1. **PROMPT.md** - Dein erstellter Prompt
|
||||
2. **openapi.json** - API-Spezifikation
|
||||
3. **payload-types.ts** - TypeScript-Typen (falls verfügbar)
|
||||
4. **Design-Assets** - Mockups, Styleguide (falls vorhanden)
|
||||
|
||||
---
|
||||
|
||||
## Teil 8: Beispiel-Prompt (Kurzversion)
|
||||
|
||||
```markdown
|
||||
# Frontend porwoll.de - Phase 1
|
||||
|
||||
## Kontext
|
||||
- **Verzeichnis:** /home/frontend/porwoll.de
|
||||
- **API:** https://cms.c2sgmbh.de/api
|
||||
- **Tenant-ID:** 1
|
||||
- **Referenz:** /docs/openapi.json
|
||||
|
||||
## Aufgaben
|
||||
|
||||
### 1. Setup
|
||||
- Next.js 15 Projekt initialisieren
|
||||
- Tailwind CSS konfigurieren
|
||||
- Environment Variables setzen
|
||||
|
||||
### 2. API-Client
|
||||
- `src/lib/api.ts` mit allen Fetch-Funktionen
|
||||
- Tenant-Filter in jedem Call
|
||||
|
||||
### 3. Blocks
|
||||
Komponenten für:
|
||||
- HeroBlock
|
||||
- TextBlock
|
||||
- ImageTextBlock
|
||||
- PostsListBlock
|
||||
- FAQBlock
|
||||
- NewsletterBlock
|
||||
- CTABlock
|
||||
- ContactFormBlock
|
||||
|
||||
### 4. Seiten
|
||||
- `/` - Startseite
|
||||
- `/[slug]` - Dynamische Seiten
|
||||
- `/blog` - Blog-Übersicht
|
||||
- `/blog/[slug]` - Blog-Artikel
|
||||
|
||||
### 5. Layout
|
||||
- Header mit Navigation
|
||||
- Footer mit Navigation
|
||||
- Cookie-Banner
|
||||
|
||||
## Erfolgskriterien
|
||||
- [ ] Build erfolgreich
|
||||
- [ ] Alle Blocks rendern
|
||||
- [ ] API-Calls funktionieren
|
||||
|
||||
## Fertig?
|
||||
<promise>PHASE1_DONE</promise>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anhang: API Query-Parameter Referenz
|
||||
|
||||
### Filtering (where)
|
||||
|
||||
```
|
||||
where[field][equals]=value
|
||||
where[field][not_equals]=value
|
||||
where[field][contains]=value
|
||||
where[field][in]=value1,value2
|
||||
where[field][greater_than]=value
|
||||
where[field][less_than]=value
|
||||
where[field][exists]=true
|
||||
```
|
||||
|
||||
### Sorting
|
||||
|
||||
```
|
||||
sort=fieldName # Aufsteigend
|
||||
sort=-fieldName # Absteigend
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```
|
||||
limit=10 # Ergebnisse pro Seite
|
||||
page=1 # Seitennummer
|
||||
```
|
||||
|
||||
### Depth (Relations)
|
||||
|
||||
```
|
||||
depth=0 # Keine Relations laden
|
||||
depth=1 # Eine Ebene Relations
|
||||
depth=2 # Zwei Ebenen (Default)
|
||||
```
|
||||
|
||||
### Lokalisierung
|
||||
|
||||
```
|
||||
locale=de # Deutsche Inhalte
|
||||
locale=en # Englische Inhalte
|
||||
locale=all # Alle Sprachen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Anleitung erstellt: Januar 2026*
|
||||
*Für: Complex Care Solutions GmbH - Payload CMS Multi-Tenant*
|
||||
Loading…
Reference in a new issue