# YouTube Operations Hub v2 – Claude Code Integration Prompt (Korrigiert)

## Projektkontext

Du arbeitest am **YouTube Operations Hub v2** für Complex Care Solutions (CCS). Dies ist eine Erweiterung des bestehenden YouTube-Moduls in Payload CMS 3.x.

**Server:** sv-payload (10.10.181.100)
**Projekt-Pfad:** `/home/payload/payload-cms`
**Datenbank:** PostgreSQL 17 auf sv-postgres (10.10.181.101)
**Tech Stack:** Payload CMS 3.69.0, Next.js 15.5.9, TypeScript, Drizzle ORM
**Package Manager:** pnpm

---

## Aufgabe

Erweitere das bestehende YouTube Operations Hub um:

1. **Script Section Block** – Block für strukturierte Video-Skripte
2. **Batch-Planung** – Neue Collection für detaillierte Produktions-Batches
3. **Monthly Goals** – KPI-Tracking pro Kanal und Monat
4. **Script Templates** – Wiederverwendbare Skript-Vorlagen
5. **Checklist Templates** – Wiederverwendbare Upload-Checklisten
6. **Erweiterte YouTube Content Felder** – Produktions- und Posting-Informationen

> **Hinweis:** Custom Admin Views (Kalender) sind derzeit nicht möglich wegen eines path-to-regexp Bugs in Payload 3.x. Diese werden in einem späteren Update implementiert (siehe `prompts/youtube3-admin-views.md`).

---

## Teil 1: Script Section Block

### Datei: `src/blocks/ScriptSectionBlock.ts`

```typescript
import type { Block } from 'payload'

export const ScriptSectionBlock: Block = {
  slug: 'script-section',
  labels: {
    singular: { de: 'Script Section', en: 'Script Section' },
    plural: { de: 'Script Sections', en: 'Script Sections' },
  },
  fields: [
    {
      type: 'row',
      fields: [
        {
          name: 'sectionType',
          type: 'select',
          required: true,
          label: { de: 'Section-Typ', en: 'Section Type' },
          options: [
            { label: 'Hook', value: 'hook' },
            { label: 'Intro/Ident', value: 'intro_ident' },
            { label: 'Context', value: 'context' },
            { label: 'Content Part', value: 'content_part' },
            { label: 'Summary', value: 'summary' },
            { label: 'CTA', value: 'cta' },
            { label: 'Outro', value: 'outro' },
            { label: 'Disclaimer', value: 'disclaimer' },
          ],
          admin: {
            width: '50%',
          },
        },
        {
          name: 'duration',
          type: 'text',
          label: { de: 'Dauer', en: 'Duration' },
          admin: {
            width: '25%',
            placeholder: 'z.B. "2-3 Min"',
          },
        },
        {
          name: 'sectionTitle',
          type: 'text',
          label: { de: 'Section-Titel', en: 'Section Title' },
          admin: {
            width: '25%',
            placeholder: 'z.B. "TEIL 1: OUTFIT"',
            condition: (data, siblingData) => siblingData?.sectionType === 'content_part',
          },
        },
      ],
    },
    {
      name: 'spokenText',
      type: 'richText',
      label: { de: 'Gesprochener Text', en: 'Spoken Text' },
      required: true,
      localized: true,
    },
    {
      name: 'bRollInstructions',
      type: 'array',
      label: { de: 'B-Roll Anweisungen', en: 'B-Roll Instructions' },
      labels: {
        singular: { de: 'B-Roll', en: 'B-Roll' },
        plural: { de: 'B-Roll Anweisungen', en: 'B-Roll Instructions' },
      },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'instruction',
              type: 'text',
              required: true,
              label: { de: 'Anweisung', en: 'Instruction' },
              admin: {
                width: '70%',
                placeholder: 'z.B. "Outfit zeigen", "Kleiderschrank-Aufnahme"',
              },
            },
            {
              name: 'timestamp',
              type: 'text',
              label: { de: 'Zeitpunkt', en: 'Timestamp' },
              admin: {
                width: '30%',
                placeholder: 'Optional: "bei 0:45"',
              },
            },
          ],
        },
      ],
      admin: {
        initCollapsed: true,
      },
    },
    {
      name: 'textOverlays',
      type: 'array',
      label: { de: 'Text-Overlays', en: 'Text Overlays' },
      labels: {
        singular: { de: 'Overlay', en: 'Overlay' },
        plural: { de: 'Text-Overlays', en: 'Text Overlays' },
      },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'text',
              type: 'text',
              required: true,
              label: { de: 'Text', en: 'Text' },
              admin: {
                width: '70%',
                placeholder: 'z.B. "3 Kombinationen = 0 Entscheidungen"',
              },
            },
            {
              name: 'style',
              type: 'select',
              label: { de: 'Stil', en: 'Style' },
              defaultValue: 'standard',
              options: [
                { label: 'Standard', value: 'standard' },
                { label: 'Highlight', value: 'highlight' },
                { label: 'Quote', value: 'quote' },
                { label: 'Statistik', value: 'statistic' },
                { label: 'Liste', value: 'list' },
              ],
              admin: { width: '30%' },
            },
          ],
        },
      ],
      admin: {
        initCollapsed: true,
      },
    },
    {
      name: 'visualNotes',
      type: 'textarea',
      label: { de: 'Visuelle Notizen', en: 'Visual Notes' },
      admin: {
        rows: 2,
        placeholder: 'Zusätzliche Anweisungen für Schnitt/Grafik',
      },
    },
  ],
}
```

---

## Teil 2: Neue Collections

### Datei: `src/collections/YtBatches.ts`

```typescript
import type { CollectionConfig } from 'payload'
import { isYouTubeManager, isYouTubeCreatorOrAbove, hasYouTubeAccess } from '../lib/youtubeAccess'

export const YtBatches: CollectionConfig = {
  slug: 'yt-batches',
  labels: {
    singular: { de: 'Production Batch', en: 'Production Batch' },
    plural: { de: 'Production Batches', en: 'Production Batches' },
  },
  admin: {
    group: 'YouTube',
    useAsTitle: 'name',
    defaultColumns: ['name', 'channel', 'status', 'productionPeriod.start', 'updatedAt'],
    listSearchableFields: ['name'],
  },
  access: {
    read: hasYouTubeAccess,
    create: isYouTubeManager,
    update: isYouTubeCreatorOrAbove,
    delete: isYouTubeManager,
  },
  fields: [
    // === BASIC INFO ===
    {
      type: 'row',
      fields: [
        {
          name: 'name',
          type: 'text',
          required: true,
          label: { de: 'Batch-Name', en: 'Batch Name' },
          admin: {
            placeholder: 'z.B. "Januar Woche 1" oder "Batch 1"',
            width: '50%',
          },
        },
        {
          name: 'channel',
          type: 'relationship',
          relationTo: 'youtube-channels',
          required: true,
          label: { de: 'Kanal', en: 'Channel' },
          admin: {
            width: '50%',
          },
        },
      ],
    },

    // === PRODUKTIONSZEITRAUM ===
    {
      name: 'productionPeriod',
      type: 'group',
      label: { de: 'Produktionszeitraum', en: 'Production Period' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'start',
              type: 'date',
              required: true,
              label: { de: 'Start', en: 'Start' },
              admin: {
                width: '50%',
                date: {
                  pickerAppearance: 'dayOnly',
                  displayFormat: 'dd.MM.yyyy',
                },
              },
            },
            {
              name: 'end',
              type: 'date',
              required: true,
              label: { de: 'Ende', en: 'End' },
              admin: {
                width: '50%',
                date: {
                  pickerAppearance: 'dayOnly',
                  displayFormat: 'dd.MM.yyyy',
                },
              },
            },
          ],
        },
        {
          name: 'shootDays',
          type: 'array',
          label: { de: 'Drehtage', en: 'Shoot Days' },
          labels: {
            singular: { de: 'Drehtag', en: 'Shoot Day' },
            plural: { de: 'Drehtage', en: 'Shoot Days' },
          },
          fields: [
            {
              type: 'row',
              fields: [
                {
                  name: 'date',
                  type: 'date',
                  required: true,
                  label: { de: 'Datum', en: 'Date' },
                  admin: {
                    width: '25%',
                    date: { pickerAppearance: 'dayOnly' },
                  },
                },
                {
                  name: 'location',
                  type: 'select',
                  label: { de: 'Location', en: 'Location' },
                  options: [
                    { label: 'Home Studio', value: 'home' },
                    { label: 'Office', value: 'office' },
                    { label: { de: 'Unterwegs', en: 'On the Go' }, value: 'mobile' },
                    { label: 'Extern', value: 'external' },
                  ],
                  admin: { width: '25%' },
                },
                {
                  name: 'duration',
                  type: 'select',
                  label: { de: 'Dauer', en: 'Duration' },
                  options: [
                    { label: '2h', value: '2h' },
                    { label: '4h (Halbtag)', value: '4h' },
                    { label: '8h (Ganztag)', value: '8h' },
                  ],
                  admin: { width: '25%' },
                },
                {
                  name: 'notes',
                  type: 'text',
                  label: { de: 'Notizen', en: 'Notes' },
                  admin: {
                    width: '25%',
                    placeholder: 'Notizen',
                  },
                },
              ],
            },
          ],
          admin: {
            initCollapsed: true,
          },
        },
      ],
    },

    // === CONTENT TARGETS ===
    {
      name: 'targets',
      type: 'group',
      label: { de: 'Content-Ziele', en: 'Content Targets' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'shortsTarget',
              type: 'number',
              label: { de: 'Shorts (Ziel)', en: 'Shorts (Target)' },
              required: true,
              defaultValue: 7,
              min: 0,
              admin: { width: '25%' },
            },
            {
              name: 'longformsTarget',
              type: 'number',
              label: { de: 'Longforms (Ziel)', en: 'Longforms (Target)' },
              required: true,
              defaultValue: 3,
              min: 0,
              admin: { width: '25%' },
            },
            {
              name: 'totalTarget',
              type: 'number',
              label: { de: 'Gesamt', en: 'Total' },
              admin: {
                width: '25%',
                readOnly: true,
              },
            },
            {
              name: 'bufferDays',
              type: 'number',
              label: { de: 'Puffer (Tage)', en: 'Buffer (Days)' },
              defaultValue: 3,
              min: 0,
              admin: {
                width: '25%',
                description: { de: 'Tage zwischen Produktion und Publish', en: 'Days between production and publish' },
              },
            },
          ],
        },
      ],
    },

    // === SERIEN-VERTEILUNG ===
    {
      name: 'seriesDistribution',
      type: 'array',
      label: { de: 'Serien-Verteilung', en: 'Series Distribution' },
      admin: {
        description: { de: 'Welche Serien in diesem Batch produziert werden', en: 'Which series are produced in this batch' },
      },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'series',
              type: 'text',
              required: true,
              label: { de: 'Serie', en: 'Series' },
              admin: {
                width: '30%',
                placeholder: 'z.B. GRFI',
              },
            },
            {
              name: 'shortsCount',
              type: 'number',
              label: 'Shorts',
              defaultValue: 0,
              min: 0,
              admin: { width: '20%' },
            },
            {
              name: 'longformsCount',
              type: 'number',
              label: 'Longforms',
              defaultValue: 0,
              min: 0,
              admin: { width: '20%' },
            },
            {
              name: 'priority',
              type: 'select',
              label: { de: 'Priorität', en: 'Priority' },
              options: [
                { label: { de: 'Hoch', en: 'High' }, value: 'high' },
                { label: 'Normal', value: 'normal' },
                { label: { de: 'Niedrig', en: 'Low' }, value: 'low' },
              ],
              defaultValue: 'normal',
              admin: { width: '30%' },
            },
          ],
        },
      ],
    },

    // === STATUS ===
    {
      name: 'status',
      type: 'select',
      required: true,
      defaultValue: 'planning',
      label: { de: 'Status', en: 'Status' },
      options: [
        { label: { de: 'Planung', en: 'Planning' }, value: 'planning' },
        { label: 'Scripts', value: 'scripting' },
        { label: { de: 'Produktion', en: 'Production' }, value: 'production' },
        { label: { de: 'Schnitt', en: 'Editing' }, value: 'editing' },
        { label: 'Review', value: 'review' },
        { label: 'Upload-Ready', value: 'ready' },
        { label: { de: 'Veröffentlicht', en: 'Published' }, value: 'published' },
      ],
      admin: {
        position: 'sidebar',
      },
    },

    // === PROGRESS (berechnet) ===
    {
      name: 'progress',
      type: 'group',
      label: { de: 'Fortschritt', en: 'Progress' },
      admin: {
        position: 'sidebar',
      },
      fields: [
        {
          name: 'shortsCompleted',
          type: 'number',
          label: { de: 'Shorts fertig', en: 'Shorts Completed' },
          defaultValue: 0,
          admin: { readOnly: true },
        },
        {
          name: 'longformsCompleted',
          type: 'number',
          label: { de: 'Longforms fertig', en: 'Longforms Completed' },
          defaultValue: 0,
          admin: { readOnly: true },
        },
        {
          name: 'percentage',
          type: 'number',
          label: { de: 'Gesamt %', en: 'Total %' },
          defaultValue: 0,
          admin: { readOnly: true },
        },
      ],
    },

    // === TEAM ===
    {
      name: 'team',
      type: 'group',
      label: { de: 'Team', en: 'Team' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'producer',
              type: 'relationship',
              relationTo: 'users',
              label: 'Producer',
              admin: { width: '33%' },
            },
            {
              name: 'editor',
              type: 'relationship',
              relationTo: 'users',
              label: 'Editor',
              admin: { width: '33%' },
            },
            {
              name: 'reviewer',
              type: 'relationship',
              relationTo: 'users',
              label: 'Reviewer',
              admin: { width: '33%' },
            },
          ],
        },
      ],
    },

    // === NOTIZEN ===
    {
      name: 'notes',
      type: 'textarea',
      label: { de: 'Notizen', en: 'Notes' },
      localized: true,
      admin: {
        rows: 4,
      },
    },
  ],

  // === HOOKS ===
  hooks: {
    beforeChange: [
      // Total Target berechnen
      ({ data }) => {
        if (data?.targets) {
          data.targets.totalTarget = (data.targets.shortsTarget || 0) + (data.targets.longformsTarget || 0)
        }
        return data
      },
    ],
    afterRead: [
      // Progress aus verknüpften Videos berechnen
      async ({ doc, req }) => {
        if (!doc?.id) return doc

        try {
          const videos = await req.payload.find({
            collection: 'youtube-content',
            where: {
              productionBatch: { equals: doc.id },
            },
            limit: 100,
            depth: 0,
          })

          const shorts = videos.docs.filter((v: { format?: string }) => v.format === 'short')
          const longforms = videos.docs.filter((v: { format?: string }) => v.format === 'longform')

          const completedStatuses = ['approved', 'upload_scheduled', 'published', 'tracked']

          const shortsCompleted = shorts.filter((v: { status?: string }) =>
            completedStatuses.includes(v.status || '')
          ).length

          const longformsCompleted = longforms.filter((v: { status?: string }) =>
            completedStatuses.includes(v.status || '')
          ).length

          const totalTarget = (doc.targets?.shortsTarget || 0) + (doc.targets?.longformsTarget || 0)
          const totalCompleted = shortsCompleted + longformsCompleted
          const percentage = totalTarget > 0 ? Math.round((totalCompleted / totalTarget) * 100) : 0

          return {
            ...doc,
            progress: {
              shortsCompleted,
              longformsCompleted,
              percentage,
            },
          }
        } catch (error) {
          console.error('Error calculating batch progress:', error)
          return doc
        }
      },
    ],
  },
  timestamps: true,
}
```

### Datei: `src/collections/YtMonthlyGoals.ts`

```typescript
import type { CollectionConfig } from 'payload'
import { isYouTubeManager, hasYouTubeAccess } from '../lib/youtubeAccess'

export const YtMonthlyGoals: CollectionConfig = {
  slug: 'yt-monthly-goals',
  labels: {
    singular: { de: 'Monatsziel', en: 'Monthly Goal' },
    plural: { de: 'Monatsziele', en: 'Monthly Goals' },
  },
  admin: {
    group: 'YouTube',
    useAsTitle: 'displayTitle',
    defaultColumns: ['displayTitle', 'channel', 'month', 'updatedAt'],
  },
  access: {
    read: hasYouTubeAccess,
    create: isYouTubeManager,
    update: isYouTubeManager,
    delete: isYouTubeManager,
  },
  fields: [
    {
      type: 'row',
      fields: [
        {
          name: 'channel',
          type: 'relationship',
          relationTo: 'youtube-channels',
          required: true,
          label: { de: 'Kanal', en: 'Channel' },
          admin: { width: '50%' },
        },
        {
          name: 'month',
          type: 'date',
          required: true,
          label: { de: 'Monat', en: 'Month' },
          admin: {
            width: '50%',
            date: {
              pickerAppearance: 'monthOnly',
              displayFormat: 'MMMM yyyy',
            },
          },
        },
      ],
    },
    {
      name: 'displayTitle',
      type: 'text',
      admin: { hidden: true },
      hooks: {
        beforeChange: [
          async ({ siblingData, req }) => {
            if (siblingData.channel && siblingData.month) {
              try {
                const channelId = typeof siblingData.channel === 'object'
                  ? siblingData.channel.id
                  : siblingData.channel
                const channel = await req.payload.findByID({
                  collection: 'youtube-channels',
                  id: channelId,
                })
                const monthStr = new Date(siblingData.month).toLocaleDateString('de-DE', {
                  month: 'long',
                  year: 'numeric',
                })
                return `${channel?.name || 'Kanal'} - ${monthStr}`
              } catch {
                return 'Neues Monatsziel'
              }
            }
            return 'Neues Monatsziel'
          },
        ],
      },
    },

    // === CONTENT GOALS ===
    {
      name: 'contentGoals',
      type: 'group',
      label: { de: 'Content-Ziele', en: 'Content Goals' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'longformsTarget',
              type: 'number',
              label: { de: 'Longforms (Ziel)', en: 'Longforms (Target)' },
              defaultValue: 12,
              admin: { width: '25%' },
            },
            {
              name: 'longformsCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              defaultValue: 0,
              admin: { width: '25%' },
            },
            {
              name: 'shortsTarget',
              type: 'number',
              label: { de: 'Shorts (Ziel)', en: 'Shorts (Target)' },
              defaultValue: 28,
              admin: { width: '25%' },
            },
            {
              name: 'shortsCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              defaultValue: 0,
              admin: { width: '25%' },
            },
          ],
        },
      ],
    },

    // === AUDIENCE GOALS ===
    {
      name: 'audienceGoals',
      type: 'group',
      label: { de: 'Audience-Ziele', en: 'Audience Goals' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'subscribersTarget',
              type: 'number',
              label: { de: 'Neue Abos (Ziel)', en: 'New Subs (Target)' },
              admin: { width: '25%' },
            },
            {
              name: 'subscribersCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
            {
              name: 'viewsTarget',
              type: 'number',
              label: { de: 'Views (Ziel)', en: 'Views (Target)' },
              admin: { width: '25%' },
            },
            {
              name: 'viewsCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
          ],
        },
      ],
    },

    // === ENGAGEMENT GOALS ===
    {
      name: 'engagementGoals',
      type: 'group',
      label: { de: 'Engagement-Ziele', en: 'Engagement Goals' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'avgCtrTarget',
              type: 'text',
              label: { de: 'Ø CTR (Ziel)', en: 'Avg CTR (Target)' },
              admin: {
                width: '25%',
                placeholder: 'z.B. ">4%"',
              },
            },
            {
              name: 'avgCtrCurrent',
              type: 'text',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
            {
              name: 'avgRetentionTarget',
              type: 'text',
              label: { de: 'Ø Retention (Ziel)', en: 'Avg Retention (Target)' },
              admin: {
                width: '25%',
                placeholder: 'z.B. ">50%"',
              },
            },
            {
              name: 'avgRetentionCurrent',
              type: 'text',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
          ],
        },
      ],
    },

    // === BUSINESS GOALS ===
    {
      name: 'businessGoals',
      type: 'group',
      label: { de: 'Business-Ziele', en: 'Business Goals' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'newsletterSignupsTarget',
              type: 'number',
              label: { de: 'Newsletter-Anmeldungen (Ziel)', en: 'Newsletter Signups (Target)' },
              admin: { width: '25%' },
            },
            {
              name: 'newsletterSignupsCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
            {
              name: 'affiliateRevenueTarget',
              type: 'number',
              label: { de: 'Affiliate-Umsatz (Ziel)', en: 'Affiliate Revenue (Target)' },
              admin: { width: '25%' },
            },
            {
              name: 'affiliateRevenueCurrent',
              type: 'number',
              label: { de: 'Aktuell', en: 'Current' },
              admin: { width: '25%' },
            },
          ],
        },
      ],
    },

    // === CUSTOM GOALS ===
    {
      name: 'customGoals',
      type: 'array',
      label: { de: 'Weitere Ziele', en: 'Custom Goals' },
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'metric',
              type: 'text',
              required: true,
              label: { de: 'Metrik', en: 'Metric' },
              admin: {
                width: '40%',
                placeholder: 'z.B. "SPARK Videos"',
              },
            },
            {
              name: 'target',
              type: 'text',
              required: true,
              label: { de: 'Ziel', en: 'Target' },
              admin: {
                width: '20%',
                placeholder: 'Ziel',
              },
            },
            {
              name: 'current',
              type: 'text',
              label: { de: 'Aktuell', en: 'Current' },
              admin: {
                width: '20%',
                placeholder: 'Aktuell',
              },
            },
            {
              name: 'status',
              type: 'select',
              label: 'Status',
              options: [
                { label: 'On Track', value: 'on_track' },
                { label: 'At Risk', value: 'at_risk' },
                { label: 'Achieved', value: 'achieved' },
                { label: 'Missed', value: 'missed' },
              ],
              admin: { width: '20%' },
            },
          ],
        },
      ],
    },

    {
      name: 'notes',
      type: 'textarea',
      label: { de: 'Notizen / Learnings', en: 'Notes / Learnings' },
      localized: true,
    },
  ],
  timestamps: true,
}
```

### Datei: `src/collections/YtScriptTemplates.ts`

```typescript
import type { CollectionConfig } from 'payload'
import { ScriptSectionBlock } from '../blocks/ScriptSectionBlock'
import { isYouTubeManager, hasYouTubeAccess } from '../lib/youtubeAccess'

export const YtScriptTemplates: CollectionConfig = {
  slug: 'yt-script-templates',
  labels: {
    singular: { de: 'Script Template', en: 'Script Template' },
    plural: { de: 'Script Templates', en: 'Script Templates' },
  },
  admin: {
    group: 'YouTube',
    useAsTitle: 'name',
    defaultColumns: ['name', 'series', 'format', 'channel', 'updatedAt'],
  },
  access: {
    read: hasYouTubeAccess,
    create: isYouTubeManager,
    update: isYouTubeManager,
    delete: isYouTubeManager,
  },
  fields: [
    {
      type: 'row',
      fields: [
        {
          name: 'name',
          type: 'text',
          required: true,
          localized: true,
          label: { de: 'Template-Name', en: 'Template Name' },
          admin: {
            width: '50%',
            placeholder: 'z.B. "GRFI Longform Template"',
          },
        },
        {
          name: 'channel',
          type: 'relationship',
          relationTo: 'youtube-channels',
          required: true,
          label: { de: 'Kanal', en: 'Channel' },
          admin: { width: '50%' },
        },
      ],
    },
    {
      type: 'row',
      fields: [
        {
          name: 'series',
          type: 'text',
          required: true,
          label: { de: 'Serie', en: 'Series' },
          admin: {
            width: '50%',
            placeholder: 'z.B. "GRFI", "M2M", "SPARK"',
          },
        },
        {
          name: 'format',
          type: 'select',
          required: true,
          label: { de: 'Format', en: 'Format' },
          options: [
            { label: 'Short (45-60s)', value: 'short' },
            { label: 'Longform (8-16 Min)', value: 'longform' },
          ],
          admin: { width: '50%' },
        },
      ],
    },
    {
      name: 'description',
      type: 'textarea',
      label: { de: 'Beschreibung', en: 'Description' },
      localized: true,
      admin: {
        rows: 2,
        description: { de: 'Hinweise zur Verwendung dieses Templates', en: 'Usage notes for this template' },
      },
    },
    {
      name: 'templateSections',
      type: 'blocks',
      label: { de: 'Template Sections', en: 'Template Sections' },
      blocks: [ScriptSectionBlock],
    },
  ],
  timestamps: true,
}
```

### Datei: `src/collections/YtChecklistTemplates.ts`

```typescript
import type { CollectionConfig } from 'payload'
import { isYouTubeManager, hasYouTubeAccess } from '../lib/youtubeAccess'

export const YtChecklistTemplates: CollectionConfig = {
  slug: 'yt-checklist-templates',
  labels: {
    singular: { de: 'Checklisten-Vorlage', en: 'Checklist Template' },
    plural: { de: 'Checklisten-Vorlagen', en: 'Checklist Templates' },
  },
  admin: {
    group: 'YouTube',
    useAsTitle: 'name',
    defaultColumns: ['name', 'type', 'channel', 'updatedAt'],
    description: { de: 'Wiederverwendbare Checklisten für Upload und Produktion', en: 'Reusable checklists for upload and production' },
  },
  access: {
    read: hasYouTubeAccess,
    create: isYouTubeManager,
    update: isYouTubeManager,
    delete: isYouTubeManager,
  },
  fields: [
    {
      type: 'row',
      fields: [
        {
          name: 'name',
          type: 'text',
          required: true,
          localized: true,
          label: { de: 'Vorlagen-Name', en: 'Template Name' },
          admin: {
            width: '50%',
            placeholder: 'z.B. "Standard Upload Checklist"',
          },
        },
        {
          name: 'channel',
          type: 'relationship',
          relationTo: 'youtube-channels',
          label: { de: 'Kanal', en: 'Channel' },
          admin: {
            width: '50%',
            description: { de: 'Optional: Kanal-spezifische Vorlage', en: 'Optional: Channel-specific template' },
          },
        },
      ],
    },
    {
      type: 'row',
      fields: [
        {
          name: 'type',
          type: 'select',
          required: true,
          label: { de: 'Typ', en: 'Type' },
          options: [
            { label: { de: 'Upload-Checkliste', en: 'Upload Checklist' }, value: 'upload' },
            { label: { de: 'Produktions-Checkliste', en: 'Production Checklist' }, value: 'production' },
            { label: { de: 'Review-Checkliste', en: 'Review Checklist' }, value: 'review' },
            { label: { de: 'Post-Publish-Checkliste', en: 'Post-Publish Checklist' }, value: 'post_publish' },
          ],
          admin: { width: '50%' },
        },
        {
          name: 'format',
          type: 'select',
          label: { de: 'Format', en: 'Format' },
          options: [
            { label: { de: 'Alle Formate', en: 'All Formats' }, value: 'all' },
            { label: 'Short', value: 'short' },
            { label: 'Longform', value: 'longform' },
          ],
          defaultValue: 'all',
          admin: { width: '50%' },
        },
      ],
    },
    {
      name: 'description',
      type: 'textarea',
      label: { de: 'Beschreibung', en: 'Description' },
      localized: true,
      admin: {
        rows: 2,
        description: { de: 'Wann diese Checkliste verwendet werden soll', en: 'When to use this checklist' },
      },
    },
    {
      name: 'items',
      type: 'array',
      required: true,
      label: { de: 'Checklisten-Punkte', en: 'Checklist Items' },
      labels: {
        singular: { de: 'Punkt', en: 'Item' },
        plural: { de: 'Punkte', en: 'Items' },
      },
      minRows: 1,
      fields: [
        {
          type: 'row',
          fields: [
            {
              name: 'order',
              type: 'number',
              label: { de: 'Reihenfolge', en: 'Order' },
              min: 1,
              admin: { width: '15%' },
            },
            {
              name: 'task',
              type: 'text',
              required: true,
              localized: true,
              label: { de: 'Aufgabe', en: 'Task' },
              admin: {
                width: '55%',
                placeholder: 'z.B. "Thumbnail hochladen"',
              },
            },
            {
              name: 'category',
              type: 'select',
              label: { de: 'Kategorie', en: 'Category' },
              options: [
                { label: 'Metadaten', value: 'metadata' },
                { label: 'Assets', value: 'assets' },
                { label: 'SEO', value: 'seo' },
                { label: 'Community', value: 'community' },
                { label: { de: 'Rechtliches', en: 'Legal' }, value: 'legal' },
                { label: { de: 'Sonstiges', en: 'Other' }, value: 'other' },
              ],
              admin: { width: '30%' },
            },
          ],
        },
        {
          name: 'details',
          type: 'textarea',
          localized: true,
          label: { de: 'Details/Hinweise', en: 'Details/Notes' },
          admin: {
            rows: 2,
            placeholder: 'Zusätzliche Anweisungen oder Hinweise',
          },
        },
        {
          name: 'isRequired',
          type: 'checkbox',
          label: { de: 'Pflichtfeld', en: 'Required' },
          defaultValue: true,
        },
      ],
    },
    {
      name: 'isDefault',
      type: 'checkbox',
      label: { de: 'Standard-Vorlage', en: 'Default Template' },
      admin: {
        position: 'sidebar',
        description: { de: 'Wird automatisch für neue Videos verwendet', en: 'Automatically used for new videos' },
      },
    },
    {
      name: 'isActive',
      type: 'checkbox',
      label: { de: 'Aktiv', en: 'Active' },
      defaultValue: true,
      admin: {
        position: 'sidebar',
      },
    },
  ],
  timestamps: true,
}
```

---

## Teil 3: Erweiterung YouTube Content Collection

### Datei: `src/collections/YouTubeContent.ts`

Füge folgende Änderungen zur bestehenden YouTubeContent Collection hinzu:

**1. Import am Anfang der Datei hinzufügen:**

```typescript
import { ScriptSectionBlock } from '../blocks/ScriptSectionBlock'
```

**2. Neue Felder in der Sidebar (nach `createdBy` bei ca. Zeile 187):**

```typescript
    {
      name: 'productionBatch',
      type: 'relationship',
      relationTo: 'yt-batches',
      label: { de: 'Produktions-Batch', en: 'Production Batch' },
      admin: {
        position: 'sidebar',
      },
    },
```

**3. Neuen Tab "Produktion" hinzufügen (im `tabs` Array nach dem Tab "Termine"):**

```typescript
        {
          label: { de: 'Produktion', en: 'Production' },
          fields: [
            {
              type: 'row',
              fields: [
                {
                  name: 'productionWeek',
                  type: 'number',
                  label: { de: 'Produktionswoche', en: 'Production Week' },
                  min: 1,
                  max: 52,
                  admin: { width: '50%' },
                },
                {
                  name: 'calendarWeek',
                  type: 'number',
                  label: { de: 'Kalenderwoche', en: 'Calendar Week' },
                  min: 1,
                  max: 52,
                  admin: { width: '50%' },
                },
              ],
            },
            {
              name: 'productionDate',
              type: 'date',
              label: { de: 'Produktionsdatum', en: 'Production Date' },
              admin: {
                date: {
                  pickerAppearance: 'dayOnly',
                  displayFormat: 'dd.MM.yyyy',
                },
              },
            },
            {
              name: 'targetDuration',
              type: 'text',
              label: { de: 'Ziel-Dauer', en: 'Target Duration' },
              admin: {
                placeholder: 'z.B. "8-12 Min" oder "45-58s"',
              },
            },
            {
              name: 'bRollNotes',
              type: 'textarea',
              label: { de: 'B-Roll / Setting Notizen', en: 'B-Roll / Setting Notes' },
              localized: true,
              admin: {
                rows: 3,
              },
            },
          ],
        },
```

**4. Neuen Tab "Script" hinzufügen (nach dem Tab "Produktion"):**

```typescript
        {
          label: 'Script',
          fields: [
            {
              name: 'script',
              type: 'blocks',
              label: { de: 'Script Sections', en: 'Script Sections' },
              labels: {
                singular: { de: 'Section', en: 'Section' },
                plural: { de: 'Script Sections', en: 'Script Sections' },
              },
              blocks: [ScriptSectionBlock],
              admin: {
                description: { de: 'Strukturiertes Video-Script mit Sections', en: 'Structured video script with sections' },
              },
            },
          ],
        },
```

**5. Neuen Tab "Posting" hinzufügen (nach dem Tab "Script"):**

```typescript
        {
          label: 'Posting',
          fields: [
            {
              name: 'publishTime',
              type: 'text',
              label: { de: 'Posting-Uhrzeit', en: 'Publish Time' },
              admin: {
                placeholder: 'z.B. "07:00" oder "17:00"',
              },
            },
            {
              name: 'thumbnailText',
              type: 'text',
              label: { de: 'Thumbnail-Text', en: 'Thumbnail Text' },
              admin: {
                placeholder: 'z.B. "BOARD READY | 7 MIN"',
              },
            },
            {
              type: 'row',
              fields: [
                {
                  name: 'ctaType',
                  type: 'select',
                  label: { de: 'CTA-Typ', en: 'CTA Type' },
                  options: [
                    { label: 'Link in Bio', value: 'link_in_bio' },
                    { label: 'Newsletter', value: 'newsletter' },
                    { label: { de: 'Longform verlinken', en: 'Link Longform' }, value: 'longform_link' },
                    { label: { de: 'Benutzerdefiniert', en: 'Custom' }, value: 'custom' },
                  ],
                  admin: { width: '50%' },
                },
                {
                  name: 'ctaDetail',
                  type: 'text',
                  label: { de: 'CTA-Detail', en: 'CTA Detail' },
                  admin: {
                    width: '50%',
                    placeholder: 'z.B. "GRFI-Checkliste"',
                    condition: (data) => !!data?.ctaType,
                  },
                },
              ],
            },
            {
              name: 'uploadChecklist',
              type: 'array',
              label: { de: 'Upload-Checkliste', en: 'Upload Checklist' },
              fields: [
                {
                  type: 'row',
                  fields: [
                    {
                      name: 'step',
                      type: 'text',
                      required: true,
                      label: { de: 'Schritt', en: 'Step' },
                      admin: { width: '60%' },
                    },
                    {
                      name: 'completed',
                      type: 'checkbox',
                      label: { de: 'Erledigt', en: 'Done' },
                      admin: { width: '20%' },
                    },
                    {
                      name: 'completedAt',
                      type: 'date',
                      label: { de: 'Am', en: 'At' },
                      admin: {
                        width: '20%',
                        readOnly: true,
                      },
                    },
                  ],
                },
              ],
            },
            {
              name: 'disclaimers',
              type: 'array',
              label: { de: 'Disclaimers', en: 'Disclaimers' },
              fields: [
                {
                  type: 'row',
                  fields: [
                    {
                      name: 'type',
                      type: 'select',
                      required: true,
                      label: { de: 'Typ', en: 'Type' },
                      options: [
                        { label: { de: 'Medizinisch', en: 'Medical' }, value: 'medical' },
                        { label: { de: 'Rechtlich', en: 'Legal' }, value: 'legal' },
                        { label: 'Affiliate', value: 'affiliate' },
                        { label: 'Sponsored', value: 'sponsored' },
                      ],
                      admin: { width: '25%' },
                    },
                    {
                      name: 'text',
                      type: 'text',
                      label: { de: 'Text', en: 'Text' },
                      localized: true,
                      admin: { width: '50%' },
                    },
                    {
                      name: 'placement',
                      type: 'select',
                      label: { de: 'Platzierung', en: 'Placement' },
                      options: [
                        { label: { de: 'Gesprochen', en: 'Spoken' }, value: 'spoken' },
                        { label: 'Text-Overlay', value: 'overlay' },
                        { label: { de: 'Beschreibung', en: 'Description' }, value: 'description' },
                        { label: { de: 'Überall', en: 'All' }, value: 'all' },
                      ],
                      admin: { width: '25%' },
                    },
                  ],
                },
              ],
            },
          ],
        },
```

---

## Teil 4: Payload Config Update

### Datei: `src/payload.config.ts`

**1. Neue Imports hinzufügen (nach den bestehenden YouTube Imports bei ca. Zeile 75):**

```typescript
// YouTube Operations Hub Collections - Erweitert
import { YtBatches } from './collections/YtBatches'
import { YtMonthlyGoals } from './collections/YtMonthlyGoals'
import { YtScriptTemplates } from './collections/YtScriptTemplates'
import { YtChecklistTemplates } from './collections/YtChecklistTemplates'
```

**2. Collections zum Array hinzufügen (nach YtNotifications):**

```typescript
collections: [
  // ... bestehende Collections
  YtBatches,
  YtMonthlyGoals,
  YtScriptTemplates,
  YtChecklistTemplates,
],
```

---

## Teil 5: Migration erstellen

### Wichtige Hinweise zur Migration

Die Migration muss folgende Tabellen erstellen:

**Haupt-Tabellen:**
- `yt_batches`
- `yt_monthly_goals`
- `yt_script_templates`
- `yt_checklist_templates`

**Array-Tabellen (werden von Payload automatisch erstellt, aber prüfen!):**
- `yt_batches_production_period_shoot_days`
- `yt_batches_series_distribution`
- `yt_checklist_templates_items`
- `yt_monthly_goals_custom_goals`
- `yt_script_templates_template_sections`
- `youtube_content_script` (für ScriptSectionBlock)
- `youtube_content_upload_checklist`
- `youtube_content_disclaimers`

**System-Tabelle MUSS erweitert werden:**

```sql
-- KRITISCH: Diese Spalten zur System-Tabelle hinzufügen
ALTER TABLE "payload_locked_documents_rels"
  ADD COLUMN IF NOT EXISTS "yt_batches_id" integer REFERENCES yt_batches(id) ON DELETE CASCADE;

ALTER TABLE "payload_locked_documents_rels"
  ADD COLUMN IF NOT EXISTS "yt_monthly_goals_id" integer REFERENCES yt_monthly_goals(id) ON DELETE CASCADE;

ALTER TABLE "payload_locked_documents_rels"
  ADD COLUMN IF NOT EXISTS "yt_script_templates_id" integer REFERENCES yt_script_templates(id) ON DELETE CASCADE;

ALTER TABLE "payload_locked_documents_rels"
  ADD COLUMN IF NOT EXISTS "yt_checklist_templates_id" integer REFERENCES yt_checklist_templates(id) ON DELETE CASCADE;

-- Indexes
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_batches_idx"
  ON "payload_locked_documents_rels" ("yt_batches_id");

CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_monthly_goals_idx"
  ON "payload_locked_documents_rels" ("yt_monthly_goals_id");

CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_script_templates_idx"
  ON "payload_locked_documents_rels" ("yt_script_templates_id");

CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_checklist_templates_idx"
  ON "payload_locked_documents_rels" ("yt_checklist_templates_id");
```

### Migration erstellen und ausführen

```bash
# 1. Migration erstellen
pnpm payload migrate:create youtube_ops_v2

# 2. Migration-Datei prüfen und ggf. anpassen
# Datei: src/migrations/[timestamp]_youtube_ops_v2.ts

# 3. Migration ausführen (direkte DB-Verbindung empfohlen)
./scripts/db-direct.sh migrate

# 4. Build erstellen
pnpm build

# 5. PM2 neu starten
pm2 restart payload
```

---

## Teil 6: Seed-Daten (BlogWoman Januar) - Optional

### Datei: `src/seed/blogwoman-januar.ts`

```typescript
import type { Payload } from 'payload'

export async function seedBlogWomanJanuar(payload: Payload) {
  console.log('Seeding BlogWoman Januar Content...')

  // 1. BlogWoman Kanal finden oder erstellen
  const channels = await payload.find({
    collection: 'youtube-channels',
    where: { slug: { equals: 'blogwoman' } },
    limit: 1,
  })

  let blogwomanChannel = channels.docs[0]

  if (!blogwomanChannel) {
    // Kanal erstellen (mit allen Pflichtfeldern!)
    blogwomanChannel = await payload.create({
      collection: 'youtube-channels',
      data: {
        name: 'BlogWoman by Caroline Porwoll',
        slug: 'blogwoman',
        youtubeChannelId: 'UCxxxxxxxxxxxxxxxxxx', // PFLICHT - echte ID eintragen!
        youtubeHandle: '@blogwoman',
        language: 'de',
        category: 'lifestyle',
        status: 'active',
        contentSeries: [
          { name: 'GRFI', slug: 'grfi', description: 'Get Ready For Impact', color: '#3B82F6', isActive: true },
          { name: 'Investment-Piece', slug: 'investment', description: 'Cost-per-Wear Analysen', color: '#8B5CF6', isActive: true },
          { name: 'Pleasure P&L', slug: 'pleasure-pl', description: 'ROI auf Genuss', color: '#EC4899', isActive: true },
          { name: 'M2M', slug: 'm2m', description: 'Meeting to Mom Mode', color: '#F59E0B', isActive: true },
          { name: 'SPARK', slug: 'spark', description: 'Die Flamme', color: '#EF4444', isActive: true },
          { name: 'Regeneration', slug: 'regeneration', description: 'Energie ohne Kitsch', color: '#10B981', isActive: true },
          { name: 'Decision-Proof', slug: 'decision-proof', description: 'Regeln statt Willenskraft', color: '#6366F1', isActive: true },
          { name: 'Sunday Reset', slug: 'sunday-reset', description: 'Wochenplanung', color: '#14B8A6', isActive: true },
        ],
        publishingSchedule: {
          defaultDays: ['sunday', 'wednesday', 'saturday'],
          defaultTime: '07:00',
          shortsPerWeek: 7,
          longformPerWeek: 3,
        },
      },
    })
    console.log('BlogWoman Kanal erstellt')
  }

  // 2. Januar Batch erstellen
  const januarBatch1 = await payload.create({
    collection: 'yt-batches',
    data: {
      name: 'Januar Woche 1',
      channel: blogwomanChannel.id,
      productionPeriod: {
        start: '2026-01-06',
        end: '2026-01-10',
      },
      shootDays: [
        { date: '2026-01-06', location: 'home', duration: '4h', notes: 'Longforms + Shorts' },
        { date: '2026-01-08', location: 'home', duration: '4h', notes: 'SPARK + M2M' },
      ],
      targets: {
        shortsTarget: 7,
        longformsTarget: 3,
        bufferDays: 3,
      },
      seriesDistribution: [
        { series: 'GRFI', shortsCount: 2, longformsCount: 1, priority: 'high' },
        { series: 'Investment-Piece', shortsCount: 1, longformsCount: 1, priority: 'high' },
        { series: 'SPARK', shortsCount: 1, longformsCount: 1, priority: 'high' },
        { series: 'M2M', shortsCount: 1, longformsCount: 0, priority: 'normal' },
        { series: 'Regeneration', shortsCount: 1, longformsCount: 0, priority: 'normal' },
        { series: 'Decision-Proof', shortsCount: 1, longformsCount: 0, priority: 'normal' },
      ],
      status: 'planning',
    },
  })
  console.log('Januar Batch 1 erstellt')

  // 3. Beispiel-Content erstellen
  const exampleVideos = [
    {
      title: '7 Minuten: Boardroom-ready (mein komplettes System)',
      contentSeries: 'grfi',
      format: 'longform' as const,
      status: 'idea' as const,
      productionBatch: januarBatch1.id,
      productionDate: '2026-01-06',
      scheduledPublishDate: '2026-01-12',
      publishTime: '10:00',
      targetDuration: '8-12 Min',
      hook: 'Board-Meeting in 20 Minuten. Der Morgen war Chaos, die Kinder waren anstrengend, der Kaffee kalt. Aber das Meeting muss sitzen. So werde ich in 7 Minuten ready.',
      thumbnailText: 'BOARD READY | 7 MIN',
      ctaType: 'newsletter' as const,
      ctaDetail: 'GRFI-Checkliste',
      bRollNotes: 'Kleiderschrank, Spiegel, Badezimmer, 3 Outfit-Kombinationen zeigen',
    },
    {
      title: '200 Euro Blazer: Cost-per-Wear nach 2 Jahren',
      contentSeries: 'investment',
      format: 'longform' as const,
      status: 'idea' as const,
      productionBatch: januarBatch1.id,
      productionDate: '2026-01-07',
      scheduledPublishDate: '2026-01-15',
      publishTime: '17:00',
      targetDuration: '10-14 Min',
      hook: '200 Euro für einen Blazer. Klingt viel? Ich hab die Rechnung gemacht. Nach 2 Jahren. Mit echten Zahlen.',
      thumbnailText: '200 EURO | DIE RECHNUNG',
      ctaType: 'newsletter' as const,
      ctaDetail: 'CPW-Rechner',
      bRollNotes: 'Blazer in 3 Kontexten: Board-Meeting, Office, Abend',
    },
    {
      title: 'Call endet in 5 Min - Kita-Abholung in 20',
      contentSeries: 'm2m',
      format: 'short' as const,
      status: 'idea' as const,
      productionBatch: januarBatch1.id,
      productionDate: '2026-01-06',
      scheduledPublishDate: '2026-01-13',
      publishTime: '07:00',
      targetDuration: '45-58s',
      hook: 'Video-Call endet in 5 Minuten. Kita-Abholung in 20. So switch ich.',
      thumbnailText: 'BUSINESS zu MAMA',
      ctaType: 'link_in_bio' as const,
      bRollNotes: 'Business zu Casual Transformation',
    },
  ]

  for (const video of exampleVideos) {
    await payload.create({
      collection: 'youtube-content',
      data: {
        ...video,
        channel: blogwomanChannel.id,
        priority: 'normal',
        slug: video.title
          .toLowerCase()
          .replace(/[äöüß]/g, (char: string) => {
            const map: Record<string, string> = { ä: 'ae', ö: 'oe', ü: 'ue', ß: 'ss' }
            return map[char] || char
          })
          .replace(/[^a-z0-9]+/g, '-')
          .replace(/^-|-$/g, ''),
      },
    })
  }
  console.log(`${exampleVideos.length} Beispiel-Videos erstellt`)

  // 4. Monatsziele erstellen
  await payload.create({
    collection: 'yt-monthly-goals',
    data: {
      channel: blogwomanChannel.id,
      month: '2026-01-01',
      contentGoals: {
        longformsTarget: 12,
        longformsCurrent: 0,
        shortsTarget: 28,
        shortsCurrent: 0,
      },
      audienceGoals: {
        subscribersTarget: 500,
        subscribersCurrent: 0,
        viewsTarget: 50000,
        viewsCurrent: 0,
      },
      engagementGoals: {
        avgCtrTarget: '>4%',
        avgRetentionTarget: '>50%',
      },
      businessGoals: {
        newsletterSignupsTarget: 500,
        newsletterSignupsCurrent: 0,
      },
      customGoals: [
        { metric: 'SPARK Videos', target: '6', current: '0', status: 'on_track' },
        { metric: 'Shorts Retention', target: '>60%', status: 'on_track' },
      ],
    },
  })
  console.log('Januar Monatsziele erstellt')

  console.log('BlogWoman Januar Seeding abgeschlossen!')
}
```

---

## Ausführungsreihenfolge

1. **Block erstellen:** `src/blocks/ScriptSectionBlock.ts`
2. **Collections erstellen:**
   - `src/collections/YtBatches.ts`
   - `src/collections/YtMonthlyGoals.ts`
   - `src/collections/YtScriptTemplates.ts`
3. **YouTubeContent.ts erweitern:** Import + neue Felder/Tabs
4. **payload.config.ts aktualisieren:** Imports + Collections Array
5. **Migration erstellen:** `pnpm payload migrate:create youtube_ops_v2`
6. **Migration prüfen und anpassen:**
   - Array-Tabellen vorhanden?
   - `payload_locked_documents_rels` erweitert?
7. **Migration ausführen:** `./scripts/db-direct.sh migrate`
8. **Build:** `pnpm build`
9. **PM2 neustarten:** `pm2 restart payload`
10. **Testen:** Collections im Admin Panel prüfen

---

## Testschritte

1. [ ] Server starten und Admin Panel öffnen
2. [ ] Neue Collections unter "YouTube" sichtbar:
   - Production Batches
   - Monatsziele
   - Script Templates
   - Checklisten-Vorlagen
3. [ ] Production Batch erstellen und mit Kanal verknüpfen
4. [ ] Checklisten-Vorlage erstellen (z.B. "Standard Upload Checklist")
5. [ ] YouTube-Video erstellen mit:
   - Production Batch Zuweisung
   - Script Sections
   - Upload-Checkliste
6. [ ] Monthly Goal für aktuellen Monat erstellen
6. [ ] Script Template erstellen und bei Video verwenden

---

## Bekannte Einschränkungen

- **Custom Admin Views:** Kalender-Ansicht nicht möglich wegen path-to-regexp Bug in Payload 3.x
- **SCSS:** Payload 3.x verwendet CSS Modules, keine SCSS-Kompilierung
- **PgBouncer:** Für Migrationen `./scripts/db-direct.sh` verwenden (umgeht Transaction-Mode)

---

## Troubleshooting

**"relation does not exist" Fehler:**
- Migration wurde nicht korrekt ausgeführt
- Array-Tabellen fehlen
- `payload_locked_documents_rels` nicht erweitert
- Lösung: Migration-Datei prüfen und erneut ausführen

**TypeScript-Fehler:**
```bash
pnpm payload generate:types
```

**Collection nicht sichtbar:**
- Import in `payload.config.ts` prüfen
- Collection im `collections` Array vorhanden?
- Build neu erstellen: `pnpm build`

**Hook-Fehler bei Progress-Berechnung:**
- `productionBatch` Feld in YouTubeContent vorhanden?
- Migration für neue Felder ausgeführt?
