mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 22:04:10 +00:00
- Add .claude/ configuration (agents, commands, hooks, get-shit-done workflows) - Add prompts/ directory with development planning documents - Add scripts/setup-tenants/ with tenant configuration - Add docs/screenshots/ - Remove obsolete phase2.2-corrections-report.md - Update pnpm-lock.yaml - Update detect-secrets.sh to ignore setup.sh (env var usage, not secrets) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
835 lines
25 KiB
Markdown
835 lines
25 KiB
Markdown
# Community Management System - Phasen 2.3 bis 3.0
|
||
|
||
## Kontext
|
||
|
||
Du arbeitest am Community Management System für Complex Care Solutions (CCS). Die Phasen 1.0, 2.1 und 2.2 sind abgeschlossen:
|
||
|
||
**Phase 1.0 (✅ Live):**
|
||
- Community Inbox mit Filter, Suche, Detail-View
|
||
- YouTube OAuth Integration
|
||
- Rules Engine für Automatisierung
|
||
- Export (PDF/Excel/CSV)
|
||
- Claude AI Sentiment-Analyse
|
||
|
||
**Phase 2.1 (✅ Merged):**
|
||
- Analytics Dashboard unter `/admin/views/community/analytics`
|
||
- 6 API-Endpoints (overview, sentiment-trend, response-metrics, channel-comparison, top-content, topic-cloud)
|
||
- 6 React-Komponenten mit Recharts
|
||
- Responsive Design, Dark Mode
|
||
|
||
**Phase 2.2 (✅ Merged):**
|
||
- YouTube Auto-Sync (Cron alle 15 Min)
|
||
- AI Reply Suggestions (3 Tonalitäten)
|
||
- Kanalspezifische Personas (corporate, lifestyle, business)
|
||
- ClaudeReplyService mit medizinischer Fragen-Erkennung
|
||
|
||
---
|
||
|
||
## Technischer Stack
|
||
|
||
| Komponente | Technologie |
|
||
|------------|-------------|
|
||
| Framework | Next.js 15 (App Router) |
|
||
| CMS | Payload CMS 3.x |
|
||
| Datenbank | PostgreSQL 17 |
|
||
| Styling | SCSS mit BEM-Konvention |
|
||
| Charts | Recharts |
|
||
| AI | Anthropic Claude API (claude-3-5-haiku) |
|
||
| Auth | Payload Auth + OAuth 2.0 |
|
||
| Deployment | Vercel |
|
||
|
||
---
|
||
|
||
## Bestehende Architektur
|
||
|
||
### Verzeichnisstruktur
|
||
```
|
||
src/
|
||
├── app/(payload)/
|
||
│ ├── admin/views/community/
|
||
│ │ ├── inbox/
|
||
│ │ │ ├── page.tsx
|
||
│ │ │ ├── CommunityInbox.tsx
|
||
│ │ │ └── inbox.scss
|
||
│ │ └── analytics/
|
||
│ │ ├── page.tsx
|
||
│ │ ├── AnalyticsDashboard.tsx
|
||
│ │ ├── analytics.scss
|
||
│ │ └── components/
|
||
│ └── api/community/
|
||
│ ├── stats/route.ts
|
||
│ ├── export/route.ts
|
||
│ ├── reply/route.ts
|
||
│ ├── sync/route.ts
|
||
│ ├── generate-reply/route.ts
|
||
│ └── analytics/[...]/route.ts
|
||
├── lib/
|
||
│ ├── integrations/
|
||
│ │ ├── youtube/
|
||
│ │ │ ├── YouTubeClient.ts
|
||
│ │ │ └── CommentsSyncService.ts
|
||
│ │ └── claude/
|
||
│ │ ├── ClaudeAnalysisService.ts
|
||
│ │ └── ClaudeReplyService.ts
|
||
│ └── jobs/
|
||
│ ├── JobLogger.ts
|
||
│ └── syncAllComments.ts
|
||
├── collections/
|
||
│ ├── SocialPlatforms.ts
|
||
│ ├── SocialAccounts.ts
|
||
│ ├── CommunityInteractions.ts
|
||
│ ├── CommunityTemplates.ts
|
||
│ └── CommunityRules.ts
|
||
└── types/
|
||
└── youtube.ts
|
||
```
|
||
|
||
### Datenbank-Schema (CommunityInteractions)
|
||
```typescript
|
||
{
|
||
platform: Relation<SocialPlatforms> // youtube, instagram, tiktok
|
||
socialAccount: Relation<SocialAccounts>
|
||
linkedContent?: Relation<YouTubeContent | InstagramContent | TikTokContent>
|
||
type: 'comment' | 'reply' | 'dm' | 'mention' | 'review' | 'question'
|
||
externalId: string // Plattform-spezifische ID
|
||
author: {
|
||
externalId: string
|
||
name: string
|
||
handle: string
|
||
avatarUrl?: string
|
||
isVerified: boolean
|
||
isSubscriber: boolean
|
||
followerCount?: number
|
||
}
|
||
message: string
|
||
analysis: {
|
||
sentiment: 'positive' | 'neutral' | 'negative' | 'question' | 'gratitude' | 'frustration'
|
||
sentimentScore: number
|
||
confidence: number
|
||
topics: Array<{ topic: string }>
|
||
suggestedReply?: string
|
||
}
|
||
flags: {
|
||
isMedicalQuestion: boolean
|
||
requiresEscalation: boolean
|
||
isSpam: boolean
|
||
isFromInfluencer: boolean
|
||
}
|
||
status: 'new' | 'in_review' | 'waiting' | 'replied' | 'resolved' | 'archived' | 'spam'
|
||
priority: 'urgent' | 'high' | 'normal' | 'low'
|
||
response?: {
|
||
text: string
|
||
usedTemplate?: Relation<CommunityTemplates>
|
||
sentAt: Date
|
||
sentBy: Relation<Users>
|
||
externalId?: string
|
||
}
|
||
}
|
||
```
|
||
|
||
### SocialAccounts Schema
|
||
```typescript
|
||
{
|
||
platform: Relation<SocialPlatforms>
|
||
accountName: string
|
||
accountHandle: string
|
||
channelType: 'corporate' | 'lifestyle' | 'business'
|
||
credentials: {
|
||
accessToken: string
|
||
refreshToken: string
|
||
expiresAt: Date
|
||
scope: string
|
||
}
|
||
syncSettings: {
|
||
enabled: boolean
|
||
lastSyncAt: Date
|
||
syncInterval: number // Minuten
|
||
}
|
||
stats: {
|
||
followers: number
|
||
totalInteractions: number
|
||
lastUpdated: Date
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 2.3: Instagram Integration
|
||
|
||
### Ziel
|
||
Instagram-Kommentare und DMs synchronisieren, analysieren und beantworten – analog zur YouTube-Integration.
|
||
|
||
### Voraussetzungen
|
||
- Instagram Business Account (verbunden mit Facebook Page)
|
||
- Meta Business Suite Zugang
|
||
- Instagram Graph API Zugang
|
||
|
||
### API-Referenz
|
||
- **Instagram Graph API:** https://developers.facebook.com/docs/instagram-api
|
||
- **Webhooks:** https://developers.facebook.com/docs/instagram-api/guides/webhooks
|
||
|
||
### Deliverables
|
||
|
||
#### 1. OAuth Flow für Instagram
|
||
```
|
||
src/app/(payload)/api/auth/instagram/
|
||
├── route.ts # OAuth initiate
|
||
└── callback/route.ts # OAuth callback
|
||
```
|
||
|
||
**Scopes benötigt:**
|
||
- `instagram_basic`
|
||
- `instagram_manage_comments`
|
||
- `instagram_manage_messages` (für DMs, falls verfügbar)
|
||
- `pages_show_list`
|
||
- `pages_read_engagement`
|
||
|
||
#### 2. Instagram Client
|
||
```
|
||
src/lib/integrations/instagram/
|
||
├── InstagramClient.ts
|
||
├── CommentsSyncService.ts
|
||
└── types.ts
|
||
```
|
||
|
||
**InstagramClient Methoden:**
|
||
```typescript
|
||
class InstagramClient {
|
||
// Auth
|
||
async refreshAccessToken(): Promise<void>
|
||
|
||
// Media (Posts, Reels, Stories)
|
||
async getMedia(accountId: string, limit?: number): Promise<InstagramMedia[]>
|
||
async getMediaComments(mediaId: string): Promise<InstagramComment[]>
|
||
|
||
// Comments
|
||
async replyToComment(commentId: string, message: string): Promise<string>
|
||
async deleteComment(commentId: string): Promise<void>
|
||
async hideComment(commentId: string): Promise<void>
|
||
|
||
// Mentions
|
||
async getMentions(accountId: string): Promise<InstagramMention[]>
|
||
|
||
// DMs (wenn verfügbar)
|
||
async getConversations(accountId: string): Promise<InstagramConversation[]>
|
||
async sendMessage(conversationId: string, message: string): Promise<void>
|
||
}
|
||
```
|
||
|
||
#### 3. Sync Service
|
||
```typescript
|
||
// CommentsSyncService.ts
|
||
class InstagramCommentsSyncService {
|
||
async syncAccount(account: SocialAccount): Promise<SyncResult>
|
||
async syncAllAccounts(): Promise<SyncResult[]>
|
||
}
|
||
```
|
||
|
||
**Sync-Logik:**
|
||
1. Alle Media-Items der letzten 30 Tage abrufen
|
||
2. Kommentare pro Media-Item abrufen
|
||
3. Neue Kommentare in DB speichern
|
||
4. Claude-Analyse triggern
|
||
5. Mentions separat abrufen und speichern
|
||
|
||
#### 4. Cron Job erweitern
|
||
```typescript
|
||
// src/app/(payload)/api/cron/social-sync/route.ts
|
||
// Erweitere bestehenden YouTube-Sync um Instagram
|
||
|
||
export async function GET(request: Request) {
|
||
// Auth check...
|
||
|
||
const results = {
|
||
youtube: await syncYouTubeComments(),
|
||
instagram: await syncInstagramComments(),
|
||
}
|
||
|
||
return Response.json({ success: true, results })
|
||
}
|
||
```
|
||
|
||
#### 5. Collection Updates
|
||
|
||
**SocialPlatforms - Neuer Eintrag:**
|
||
```typescript
|
||
{
|
||
name: 'Instagram',
|
||
slug: 'instagram',
|
||
icon: 'instagram',
|
||
features: ['comments', 'mentions', 'dms', 'stories'],
|
||
apiVersion: 'v18.0'
|
||
}
|
||
```
|
||
|
||
**InstagramContent Collection (neu):**
|
||
```typescript
|
||
// src/collections/InstagramContent.ts
|
||
{
|
||
slug: 'instagram-content',
|
||
fields: [
|
||
{ name: 'externalId', type: 'text', required: true, unique: true },
|
||
{ name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts' },
|
||
{ name: 'mediaType', type: 'select', options: ['image', 'video', 'carousel', 'reel', 'story'] },
|
||
{ name: 'caption', type: 'textarea' },
|
||
{ name: 'permalink', type: 'text' },
|
||
{ name: 'thumbnailUrl', type: 'text' },
|
||
{ name: 'postedAt', type: 'date' },
|
||
{ name: 'stats', type: 'group', fields: [
|
||
{ name: 'likes', type: 'number' },
|
||
{ name: 'comments', type: 'number' },
|
||
{ name: 'shares', type: 'number' },
|
||
{ name: 'saves', type: 'number' },
|
||
{ name: 'reach', type: 'number' },
|
||
]},
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 6. UI Updates
|
||
|
||
**CommunityInbox.tsx:**
|
||
- Platform-Filter um "Instagram" erweitern
|
||
- Instagram-spezifische Icons und Badges
|
||
- "Hide Comment" Action für Instagram
|
||
- Story-Mentions separat anzeigen
|
||
|
||
**ChannelComparison.tsx:**
|
||
- Instagram-Accounts in Vergleichstabelle
|
||
|
||
### Acceptance Criteria
|
||
|
||
- [ ] OAuth-Flow für Instagram Business Accounts funktioniert
|
||
- [ ] Kommentare werden alle 15 Minuten synchronisiert
|
||
- [ ] Mentions werden erkannt und importiert
|
||
- [ ] Antworten werden über API gepostet
|
||
- [ ] "Hide Comment" Funktion verfügbar
|
||
- [ ] Analytics Dashboard zeigt Instagram-Daten
|
||
- [ ] Keine TypeScript-Fehler
|
||
- [ ] Error Handling für API-Rate-Limits
|
||
|
||
### Wichtige Hinweise
|
||
|
||
1. **Long-lived Tokens:** Instagram-Tokens laufen nach 60 Tagen ab → Refresh implementieren
|
||
2. **Rate Limits:** 200 Calls/Stunde pro User → Batching implementieren
|
||
3. **Webhooks (optional):** Für Echtzeit-Updates statt Polling
|
||
4. **Story-Mentions:** Verfallen nach 24h → schnell syncen
|
||
|
||
---
|
||
|
||
## Phase 2.4: TikTok Integration
|
||
|
||
### Ziel
|
||
TikTok-Kommentare synchronisieren und beantworten.
|
||
|
||
### Voraussetzungen
|
||
- TikTok Business Account
|
||
- TikTok for Developers Zugang
|
||
- App-Genehmigung für Comment API
|
||
|
||
### API-Referenz
|
||
- **TikTok Content Posting API:** https://developers.tiktok.com/doc/content-posting-api-get-started
|
||
- **TikTok Login Kit:** https://developers.tiktok.com/doc/login-kit-web
|
||
|
||
### Deliverables
|
||
|
||
#### 1. OAuth Flow für TikTok
|
||
```
|
||
src/app/(payload)/api/auth/tiktok/
|
||
├── route.ts
|
||
└── callback/route.ts
|
||
```
|
||
|
||
**Scopes:**
|
||
- `user.info.basic`
|
||
- `video.list`
|
||
- `comment.list`
|
||
- `comment.list.manage`
|
||
|
||
#### 2. TikTok Client
|
||
```
|
||
src/lib/integrations/tiktok/
|
||
├── TikTokClient.ts
|
||
├── CommentsSyncService.ts
|
||
└── types.ts
|
||
```
|
||
|
||
**TikTokClient Methoden:**
|
||
```typescript
|
||
class TikTokClient {
|
||
async refreshAccessToken(): Promise<void>
|
||
async getVideos(openId: string, cursor?: string): Promise<TikTokVideo[]>
|
||
async getVideoComments(videoId: string, cursor?: string): Promise<TikTokComment[]>
|
||
async replyToComment(videoId: string, commentId: string, text: string): Promise<string>
|
||
}
|
||
```
|
||
|
||
#### 3. TikTokContent Collection
|
||
```typescript
|
||
// src/collections/TikTokContent.ts
|
||
{
|
||
slug: 'tiktok-content',
|
||
fields: [
|
||
{ name: 'externalId', type: 'text', required: true, unique: true },
|
||
{ name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts' },
|
||
{ name: 'title', type: 'text' },
|
||
{ name: 'description', type: 'textarea' },
|
||
{ name: 'shareUrl', type: 'text' },
|
||
{ name: 'coverImageUrl', type: 'text' },
|
||
{ name: 'duration', type: 'number' },
|
||
{ name: 'postedAt', type: 'date' },
|
||
{ name: 'stats', type: 'group', fields: [
|
||
{ name: 'views', type: 'number' },
|
||
{ name: 'likes', type: 'number' },
|
||
{ name: 'comments', type: 'number' },
|
||
{ name: 'shares', type: 'number' },
|
||
]},
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 4. Cron Job erweitern
|
||
```typescript
|
||
// Erweitere social-sync um TikTok
|
||
const results = {
|
||
youtube: await syncYouTubeComments(),
|
||
instagram: await syncInstagramComments(),
|
||
tiktok: await syncTikTokComments(),
|
||
}
|
||
```
|
||
|
||
### Acceptance Criteria
|
||
|
||
- [ ] OAuth-Flow für TikTok Business Accounts
|
||
- [ ] Videos werden synchronisiert
|
||
- [ ] Kommentare werden importiert und analysiert
|
||
- [ ] Antworten können gepostet werden
|
||
- [ ] Analytics Dashboard integriert TikTok
|
||
- [ ] Rate Limiting beachtet (100 Requests/Minute)
|
||
|
||
### Wichtige Hinweise
|
||
|
||
1. **API-Zugang:** TikTok Comment API erfordert App-Review → frühzeitig beantragen
|
||
2. **Sandbox Mode:** Erst in Sandbox testen, dann Production beantragen
|
||
3. **Cursor-based Pagination:** TikTok nutzt Cursor statt Offset
|
||
4. **Kein DM-Zugang:** TikTok API erlaubt keine DM-Integration
|
||
|
||
---
|
||
|
||
## Phase 2.5: Auto-Reply System
|
||
|
||
### Ziel
|
||
Vollautomatische Antworten für bestimmte Kommentar-Typen basierend auf konfigurierbaren Regeln.
|
||
|
||
### Architektur
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ AUTO-REPLY FLOW │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ New Comment ──▶ Analysis ──▶ Rule Matching ──▶ Decision │
|
||
│ │ │
|
||
│ ┌──────────┴──────────┐ │
|
||
│ ▼ ▼ │
|
||
│ [Auto-Reply] [Manual Queue] │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ Generate Reply ──▶ Post ──▶ Log │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Deliverables
|
||
|
||
#### 1. AutoReplyRules Collection
|
||
```typescript
|
||
// src/collections/AutoReplyRules.ts
|
||
{
|
||
slug: 'auto-reply-rules',
|
||
admin: {
|
||
useAsTitle: 'name',
|
||
group: 'Community',
|
||
},
|
||
fields: [
|
||
{ name: 'name', type: 'text', required: true },
|
||
{ name: 'description', type: 'textarea' },
|
||
{ name: 'enabled', type: 'checkbox', defaultValue: true },
|
||
{ name: 'priority', type: 'number', defaultValue: 0 }, // Höher = wird zuerst geprüft
|
||
|
||
// Conditions
|
||
{ name: 'conditions', type: 'group', fields: [
|
||
{ name: 'platforms', type: 'relationship', relationTo: 'social-platforms', hasMany: true },
|
||
{ name: 'accounts', type: 'relationship', relationTo: 'social-accounts', hasMany: true },
|
||
{ name: 'sentiments', type: 'select', hasMany: true, options: [
|
||
'positive', 'neutral', 'negative', 'question', 'gratitude', 'frustration'
|
||
]},
|
||
{ name: 'topicsInclude', type: 'array', fields: [{ name: 'topic', type: 'text' }] },
|
||
{ name: 'topicsExclude', type: 'array', fields: [{ name: 'topic', type: 'text' }] },
|
||
{ name: 'keywordsInclude', type: 'array', fields: [{ name: 'keyword', type: 'text' }] },
|
||
{ name: 'keywordsExclude', type: 'array', fields: [{ name: 'keyword', type: 'text' }] },
|
||
{ name: 'authorMinFollowers', type: 'number' },
|
||
{ name: 'authorMaxFollowers', type: 'number' },
|
||
{ name: 'excludeVerified', type: 'checkbox', defaultValue: false },
|
||
{ name: 'excludeInfluencers', type: 'checkbox', defaultValue: true },
|
||
]},
|
||
|
||
// Exclusions (WICHTIG für Sicherheit)
|
||
{ name: 'exclusions', type: 'group', fields: [
|
||
{ name: 'excludeMedicalQuestions', type: 'checkbox', defaultValue: true },
|
||
{ name: 'excludeEscalations', type: 'checkbox', defaultValue: true },
|
||
{ name: 'excludeNegativeSentiment', type: 'checkbox', defaultValue: true },
|
||
{ name: 'excludeLongMessages', type: 'checkbox', defaultValue: true },
|
||
{ name: 'maxMessageLength', type: 'number', defaultValue: 500 },
|
||
{ name: 'confidenceThreshold', type: 'number', defaultValue: 80 }, // Mindest-Confidence für Auto-Reply
|
||
]},
|
||
|
||
// Action
|
||
{ name: 'action', type: 'group', fields: [
|
||
{ name: 'type', type: 'select', options: [
|
||
{ label: 'Template verwenden', value: 'template' },
|
||
{ label: 'AI generieren', value: 'ai_generate' },
|
||
{ label: 'Template + AI personalisieren', value: 'template_ai' },
|
||
]},
|
||
{ name: 'template', type: 'relationship', relationTo: 'community-templates' },
|
||
{ name: 'aiTone', type: 'select', options: ['freundlich', 'professionell', 'empathisch'] },
|
||
{ name: 'delay', type: 'number', defaultValue: 0 }, // Minuten bis Auto-Reply
|
||
{ name: 'addSignature', type: 'checkbox', defaultValue: true },
|
||
]},
|
||
|
||
// Limits
|
||
{ name: 'limits', type: 'group', fields: [
|
||
{ name: 'maxPerDay', type: 'number', defaultValue: 50 },
|
||
{ name: 'maxPerHour', type: 'number', defaultValue: 10 },
|
||
{ name: 'cooldownMinutes', type: 'number', defaultValue: 5 }, // Cooldown pro Autor
|
||
]},
|
||
|
||
// Stats
|
||
{ name: 'stats', type: 'group', fields: [
|
||
{ name: 'totalFired', type: 'number', defaultValue: 0 },
|
||
{ name: 'lastFiredAt', type: 'date' },
|
||
]},
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 2. AutoReplyService
|
||
```typescript
|
||
// src/lib/services/AutoReplyService.ts
|
||
|
||
interface AutoReplyDecision {
|
||
shouldAutoReply: boolean
|
||
rule?: AutoReplyRule
|
||
reason: string
|
||
generatedReply?: string
|
||
}
|
||
|
||
class AutoReplyService {
|
||
/**
|
||
* Prüft ob ein Kommentar auto-replied werden soll
|
||
*/
|
||
async evaluate(interaction: CommunityInteraction): Promise<AutoReplyDecision>
|
||
|
||
/**
|
||
* Führt Auto-Reply aus
|
||
*/
|
||
async execute(interaction: CommunityInteraction, rule: AutoReplyRule): Promise<void>
|
||
|
||
/**
|
||
* Prüft Rate Limits für eine Regel
|
||
*/
|
||
private async checkLimits(rule: AutoReplyRule): Promise<boolean>
|
||
|
||
/**
|
||
* Generiert Reply basierend auf Action-Type
|
||
*/
|
||
private async generateReply(
|
||
interaction: CommunityInteraction,
|
||
rule: AutoReplyRule
|
||
): Promise<string>
|
||
|
||
/**
|
||
* Postet Reply auf Plattform
|
||
*/
|
||
private async postReply(
|
||
interaction: CommunityInteraction,
|
||
replyText: string
|
||
): Promise<string>
|
||
}
|
||
```
|
||
|
||
#### 3. Integration in Sync-Flow
|
||
```typescript
|
||
// Erweitere CommentsSyncService (alle Plattformen)
|
||
|
||
async syncComment(comment: RawComment): Promise<void> {
|
||
// 1. In DB speichern
|
||
const interaction = await this.saveInteraction(comment)
|
||
|
||
// 2. Claude-Analyse
|
||
await this.analyzeInteraction(interaction)
|
||
|
||
// 3. Auto-Reply Check (NEU)
|
||
const autoReplyService = new AutoReplyService()
|
||
const decision = await autoReplyService.evaluate(interaction)
|
||
|
||
if (decision.shouldAutoReply && decision.rule) {
|
||
await autoReplyService.execute(interaction, decision.rule)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4. Auto-Reply Log Collection
|
||
```typescript
|
||
// src/collections/AutoReplyLogs.ts
|
||
{
|
||
slug: 'auto-reply-logs',
|
||
fields: [
|
||
{ name: 'interaction', type: 'relationship', relationTo: 'community-interactions' },
|
||
{ name: 'rule', type: 'relationship', relationTo: 'auto-reply-rules' },
|
||
{ name: 'generatedReply', type: 'textarea' },
|
||
{ name: 'postedAt', type: 'date' },
|
||
{ name: 'success', type: 'checkbox' },
|
||
{ name: 'error', type: 'text' },
|
||
{ name: 'responseTime', type: 'number' }, // ms bis Antwort gepostet
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5. Admin UI für Auto-Reply Rules
|
||
|
||
**Neue View:** `/admin/views/community/auto-reply`
|
||
|
||
```
|
||
src/app/(payload)/admin/views/community/auto-reply/
|
||
├── page.tsx
|
||
├── AutoReplyManager.tsx
|
||
└── auto-reply.scss
|
||
```
|
||
|
||
**Features:**
|
||
- Liste aller Regeln mit Enable/Disable Toggle
|
||
- Regel-Editor mit Condition Builder
|
||
- Test-Modus: Kommentar eingeben und prüfen welche Regel matcht
|
||
- Stats: Fired Count, Success Rate, Avg Response Time
|
||
- Logs: Letzte 100 Auto-Replies mit Details
|
||
|
||
#### 6. Analytics Integration
|
||
|
||
**Neue Metriken:**
|
||
- Auto-Reply Rate (% der Kommentare die auto-replied wurden)
|
||
- Auto-Reply Success Rate
|
||
- Durchschnittliche Auto-Reply Zeit
|
||
- Top Auto-Reply Rules
|
||
- Manual Override Rate (wie oft wurde Auto-Reply überschrieben)
|
||
|
||
### Acceptance Criteria
|
||
|
||
- [ ] Regel-Editor im Admin-Panel
|
||
- [ ] Conditions werden korrekt ausgewertet
|
||
- [ ] Exclusions verhindern Auto-Reply bei sensiblen Inhalten
|
||
- [ ] Rate Limits funktionieren
|
||
- [ ] Logs werden geschrieben
|
||
- [ ] Test-Modus funktioniert
|
||
- [ ] Analytics zeigen Auto-Reply Metriken
|
||
- [ ] Medizinische Fragen werden NIEMALS auto-replied
|
||
- [ ] Negative Kommentare werden NIEMALS auto-replied (außer explizit konfiguriert)
|
||
|
||
### Sicherheitsregeln (KRITISCH)
|
||
|
||
```
|
||
⚠️ NIEMALS AUTO-REPLY BEI:
|
||
1. Medizinischen Fragen (isMedicalQuestion = true)
|
||
2. Eskalationen (requiresEscalation = true)
|
||
3. Negativem Sentiment (außer explizit aktiviert)
|
||
4. Kommentaren von verifizierten Accounts (außer explizit aktiviert)
|
||
5. Langen, komplexen Nachrichten (> 500 Zeichen default)
|
||
6. Niedriger Analyse-Confidence (< 80% default)
|
||
7. Kommentaren die bereits beantwortet wurden
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 3.0: Scheduled Reports & Real-time Updates
|
||
|
||
### Ziel
|
||
Automatische Reports per E-Mail und Echtzeit-Updates im Dashboard.
|
||
|
||
### Deliverables
|
||
|
||
#### 1. Scheduled Reports
|
||
|
||
**ReportSchedules Collection:**
|
||
```typescript
|
||
// src/collections/ReportSchedules.ts
|
||
{
|
||
slug: 'report-schedules',
|
||
fields: [
|
||
{ name: 'name', type: 'text', required: true },
|
||
{ name: 'enabled', type: 'checkbox', defaultValue: true },
|
||
{ name: 'frequency', type: 'select', options: ['daily', 'weekly', 'monthly'] },
|
||
{ name: 'dayOfWeek', type: 'select', options: ['monday', 'tuesday', ...] }, // für weekly
|
||
{ name: 'dayOfMonth', type: 'number', min: 1, max: 28 }, // für monthly
|
||
{ name: 'time', type: 'text' }, // HH:MM Format
|
||
{ name: 'timezone', type: 'text', defaultValue: 'Europe/Berlin' },
|
||
{ name: 'recipients', type: 'array', fields: [{ name: 'email', type: 'email' }] },
|
||
{ name: 'reportType', type: 'select', options: [
|
||
'overview', // KPI Summary
|
||
'sentiment_analysis', // Sentiment Deep-Dive
|
||
'response_metrics', // Team Performance
|
||
'content_performance', // Top Content
|
||
'full_report', // Alle oben kombiniert
|
||
]},
|
||
{ name: 'channels', type: 'relationship', relationTo: 'social-accounts', hasMany: true },
|
||
{ name: 'format', type: 'select', options: ['pdf', 'excel', 'html_email'] },
|
||
{ name: 'lastSentAt', type: 'date' },
|
||
{ name: 'nextScheduledAt', type: 'date' },
|
||
]
|
||
}
|
||
```
|
||
|
||
**ReportGeneratorService:**
|
||
```typescript
|
||
// src/lib/services/ReportGeneratorService.ts
|
||
|
||
class ReportGeneratorService {
|
||
async generateReport(schedule: ReportSchedule): Promise<Buffer>
|
||
async sendReport(schedule: ReportSchedule, report: Buffer): Promise<void>
|
||
}
|
||
```
|
||
|
||
**Cron Job:**
|
||
```typescript
|
||
// src/app/(payload)/api/cron/send-reports/route.ts
|
||
// Läuft stündlich, prüft welche Reports fällig sind
|
||
```
|
||
|
||
#### 2. Real-time Updates (WebSocket)
|
||
|
||
**Option A: Vercel Serverless (Polling-Fallback)**
|
||
```typescript
|
||
// Da Vercel keine WebSockets unterstützt, Server-Sent Events (SSE) nutzen
|
||
// src/app/(payload)/api/community/stream/route.ts
|
||
|
||
export async function GET(request: Request) {
|
||
const encoder = new TextEncoder()
|
||
const stream = new ReadableStream({
|
||
async start(controller) {
|
||
// Polling-Loop mit 5s Intervall
|
||
while (true) {
|
||
const updates = await getRecentUpdates()
|
||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(updates)}\n\n`))
|
||
await sleep(5000)
|
||
}
|
||
}
|
||
})
|
||
|
||
return new Response(stream, {
|
||
headers: {
|
||
'Content-Type': 'text/event-stream',
|
||
'Cache-Control': 'no-cache',
|
||
'Connection': 'keep-alive',
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
**Option B: Externe WebSocket-Lösung**
|
||
- Pusher, Ably, oder Socket.io mit separatem Server
|
||
- Trigger bei DB-Änderungen via Payload Hooks
|
||
|
||
**Dashboard Integration:**
|
||
```typescript
|
||
// useRealtimeUpdates.ts Hook
|
||
function useRealtimeUpdates() {
|
||
const [updates, setUpdates] = useState<Update[]>([])
|
||
|
||
useEffect(() => {
|
||
const eventSource = new EventSource('/api/community/stream')
|
||
eventSource.onmessage = (event) => {
|
||
const data = JSON.parse(event.data)
|
||
setUpdates(prev => [...data, ...prev].slice(0, 100))
|
||
}
|
||
return () => eventSource.close()
|
||
}, [])
|
||
|
||
return updates
|
||
}
|
||
```
|
||
|
||
**UI Updates:**
|
||
- Live-Counter für neue Kommentare
|
||
- Toast-Notifications bei wichtigen Events
|
||
- Auto-Refresh der Inbox bei neuen Items
|
||
- Pulsierender Badge wenn neue ungelesene Items
|
||
|
||
### Acceptance Criteria
|
||
|
||
**Scheduled Reports:**
|
||
- [ ] Report-Editor im Admin-Panel
|
||
- [ ] Daily/Weekly/Monthly Scheduling funktioniert
|
||
- [ ] PDF-Reports werden generiert
|
||
- [ ] E-Mail-Versand funktioniert
|
||
- [ ] Timezone-Support
|
||
|
||
**Real-time Updates:**
|
||
- [ ] SSE-Endpoint funktioniert
|
||
- [ ] Dashboard zeigt Live-Updates
|
||
- [ ] Toast-Notifications bei neuen Kommentaren
|
||
- [ ] Kein Performance-Impact bei vielen Clients
|
||
|
||
---
|
||
|
||
## Priorisierung
|
||
|
||
| Phase | Priorität | Geschätzter Aufwand | Abhängigkeiten |
|
||
|-------|-----------|---------------------|----------------|
|
||
| 2.3 Instagram | Hoch | 3-4 Tage | Meta Business Suite Zugang |
|
||
| 2.4 TikTok | Mittel | 2-3 Tage | TikTok API Approval |
|
||
| 2.5 Auto-Reply | Mittel | 3-4 Tage | Phase 2.3 (mehr Testdaten) |
|
||
| 3.0 Reports | Niedrig | 2-3 Tage | E-Mail-Service (Resend/SendGrid) |
|
||
| 3.0 Real-time | Niedrig | 2-3 Tage | Optional: Pusher Account |
|
||
|
||
---
|
||
|
||
## Code-Konventionen (Referenz)
|
||
|
||
### TypeScript
|
||
- Strict Mode aktiviert
|
||
- Interfaces für alle API-Responses
|
||
- Zod für Runtime-Validation
|
||
|
||
### SCSS
|
||
- BEM-Konvention: `.block__element--modifier`
|
||
- Payload CSS-Variablen nutzen: `--theme-elevation-X`
|
||
- Mobile-first mit Breakpoints: 480px, 768px, 1024px, 1200px
|
||
|
||
### API Routes
|
||
- Payload Auth via `getPayload({ config })`
|
||
- Konsistente Error-Responses: `{ error: string, details?: any }`
|
||
- Success-Responses: `{ success: true, data: any }`
|
||
|
||
### Testing
|
||
- Manueller Test nach jeder Komponente
|
||
- API-Tests via curl/Postman dokumentieren
|
||
- Edge Cases: Leere Daten, API-Fehler, Rate Limits
|
||
|
||
---
|
||
|
||
## Checkliste vor Merge
|
||
|
||
- [ ] Keine TypeScript-Fehler (`npm run typecheck`)
|
||
- [ ] Build erfolgreich (`npm run build`)
|
||
- [ ] Alle neuen Collections in payload.config.ts registriert
|
||
- [ ] Environment Variables dokumentiert
|
||
- [ ] API-Endpoints getestet
|
||
- [ ] UI responsiv auf Mobile
|
||
- [ ] Error States implementiert
|
||
- [ ] Loading States implementiert
|
||
|
||
---
|
||
|
||
*Erstellt: 16. Januar 2026*
|
||
*Für: Dev-KI (Claude Code)*
|
||
*Von: Konzept-KI (Claude Opus 4.5)*
|