// src/collections/CommunityInteractions.ts import type { CollectionConfig } from 'payload' import { isCommunityModeratorOrAbove, isCommunityManager, canAccessAssignedInteractions, } from '../lib/communityAccess' /** * CommunityInteractions Collection * * Speichert alle Social Media Interaktionen (Kommentare, DMs, Mentions). * Teil des Community Management Systems. */ export const CommunityInteractions: CollectionConfig = { slug: 'community-interactions', labels: { singular: 'Interaction', plural: 'Interactions', }, admin: { group: 'Community', defaultColumns: ['platform', 'type', 'author.name', 'status', 'priority', 'createdAt'], listSearchableFields: ['author.name', 'author.handle', 'message'], pagination: { defaultLimit: 50, }, }, access: { read: canAccessAssignedInteractions, create: isCommunityModeratorOrAbove, update: canAccessAssignedInteractions, delete: isCommunityManager, }, fields: [ // === SOURCE === { type: 'row', fields: [ { name: 'platform', type: 'relationship', relationTo: 'social-platforms', required: true, label: 'Plattform', admin: { width: '33%' }, }, { name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts', required: true, label: 'Account', admin: { width: '33%' }, }, { name: 'linkedContent', type: 'relationship', relationTo: 'youtube-content', label: 'Verknüpfter Content', admin: { width: '33%' }, }, ], }, // === INTERACTION TYPE === { type: 'row', fields: [ { name: 'type', type: 'select', required: true, label: 'Typ', options: [ { label: 'Kommentar', value: 'comment' }, { label: 'Antwort', value: 'reply' }, { label: 'Direktnachricht', value: 'dm' }, { label: 'Erwähnung', value: 'mention' }, { label: 'Bewertung', value: 'review' }, { label: 'Frage', value: 'question' }, ], admin: { width: '50%' }, }, { name: 'externalId', type: 'text', label: 'External ID', required: true, unique: true, index: true, admin: { width: '50%', description: 'YouTube Comment ID, etc.', }, }, ], }, // === PARENT (für Threads) === { name: 'parentInteraction', type: 'relationship', relationTo: 'community-interactions', label: 'Parent (bei Replies)', admin: { condition: (data) => data?.type === 'reply', }, }, // === AUTHOR INFO === { name: 'author', type: 'group', label: 'Author', fields: [ { type: 'row', fields: [ { name: 'name', type: 'text', label: 'Name', admin: { width: '50%' }, }, { name: 'handle', type: 'text', label: 'Handle', admin: { width: '50%' }, }, ], }, { type: 'row', fields: [ { name: 'profileUrl', type: 'text', label: 'Profile URL', admin: { width: '50%' }, }, { name: 'avatarUrl', type: 'text', label: 'Avatar URL', admin: { width: '50%' }, }, ], }, { type: 'row', fields: [ { name: 'isVerified', type: 'checkbox', label: 'Verifiziert', admin: { width: '25%' }, }, { name: 'isSubscriber', type: 'checkbox', label: 'Subscriber/Follower', admin: { width: '25%' }, }, { name: 'isMember', type: 'checkbox', label: 'Channel Member', admin: { width: '25%' }, }, { name: 'subscriberCount', type: 'number', label: 'Ihre Subscriber', admin: { width: '25%' }, }, ], }, ], }, // === MESSAGE CONTENT === { name: 'message', type: 'textarea', label: 'Nachricht', required: true, admin: { rows: 4, }, }, { name: 'messageHtml', type: 'textarea', label: 'Original HTML', admin: { rows: 2, description: 'Falls Plattform HTML liefert', }, }, { name: 'attachments', type: 'array', label: 'Attachments', fields: [ { type: 'row', fields: [ { name: 'type', type: 'select', label: 'Typ', options: [ { label: 'Bild', value: 'image' }, { label: 'Video', value: 'video' }, { label: 'Link', value: 'link' }, { label: 'Sticker', value: 'sticker' }, ], admin: { width: '30%' }, }, { name: 'url', type: 'text', label: 'URL', admin: { width: '70%' }, }, ], }, ], }, { name: 'publishedAt', type: 'date', label: 'Veröffentlicht am', required: true, admin: { date: { pickerAppearance: 'dayAndTime', }, }, }, // === AI ANALYSIS === { name: 'analysis', type: 'group', label: 'AI Analyse', admin: { description: 'Automatisch via Claude API', }, fields: [ { type: 'row', fields: [ { name: 'sentiment', type: 'select', label: 'Sentiment', options: [ { label: 'Positiv', value: 'positive' }, { label: 'Neutral', value: 'neutral' }, { label: 'Negativ', value: 'negative' }, { label: 'Frage', value: 'question' }, { label: 'Dankbarkeit', value: 'gratitude' }, { label: 'Frustration', value: 'frustration' }, ], admin: { width: '33%' }, }, { name: 'sentimentScore', type: 'number', label: 'Score (-1 bis 1)', min: -1, max: 1, admin: { width: '33%' }, }, { name: 'confidence', type: 'number', label: 'Confidence %', min: 0, max: 100, admin: { width: '33%' }, }, ], }, { name: 'topics', type: 'array', label: 'Erkannte Themen', fields: [ { name: 'topic', type: 'text', label: 'Thema', }, ], }, { name: 'language', type: 'text', label: 'Sprache', admin: { placeholder: 'de', }, }, { name: 'suggestedTemplate', type: 'relationship', relationTo: 'community-templates', label: 'Vorgeschlagenes Template', }, { name: 'suggestedReply', type: 'textarea', label: 'AI-generierter Antwortvorschlag', }, { name: 'analyzedAt', type: 'date', label: 'Analysiert am', }, ], }, // === FLAGS === { name: 'flags', type: 'group', label: 'Flags', fields: [ { type: 'row', fields: [ { name: 'isMedicalQuestion', type: 'checkbox', label: 'Medizinische Frage', admin: { width: '25%', description: 'Erfordert ärztliche Review', }, }, { name: 'requiresEscalation', type: 'checkbox', label: 'Eskalation nötig', admin: { width: '25%' }, }, { name: 'isSpam', type: 'checkbox', label: 'Spam', admin: { width: '25%' }, }, { name: 'isFromInfluencer', type: 'checkbox', label: 'Influencer', admin: { width: '25%', description: '>10k Follower', }, }, ], }, ], }, // === WORKFLOW === { name: 'status', type: 'select', required: true, defaultValue: 'new', index: true, label: 'Status', options: [ { label: 'Neu', value: 'new' }, { label: 'In Review', value: 'in_review' }, { label: 'Warten auf Info', value: 'waiting' }, { label: 'Beantwortet', value: 'replied' }, { label: 'Erledigt', value: 'resolved' }, { label: 'Archiviert', value: 'archived' }, { label: 'Spam', value: 'spam' }, ], admin: { position: 'sidebar', }, }, { name: 'priority', type: 'select', required: true, defaultValue: 'normal', index: true, label: 'Priorität', options: [ { label: 'Urgent', value: 'urgent' }, { label: 'Hoch', value: 'high' }, { label: 'Normal', value: 'normal' }, { label: 'Niedrig', value: 'low' }, ], admin: { position: 'sidebar', }, }, { name: 'assignedTo', type: 'relationship', relationTo: 'users', label: 'Zugewiesen an', admin: { position: 'sidebar', }, }, { name: 'responseDeadline', type: 'date', label: 'Antwort-Deadline', admin: { position: 'sidebar', date: { pickerAppearance: 'dayAndTime', }, }, }, // === OUR RESPONSE === { name: 'response', type: 'group', label: 'Unsere Antwort', fields: [ { name: 'text', type: 'textarea', label: 'Antwort-Text', admin: { rows: 4 }, }, { name: 'usedTemplate', type: 'relationship', relationTo: 'community-templates', label: 'Verwendetes Template', }, { name: 'sentAt', type: 'date', label: 'Gesendet am', }, { name: 'sentBy', type: 'relationship', relationTo: 'users', label: 'Gesendet von', }, { name: 'externalReplyId', type: 'text', label: 'Reply ID (extern)', }, ], }, // === ENGAGEMENT (Platform-spezifisch) === { name: 'engagement', type: 'group', label: 'Engagement', admin: { description: 'Wird beim Sync aktualisiert', }, fields: [ { type: 'row', fields: [ { name: 'likes', type: 'number', label: 'Likes', admin: { width: '25%' }, }, { name: 'replies', type: 'number', label: 'Replies', admin: { width: '25%' }, }, { name: 'isHearted', type: 'checkbox', label: 'Creator Heart', admin: { width: '25%' }, }, { name: 'isPinned', type: 'checkbox', label: 'Angepinnt', admin: { width: '25%' }, }, ], }, ], }, // === INTERNAL NOTES === { name: 'internalNotes', type: 'textarea', label: 'Interne Notizen', admin: { rows: 2, description: 'Nur für Team sichtbar', }, }, ], // === HOOKS === hooks: { beforeChange: [ // Auto-set priority based on flags async ({ data, operation }) => { if (!data) return data if (operation === 'create' || !data.priority) { if (data?.flags?.isMedicalQuestion) { data.priority = 'high' } if (data?.flags?.requiresEscalation) { data.priority = 'urgent' } if (data?.flags?.isFromInfluencer) { data.priority = data.priority === 'urgent' ? 'urgent' : 'high' } } return data }, ], afterChange: [ // Send notification for urgent items async ({ doc, operation }) => { if (operation === 'create' && doc.priority === 'urgent') { // TODO: Notification Logic via YtNotifications console.log(`🚨 Urgent interaction: ${doc.id}`) } }, // Run Rules Engine on new interactions async ({ doc, operation, req }) => { if (operation === 'create') { try { const { RulesEngine } = await import('@/lib/services/RulesEngine') const rulesEngine = new RulesEngine(req.payload) const result = await rulesEngine.evaluateRules(doc.id) if (result.appliedRules.length > 0) { console.log(`[RulesEngine] Applied ${result.appliedRules.length} rules to interaction ${doc.id}:`, result.appliedRules) } } catch (error) { console.error('[RulesEngine] Error evaluating rules:', error) } } }, ], }, timestamps: true, }