Watchers/Show
Detailseite eines Watchers mit Metriken-Charts, Profil-Info, Bild-Lightbox und Admin-Aktionen (Rescrape, Video Sync, Block, Score Penalty).
Route: /watcher/{watcher}
View: resources/views/livewire/watchers/show.blade.php
Location: app/Livewire/Watchers/Show.php
Public Properties
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
$watcher | Watcher | -- | Route-Model-Binding |
$confirmingDelete | bool | false | Delete-Confirmation Modal State |
$showImageModal | bool | false | Bild-Lightbox State |
$activeImageSourceId | int|null | null | Aktive Source fuer Lightbox |
$activeImageIndex | int | 0 | Aktueller Bild-Index in Lightbox |
$imageMap | array | [] | Source-ID => Bilder-Array |
$favoriteProfileIds | array | [] | Favorisierte Profile des Users |
$moveWorkspaceId | int|null | null | Ziel-Workspace fuer Move |
Actions
| Method | Parameter | Auth | Beschreibung |
|---|---|---|---|
confirmDelete() | -- | User | Delete-Modal oeffnen |
deleteWatcherConfirmed() | -- | User | Watcher loeschen (Proxy) |
deleteWatcher() | CurrentWorkspace, FavoriteManager | User | Watcher + Sources loeschen, Profil bleibt |
moveToWorkspace(int $id) | Workspace-ID | User | Watcher in anderen Workspace verschieben |
toggleFavorite(int $sourceId) | Source-ID | User | Favorit-Status toggeln |
openImageModal(int $sourceId, int $index) | Source-ID, Bild-Index | User | Lightbox oeffnen |
closeImageModal() | -- | User | Lightbox schliessen |
nextImage() / prevImage() | -- | User | Lightbox-Navigation |
queuePriorityRescrape() | -- | Admin | Priority-Rescrape aller Sources queuen |
requestYouTubeVideoStats() | -- | Admin | YouTube Video Stats Sync starten |
enableYouTubeVideoAutoSync() | -- | Admin | Auto-Sync aktivieren + initialen Sync |
disableYouTubeVideoAutoSync() | -- | Admin | Auto-Sync deaktivieren |
toggleProfileBlock() | -- | Admin | Profil sperren/entsperren |
toggleScorePenalty() | -- | Admin | Score-Strafe (25%) toggeln |
Metriken-Charts
Pro Source/Profil werden plattformspezifische Metriken als Charts dargestellt:
YouTube:
- Followers, Views, Videos, Kommentare
Instagram:
- Followers, Following, Beitraege
Die Chart-Daten werden aus SocialProfileDailyMetric geladen (max. 180 Datenpunkte) und als Zeitserie mit optionalem Delta aufbereitet. Charts nutzen native Flux UI <flux:chart> Komponenten mit automatischer Achsen-Skalierung und Delta-Tooltips (Δ mit signDisplay).
Video Performance Summary
Fuer YouTube-Profile wird eine Video-Performance-Zusammenfassung berechnet:
// Basiert auf YouTubeVideoScore fuer den letzten verfuegbaren Tag
[
'date' => '2026-02-08',
'avg_score' => 72,
'total_scored' => 15,
'top_videos' => [...], // Top 3 nach Score
'flop_videos' => [...], // Bottom 3 nach Score
]
Location: buildVideoPerformanceSummary() in app/Livewire/Watchers/Show.php
Admin-only Features
Alle Admin-Aktionen sind durch $this->authorizeAdmin() geschuetzt (Gate::allows('admin')).
AI Enhancer Anzeige
Unterhalb der "Letzte Aktualisierung"-Zeile wird fuer Admins das Datum der letzten AI-Enhancer-Ausfuehrung angezeigt, sofern $profile->detected_at gesetzt ist. Sichtbarkeit via @can('admin') im Blade-Template.
Format: "Letzte Aktualisierung durch den AI-Enhancer: 9. Februar 2026, 14:30 Uhr"
Priority Rescrape
Erzeugt einen neuen WatcherImportRun und dispatcht ImportWatcherFromUrl-Jobs auf der Priority-Queue (imports-youtube-priority). Instagram-Sources nutzen den CollectorJobDispatcher mit erhoehter Priority.
YouTube Video Stats
requestYouTubeVideoStats(): Erstellt/aktualisiertYouTubeVideoSync-Eintrag und dispatchtSyncYouTubeVideoStats-Job (Queue:imports-youtube-video-priority, Mode:full). Aufruf via Form-POST anwatchers.youtube-video-stats.enableYouTubeVideoAutoSync()/disableYouTubeVideoAutoSync(): Toggle fuer Auto-Sync. Im View als Form-POST an dedizierte Controller-Routes implementiert (watchers.youtube-video-auto-sync.enable/.disable), da$wire-Calls innerhalb von Flux-Dropdowns unzuverlaessig sind (Dropdown schliesst vor Wire-Ausfuehrung).
Profil-Block
Toggelt blocked_at/blocked_by/block_reason auf dem SocialProfile. Geblockte Profile werden aus Explore, Related und Scoring ausgeschlossen.
Score Penalty
Toggelt manual_penalty zwischen 0 und 25 (25% Abzug auf den Postbox Score).
Events
| Richtung | Event | Beschreibung |
|---|---|---|
| dispatch | show-toast | Erfolgs-/Fehlermeldungen |
| listen | youtube-sync-completed | Seite neu laden nach Video-Sync |
Autorisierung
authorizeWatcher() prueft ob der Watcher zum aktuellen User-Workspace gehoert. Admins erhalten Zugriff auf den Admin-Workspace (AdminWorkspaceManager::ADMIN_WORKSPACE_ID).
Profil-Bilder
ensureProfileImageStored() prueft bei jedem Seitenaufruf:
- Kein Bild in DB → Bild erstmalig von Platform-API holen (YouTube/Instagram)
- Bilder in DB → Proaktive R2-Verifizierung:
Storage::exists()pruefen ob die primaere Bilddatei in R2 tatsaechlich existiert. Falls nicht: stale DB-Records loeschen und Bild sofort neu von der Platform-API holen. - Rate-Limit →
exists()-Check nur alle 10 Minuten pro Profil (Cache-Key:profile-image-verified:{profile_id})
Die Lightbox (flux:lightbox) zeigt alle gespeicherten Bilder chronologisch sortiert. Als src wird $displayImageUrl verwendet (Fallback-Kette: gespeichertes Bild → externe thumbnail_url → Placeholder), damit auch bei fehlenden R2-Dateien sofort ein Bild angezeigt wird.
Wichtig — Lazy Loading + Visibility: Die flux:lightbox.image Komponente (resources/views/flux/lightbox/image.blade.php) nutzt loading="lazy" auf dem <img>. Thumbnails duerfen NICHT mit x-show (= display:none) versteckt werden, da der Browser unsichtbare Bilder nicht laedt (Deadlock). Stattdessen wird opacity-0/opacity-100 verwendet — das Bild bleibt im Layout sichtbar, ist aber visuell erst nach dem Load-Event sichtbar. Das Skeleton-Overlay liegt per z-10 darueber.