// src/collections/YtTasks.ts import type { CollectionConfig } from 'payload' import { isYouTubeManager, canAccessAssignedContent } from '../lib/youtubeAccess' import { notifyOnAssignment } from '../hooks/ytTasks/notifyOnAssignment' /** * YtTasks Collection * * Aufgabenverwaltung für die Video-Produktion. * Tasks werden automatisch bei Statuswechsel erstellt. * Teil des YouTube Operations Hub. */ export const YtTasks: CollectionConfig = { slug: 'yt-tasks', labels: { singular: 'YouTube-Aufgabe', plural: 'YouTube-Aufgaben', }, admin: { useAsTitle: 'title', group: 'YouTube', defaultColumns: ['title', 'video', 'assignedTo', 'status', 'dueDate', 'priority'], listSearchableFields: ['title'], description: 'Aufgaben für die Video-Produktion', }, access: { read: canAccessAssignedContent, create: isYouTubeManager, update: canAccessAssignedContent, delete: isYouTubeManager, }, fields: [ { name: 'title', type: 'text', required: true, localized: true, label: 'Aufgabe', }, { name: 'description', type: 'textarea', localized: true, label: 'Beschreibung', }, { name: 'video', type: 'relationship', relationTo: 'youtube-content', label: 'Zugehöriges Video', admin: { position: 'sidebar', }, }, { name: 'channel', type: 'relationship', relationTo: 'youtube-channels', label: 'Kanal', admin: { position: 'sidebar', description: 'Wird automatisch vom Video übernommen', }, }, { name: 'taskType', type: 'select', required: true, label: 'Aufgabentyp', options: [ { label: 'Skript schreiben', value: 'script_write' }, { label: 'Skript reviewen', value: 'script_review' }, { label: 'Dreh vorbereiten', value: 'shoot_prep' }, { label: 'Drehen', value: 'shoot' }, { label: 'Schneiden', value: 'edit' }, { label: 'Grafiken erstellen', value: 'graphics' }, { label: 'Thumbnail erstellen', value: 'thumbnail' }, { label: 'Review/Freigabe', value: 'review' }, { label: 'Hochladen', value: 'upload' }, { label: 'Performance tracken', value: 'track' }, { label: 'Kommentare beantworten', value: 'comments' }, { label: 'Sonstiges', value: 'other' }, ], admin: { position: 'sidebar', }, }, { name: 'status', type: 'select', required: true, defaultValue: 'todo', label: 'Status', options: [ { label: 'Offen', value: 'todo' }, { label: 'In Arbeit', value: 'in_progress' }, { label: 'Blockiert', value: 'blocked' }, { label: 'Wartet auf Review', value: 'waiting_review' }, { label: 'Erledigt', value: 'done' }, { label: 'Abgebrochen', value: 'cancelled' }, ], admin: { position: 'sidebar', }, }, { name: 'priority', type: 'select', defaultValue: 'normal', label: 'Priorität', options: [ { label: 'Dringend', value: 'urgent' }, { label: 'Hoch', value: 'high' }, { label: 'Normal', value: 'normal' }, { label: 'Niedrig', value: 'low' }, ], admin: { position: 'sidebar', }, }, { name: 'assignedTo', type: 'relationship', relationTo: 'users', required: true, label: 'Zugewiesen an', admin: { position: 'sidebar', }, }, { name: 'dueDate', type: 'date', label: 'Fälligkeitsdatum', admin: { position: 'sidebar', date: { pickerAppearance: 'dayAndTime' }, }, }, { name: 'completedAt', type: 'date', label: 'Abgeschlossen am', admin: { readOnly: true, position: 'sidebar', }, }, { name: 'completedBy', type: 'relationship', relationTo: 'users', label: 'Abgeschlossen von', admin: { readOnly: true }, }, { name: 'blockedReason', type: 'text', localized: true, label: 'Grund für Blockierung', admin: { condition: (data) => data?.status === 'blocked', }, }, { name: 'attachments', type: 'array', label: 'Anhänge', fields: [ { name: 'file', type: 'upload', relationTo: 'media', label: 'Datei' }, { name: 'note', type: 'text', label: 'Notiz' }, ], }, { name: 'comments', type: 'array', label: 'Kommentare', fields: [ { name: 'author', type: 'relationship', relationTo: 'users', label: 'Autor' }, { name: 'content', type: 'textarea', label: 'Inhalt' }, { name: 'createdAt', type: 'date', label: 'Erstellt am' }, ], }, ], timestamps: true, hooks: { afterChange: [notifyOnAssignment], beforeChange: [ async ({ data, originalDoc, req }) => { if (!data) return data // Setze completedAt wenn Status auf "done" wechselt if (data.status === 'done' && originalDoc?.status !== 'done') { data.completedAt = new Date().toISOString() data.completedBy = req.user?.id } // Setze Channel automatisch vom Video if (data.video && !data.channel) { try { const video = await req.payload.findByID({ collection: 'youtube-content', id: typeof data.video === 'object' ? data.video.id : data.video, depth: 0, }) if (video?.channel) { data.channel = typeof video.channel === 'object' ? video.channel.id : video.channel } } catch (error) { console.error('[YtTasks] Error fetching video for channel:', error) } } return data }, ], }, }