mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
Adds channelThumbnailUrl field to store YouTube API URL. afterChange hook downloads image to Payload Media when branding.logo is empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
232 lines
6.2 KiB
TypeScript
232 lines
6.2 KiB
TypeScript
// src/collections/YouTubeChannels.ts
|
|
|
|
import type { CollectionConfig } from 'payload'
|
|
import { isYouTubeManager, hasYouTubeAccess } from '../lib/youtubeAccess'
|
|
import { downloadChannelImage } from '../hooks/youtubeChannels/downloadChannelImage'
|
|
|
|
/**
|
|
* YouTubeChannels Collection
|
|
*
|
|
* Verwaltet YouTube-Kanäle mit Branding, Content-Serien und Metriken.
|
|
* Teil des YouTube Operations Hub.
|
|
*/
|
|
export const YouTubeChannels: CollectionConfig = {
|
|
slug: 'youtube-channels',
|
|
labels: {
|
|
singular: 'YouTube-Kanal',
|
|
plural: 'YouTube-Kanäle',
|
|
},
|
|
admin: {
|
|
useAsTitle: 'name',
|
|
group: 'YouTube',
|
|
defaultColumns: ['name', 'youtubeHandle', 'status', 'language'],
|
|
description: 'YouTube-Kanäle und ihre Konfiguration',
|
|
},
|
|
access: {
|
|
read: hasYouTubeAccess,
|
|
create: isYouTubeManager,
|
|
update: isYouTubeManager,
|
|
delete: isYouTubeManager,
|
|
},
|
|
hooks: {
|
|
afterChange: [downloadChannelImage],
|
|
},
|
|
fields: [
|
|
{
|
|
name: 'name',
|
|
type: 'text',
|
|
required: true,
|
|
localized: true,
|
|
label: 'Kanalname',
|
|
admin: {
|
|
description: 'z.B. "BlogWoman by Caroline Porwoll"',
|
|
},
|
|
},
|
|
{
|
|
name: 'slug',
|
|
type: 'text',
|
|
required: true,
|
|
unique: true,
|
|
label: 'Slug',
|
|
admin: {
|
|
description: 'Interner Kurzname (z.B. "blogwoman", "corporate-de")',
|
|
},
|
|
},
|
|
{
|
|
name: 'youtubeChannelId',
|
|
type: 'text',
|
|
required: true,
|
|
label: 'YouTube Channel ID',
|
|
admin: {
|
|
description: 'Die YouTube Channel ID (z.B. "UCxxxxxxxxxxxxx")',
|
|
},
|
|
},
|
|
{
|
|
name: 'youtubeHandle',
|
|
type: 'text',
|
|
label: 'YouTube Handle',
|
|
admin: {
|
|
description: 'z.B. "@blogwoman" oder "@zweitmeinu.ng"',
|
|
},
|
|
},
|
|
{
|
|
name: 'language',
|
|
type: 'select',
|
|
required: true,
|
|
label: 'Sprache',
|
|
options: [
|
|
{ label: 'Deutsch', value: 'de' },
|
|
{ label: 'Englisch', value: 'en' },
|
|
],
|
|
admin: {
|
|
position: 'sidebar',
|
|
},
|
|
},
|
|
{
|
|
name: 'category',
|
|
type: 'select',
|
|
required: true,
|
|
label: 'Kategorie',
|
|
options: [
|
|
{ label: 'Lifestyle', value: 'lifestyle' },
|
|
{ label: 'Corporate', value: 'corporate' },
|
|
{ label: 'Business B2B', value: 'b2b' },
|
|
],
|
|
admin: {
|
|
position: 'sidebar',
|
|
},
|
|
},
|
|
{
|
|
name: 'status',
|
|
type: 'select',
|
|
required: true,
|
|
defaultValue: 'active',
|
|
label: 'Status',
|
|
options: [
|
|
{ label: 'Aktiv', value: 'active' },
|
|
{ label: 'In Planung', value: 'planned' },
|
|
{ label: 'Pausiert', value: 'paused' },
|
|
{ label: 'Archiviert', value: 'archived' },
|
|
],
|
|
admin: {
|
|
position: 'sidebar',
|
|
},
|
|
},
|
|
{
|
|
name: 'channelThumbnailUrl',
|
|
type: 'text',
|
|
label: 'Kanal-Thumbnail URL',
|
|
admin: {
|
|
readOnly: true,
|
|
description: 'Profil-Thumbnail URL von YouTube (automatisch befüllt)',
|
|
position: 'sidebar',
|
|
},
|
|
},
|
|
// Branding
|
|
{
|
|
name: 'branding',
|
|
type: 'group',
|
|
label: 'Branding',
|
|
fields: [
|
|
{
|
|
name: 'primaryColor',
|
|
type: 'text',
|
|
label: 'Primärfarbe (Hex)',
|
|
admin: { description: 'z.B. #1278B3' },
|
|
validate: (value: string | undefined | null) => {
|
|
if (!value) return true
|
|
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/
|
|
if (!hexRegex.test(value)) {
|
|
return 'Bitte einen gültigen Hex-Farbcode eingeben (z.B. #1278B3)'
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
{
|
|
name: 'secondaryColor',
|
|
type: 'text',
|
|
label: 'Sekundärfarbe (Hex)',
|
|
validate: (value: string | undefined | null) => {
|
|
if (!value) return true
|
|
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/
|
|
if (!hexRegex.test(value)) {
|
|
return 'Bitte einen gültigen Hex-Farbcode eingeben'
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
{
|
|
name: 'logo',
|
|
type: 'upload',
|
|
relationTo: 'media',
|
|
label: 'Logo',
|
|
},
|
|
{
|
|
name: 'thumbnailTemplate',
|
|
type: 'upload',
|
|
relationTo: 'media',
|
|
label: 'Thumbnail-Vorlage',
|
|
},
|
|
],
|
|
},
|
|
// Content-Serien sind jetzt in der YtSeries Collection verwaltet
|
|
// Siehe: YouTube → YouTube-Serien
|
|
// Veröffentlichungsplan
|
|
{
|
|
name: 'publishingSchedule',
|
|
type: 'group',
|
|
label: 'Veröffentlichungsplan',
|
|
fields: [
|
|
{
|
|
name: 'defaultDays',
|
|
type: 'select',
|
|
hasMany: true,
|
|
label: 'Standard-Tage',
|
|
options: [
|
|
{ label: 'Montag', value: 'monday' },
|
|
{ label: 'Dienstag', value: 'tuesday' },
|
|
{ label: 'Mittwoch', value: 'wednesday' },
|
|
{ label: 'Donnerstag', value: 'thursday' },
|
|
{ label: 'Freitag', value: 'friday' },
|
|
{ label: 'Samstag', value: 'saturday' },
|
|
{ label: 'Sonntag', value: 'sunday' },
|
|
],
|
|
},
|
|
{
|
|
name: 'defaultTime',
|
|
type: 'text',
|
|
label: 'Standard-Uhrzeit',
|
|
admin: { description: 'z.B. "12:00"' },
|
|
},
|
|
{
|
|
name: 'shortsPerWeek',
|
|
type: 'number',
|
|
defaultValue: 4,
|
|
label: 'Shorts pro Woche',
|
|
},
|
|
{
|
|
name: 'longformPerWeek',
|
|
type: 'number',
|
|
defaultValue: 1,
|
|
label: 'Longform pro Woche',
|
|
},
|
|
],
|
|
},
|
|
// Metriken (via YouTube API Sync)
|
|
{
|
|
name: 'currentMetrics',
|
|
type: 'group',
|
|
label: 'Aktuelle Metriken',
|
|
admin: {
|
|
description: 'Automatisch via YouTube API aktualisiert',
|
|
},
|
|
fields: [
|
|
{ name: 'subscriberCount', type: 'number', label: 'Abonnenten', admin: { readOnly: true } },
|
|
{ name: 'totalViews', type: 'number', label: 'Gesamtaufrufe', admin: { readOnly: true } },
|
|
{ name: 'videoCount', type: 'number', label: 'Anzahl Videos', admin: { readOnly: true } },
|
|
{ name: 'lastSyncedAt', type: 'date', label: 'Letzter Sync', admin: { readOnly: true } },
|
|
],
|
|
},
|
|
],
|
|
timestamps: true,
|
|
}
|