From 06c93ba05ca60ccb3fc4d4b1f86b04cc1bac03b2 Mon Sep 17 00:00:00 2001 From: Martin Porwoll Date: Fri, 13 Feb 2026 13:50:35 +0000 Subject: [PATCH] feat: add YouTube Analytics Dashboard custom admin view Custom admin view at /admin/youtube-analytics with 4 tabs: - Performance: Views, Watch Time, CTR, Subscribers with period comparison - Pipeline: Status distribution, scheduled videos, overdue tasks - Goals: Monthly target progress bars and custom KPIs - Community: Sentiment analysis, response time, top topics Includes channel selector, period selector (7d/30d/90d), and sidebar nav link in the YouTube section. Co-Authored-By: Claude Opus 4.6 --- ...2-13-youtube-analytics-dashboard-design.md | 155 +++ .../2026-02-13-youtube-analytics-dashboard.md | 971 ++++++++++++++++++ src/app/(payload)/admin/importMap.js | 4 + .../(payload)/api/youtube/analytics/route.ts | 442 ++++++++ .../admin/YouTubeAnalyticsDashboard.scss | 722 +++++++++++++ .../admin/YouTubeAnalyticsDashboard.tsx | 865 ++++++++++++++++ .../admin/YouTubeAnalyticsDashboardView.tsx | 10 + .../admin/YouTubeAnalyticsNavLinks.scss | 87 ++ .../admin/YouTubeAnalyticsNavLinks.tsx | 45 + src/payload.config.ts | 11 +- 10 files changed, 3309 insertions(+), 3 deletions(-) create mode 100644 docs/plans/2026-02-13-youtube-analytics-dashboard-design.md create mode 100644 docs/plans/2026-02-13-youtube-analytics-dashboard.md create mode 100644 src/app/(payload)/api/youtube/analytics/route.ts create mode 100644 src/components/admin/YouTubeAnalyticsDashboard.scss create mode 100644 src/components/admin/YouTubeAnalyticsDashboard.tsx create mode 100644 src/components/admin/YouTubeAnalyticsDashboardView.tsx create mode 100644 src/components/admin/YouTubeAnalyticsNavLinks.scss create mode 100644 src/components/admin/YouTubeAnalyticsNavLinks.tsx diff --git a/docs/plans/2026-02-13-youtube-analytics-dashboard-design.md b/docs/plans/2026-02-13-youtube-analytics-dashboard-design.md new file mode 100644 index 0000000..981ec44 --- /dev/null +++ b/docs/plans/2026-02-13-youtube-analytics-dashboard-design.md @@ -0,0 +1,155 @@ +# YouTube Analytics Dashboard - Design + +**Datum:** 2026-02-13 +**Status:** Approved + +## Übersicht + +Custom Admin View für YouTube Analytics unter `/admin/youtube-analytics`. +Kombiniertes Dashboard mit 4 Tabs: Performance, Pipeline, Goals, Community. +Channel-Selector + Period-Selector oben. Sidebar Nav-Link im YouTube-Bereich. + +## Architektur + +### Ansatz +Einzelnes Dashboard mit Tab-Navigation (Ansatz A). +Jeder Tab lädt Daten lazy beim Wechsel über eine gemeinsame API-Route. + +### Dateien + +``` +src/ +├── components/admin/ +│ ├── YouTubeAnalyticsDashboard.tsx # Haupt-Component (Client) +│ ├── YouTubeAnalyticsDashboardView.tsx # Wrapper für Payload Custom View +│ ├── YouTubeAnalyticsDashboard.scss # BEM-Styling +│ └── YouTubeAnalyticsNavLinks.tsx # Sidebar Nav-Link Component +├── app/(payload)/api/youtube/ +│ └── analytics/route.ts # API-Route für alle Tabs +``` + +### Registrierung (payload.config.ts) + +```typescript +admin: { + components: { + afterNavLinks: [ + '@/components/admin/CommunityNavLinks#CommunityNavLinks', + '@/components/admin/YouTubeAnalyticsNavLinks#YouTubeAnalyticsNavLinks', + ], + views: { + TenantDashboard: { ... }, + YouTubeAnalyticsDashboard: { + Component: '@/components/admin/YouTubeAnalyticsDashboardView#YouTubeAnalyticsDashboardView', + path: '/youtube-analytics', + }, + }, + }, +}, +``` + +### Datenfluss + +``` +Browser → YouTubeAnalyticsDashboard (Client Component) + → fetch('/api/youtube/analytics?tab=X&channel=Y&period=Z') + → API Route → payload.find() / payload.count() parallel + → Aggregierte JSON-Response +``` + +## Tab-Struktur & Metriken + +### Tab 1: Performance + +**Stat-Cards:** +- Gesamt-Views (Zeitraum) mit Trend vs. Vorperiode +- Gesamt Watch Time (Stunden) +- Durchschnittliche CTR (%) +- Subscriber-Gewinn (Zeitraum) + +**Sektionen:** +- Top 5 Videos (nach Views): Thumbnail-Text, Title, Views, CTR, Avg. Retention +- Bottom 5 Videos (nach Views): gleiche Felder +- Engagement-Übersicht: Likes, Comments, Shares aggregiert + +### Tab 2: Pipeline + +**Stat-Cards:** +- Videos in Produktion (status < published) +- Diese Woche geplant +- Überfällige Aufgaben +- Offene Approvals + +**Sektionen:** +- Pipeline-Balken: Visueller Balken mit Anzahl pro Status-Stufe (idea → published) +- Nächste Veröffentlichungen: Liste der scheduled Videos mit Datum +- Überfällige Tasks: Liste mit Assignee, Deadline, Video-Titel + +### Tab 3: Goals + +**Stat-Cards:** +- Content-Ziel-Fortschritt (z.B. "8/12 Videos") +- Subscriber-Ziel-Fortschritt +- Views-Ziel-Fortschritt +- Gesamt-Zielerreichung (%) + +**Sektionen:** +- Monatsziele-Übersicht: Alle Goals mit Progress-Bars (Target vs. Current) +- Custom Goals: User-definierte Metriken aus YtMonthlyGoals + +### Tab 4: Community + +**Stat-Cards:** +- Unbearbeitete Kommentare +- Sentiment-Verteilung (% positiv) +- Durchschnittliche Antwortzeit +- Eskalationen offen + +**Sektionen:** +- Sentiment-Breakdown: Positiv/Neutral/Negativ mit Zähler +- Letzte Interaktionen: 5 neueste unbearbeitete Kommentare +- Top-Themen: Häufigste Topics aus AI-Analyse + +## API-Route + +**Endpoint:** `GET /api/youtube/analytics` + +| Param | Werte | Default | +|-------|-------|---------| +| `tab` | `performance`, `pipeline`, `goals`, `community` | `performance` | +| `channel` | `all` oder Channel-ID | `all` | +| `period` | `7d`, `30d`, `90d` | `30d` | + +**Auth:** Cookie-basiert, YouTube-Access Check. +**Response:** Tab-spezifisches JSON. Nur Daten des angeforderten Tabs. + +## UI/Styling + +- BEM mit `.yt-analytics` Prefix +- Payload CSS-Variablen (`--theme-elevation-*`, `--theme-success-*`, etc.) +- Tab-Bar: Horizontal unter Header, aktiver Tab mit Border-Bottom +- Stat-Cards: 4er Grid mit farbigen Left-Borders (wie TenantDashboard) +- Pipeline-Balken: Horizontal segmentiert, jede Status-Stufe eine Farbe +- Progress-Bars: Success-Color für On-Track, Warning für At-Risk +- Responsive: 2-Spalten Grid auf Mobile, Tabs horizontal scrollbar +- Dark Mode: `.dark .yt-analytics` Overrides + +## Datenquellen + +| Collection | Verwendung | +|------------|------------| +| YouTubeContent | Performance-Metriken, Pipeline-Status, Veröffentlichungen | +| YouTubeChannels | Channel-Liste für Selector, Subscriber-Count | +| YtMonthlyGoals | Goals-Tab: Target vs. Current | +| YtTasks | Pipeline-Tab: Überfällige Tasks, Workload | +| YtBatches | Pipeline-Tab: Batch-Fortschritt | +| CommunityInteractions | Community-Tab: Sentiment, Antwortzeit, Themen | + +## Patterns (aus TenantDashboard) + +- `useTenantSelection()` für Tenant-Kontext +- `useState` + `useEffect` + `useCallback` für Daten-Fetching +- `credentials: 'include'` für Auth-Cookies +- Loading/Error States mit Spinner/Error-Box +- Period-Selector als ` setChannel(e.target.value)} + > + + {channels.map((ch) => ( + + ))} + + + + + + +
+ {tabConfig.map((tab) => ( + + ))} +
+ + {loading && !data && ( +
+ + + +

Lade Daten...

+
+ )} + + {error && ( +
+ + + + + +

{error}

+
+ )} + + {!loading && !error && data && ( +
+ {activeTab === 'performance' && renderPerformance()} + {activeTab === 'pipeline' && renderPipeline()} + {activeTab === 'goals' && renderGoals()} + {activeTab === 'community' && renderCommunity()} +
+ )} + + ) +} + +export default YouTubeAnalyticsDashboard diff --git a/src/components/admin/YouTubeAnalyticsDashboardView.tsx b/src/components/admin/YouTubeAnalyticsDashboardView.tsx new file mode 100644 index 0000000..34d83bf --- /dev/null +++ b/src/components/admin/YouTubeAnalyticsDashboardView.tsx @@ -0,0 +1,10 @@ +'use client' + +import React from 'react' +import { YouTubeAnalyticsDashboard } from './YouTubeAnalyticsDashboard' + +export const YouTubeAnalyticsDashboardView: React.FC = () => { + return +} + +export default YouTubeAnalyticsDashboardView diff --git a/src/components/admin/YouTubeAnalyticsNavLinks.scss b/src/components/admin/YouTubeAnalyticsNavLinks.scss new file mode 100644 index 0000000..287791f --- /dev/null +++ b/src/components/admin/YouTubeAnalyticsNavLinks.scss @@ -0,0 +1,87 @@ +.yt-nav-links { + padding: 0 var(--base); + margin-top: var(--base); + border-top: 1px solid var(--theme-elevation-100); + padding-top: var(--base); + + &__header { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--theme-elevation-500); + } + + &__icon { + font-size: 14px; + } + + &__title { + flex: 1; + } + + &__list { + display: flex; + flex-direction: column; + gap: 2px; + } + + &__link { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + border-radius: 4px; + text-decoration: none; + color: var(--theme-elevation-800); + font-size: 13px; + transition: all 0.15s ease; + + &:hover { + background-color: var(--theme-elevation-50); + color: var(--theme-elevation-900); + } + + &--active { + background-color: var(--theme-elevation-100); + color: var(--theme-text); + font-weight: 500; + + &:hover { + background-color: var(--theme-elevation-150); + } + } + } + + &__link-icon { + font-size: 16px; + width: 20px; + text-align: center; + } + + &__link-label { + flex: 1; + } +} + +[data-theme='dark'] { + .yt-nav-links { + &__link { + &:hover { + background-color: var(--theme-elevation-100); + } + + &--active { + background-color: var(--theme-elevation-150); + + &:hover { + background-color: var(--theme-elevation-200); + } + } + } + } +} diff --git a/src/components/admin/YouTubeAnalyticsNavLinks.tsx b/src/components/admin/YouTubeAnalyticsNavLinks.tsx new file mode 100644 index 0000000..1e51fb1 --- /dev/null +++ b/src/components/admin/YouTubeAnalyticsNavLinks.tsx @@ -0,0 +1,45 @@ +'use client' + +import React from 'react' +import Link from 'next/link' +import { usePathname } from 'next/navigation' + +import './YouTubeAnalyticsNavLinks.scss' + +export const YouTubeAnalyticsNavLinks: React.FC = () => { + const pathname = usePathname() + + const links = [ + { + href: '/admin/youtube-analytics', + label: 'YouTube Analytics', + icon: '📈', + }, + ] + + return ( +
+
+ 🎬 + YouTube +
+ +
+ ) +} + +export default YouTubeAnalyticsNavLinks diff --git a/src/payload.config.ts b/src/payload.config.ts index c621713..4fbcc2e 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -128,14 +128,19 @@ export default buildConfig({ admin: { user: Users.slug, components: { - // Community Management Nav Links - afterNavLinks: ['@/components/admin/CommunityNavLinks#CommunityNavLinks'], - // Custom Views - re-enabled after Payload 3.76.1 upgrade (Issue #15241 test) + afterNavLinks: [ + '@/components/admin/CommunityNavLinks#CommunityNavLinks', + '@/components/admin/YouTubeAnalyticsNavLinks#YouTubeAnalyticsNavLinks', + ], views: { TenantDashboard: { Component: '@/components/admin/TenantDashboardView#TenantDashboardView', path: '/tenant-dashboard', }, + YouTubeAnalyticsDashboard: { + Component: '@/components/admin/YouTubeAnalyticsDashboardView#YouTubeAnalyticsDashboardView', + path: '/youtube-analytics', + }, }, }, },