Zum Hauptinhalt springen

Watcher Sub-Components

Untergeordnete Livewire-Komponenten, die auf der Watcher-Detailseite (Watchers\Show) eingebettet werden.

Lazy Loading

Folgende Sub-Components nutzen #[Lazy] fuer asynchrones Laden per XHR (Viewport-basiert):

ComponentPlaceholder
ContactLinksInline-Skeleton (Link-Liste mit 4 Icon-Zeilen)
RelatedChannelsShared Slider: livewire.partials.slider-placeholder
RelatedProfilesShared Slider: livewire.partials.slider-placeholder
CrossPlatformRelatedShared Slider: livewire.partials.slider-placeholder
VideoStatsChartInline-Skeleton (Stats-Grid + Chart)
AggregatedVideoChartInline-Skeleton in <flux:card>

Die drei Related-Slider teilen sich denselben Flux UI Placeholder (<flux:skeleton.group animate="shimmer">, 7 Karten im w-48-Format) wie die Dashboard-Slider (TrendingSlider, RecommendedProfiles).

Nicht-Lazy: ApexCharts-Komponenten

ScoreChart und VideoScoreChart werden nicht lazy geladen. Beide nutzen wire:ignore fuer ApexCharts mit dynamischem CDN-Script-Loading. Livewires Lazy-Morph (Placeholder → Content) initialisiert wire:ignore-Elemente nicht korrekt — Alpine's init() feuert nicht und der Chart wird nicht gerendert. Diese Components werden daher eager geladen.

Testen von Lazy Components: Livewire::test() rendert initial den Placeholder. Fuer Assertions auf den echten Inhalt muss ->call('$refresh') aufgerufen werden, um einen normalen Render-Zyklus auszuloesen.


Kontakt-Links eines Profils anzeigen und verwalten. Admin-Workflow fuer Approve/Reject, User-Vorschlaege, Re-Parse.

View: resources/views/livewire/watchers/contact-links.blade.php Location: app/Livewire/Watchers/ContactLinks.php

Public Properties

PropertyTypDefaultBeschreibung
$profileIdint--ID des SocialProfile
$watcherId?intnullID des Eltern-Watchers (optional)
$newLinkInputstring''Neuer Link: URL/Email/Handle Eingabe
$newLinkTypestring'social'Neuer Link: Typ (social, email, url)
$newLinkPlatformstring''Neuer Link: Auto-detected Plattform
$showAddFormboolfalseEingabeformular sichtbar
$detectedPlatform?stringnullAuto-Erkennung: Plattform aus URL
$detectedHandle?stringnullAuto-Erkennung: Handle aus URL

Actions

MethodBeschreibungBerechtigung
addLink()Link hinzufuegen (Admin: direkt, User: Vorschlag)Alle authentifizierten User
toggleApproval(int $id)Link genehmigen/ablehnen + Auto-Import triggernAdmin
deleteLink(int $id)Link loeschenAdmin
reparseLinks()Profil-Beschreibung erneut parsenAdmin
retryImport(int $id)Fehlgeschlagenen Import erneut versuchenAdmin
editLink(int $id)Plattform-Zuordnung aendern (Inline-Edit)Admin
updateLink()Plattform-Aenderung speichernAdmin
cancelEdit()Inline-Edit abbrechenAdmin
validateLink(int $id)Link-URL auf Erreichbarkeit pruefenAdmin

Computed Properties

PropertyBeschreibung
linksAlle Links des Profils (Non-Admins: nur genehmigte)
linkedWatcherUrlsMap link_id → Watcher-URL für verknuepfte Profile im selben Workspace

Auto-Import bei Freigabe

Wenn ein Admin einen YouTube/Instagram-Kontaktlink freigibt (toggleApproval), wird maybeImportProfile() aufgerufen:

  1. Profil existiert: Handle wird direkt mit linked_social_profile_id verknuepft, import_status = completed (Auto-Approval)
  2. Profil nicht gefunden: ImportContactLinkProfile-Job wird dispatcht, import_status = pending

UI-Elemente

  • Interne Links: Verknuepfte Profile zeigen indigo-farbene Links mit wire:navigate zum Watcher
  • Import-Status Badges: "Import..." (blau, pending) oder "Fehler" (rot, failed) mit Tooltip
  • Retry-Button: Pfeil-Icon bei fehlgeschlagenen Imports (nur Admin)

User-Vorschlaege

Nicht-Admins koennen Links vorschlagen (source='user_suggested', suggested_by gesetzt). Admins werden via NotificationService::announceToAll() benachrichtigt. Vorgeschlagene Links erscheinen erst nach Admin-Genehmigung oeffentlich.

Links werden aus dem SocialProfileLink-Model geladen. Geparste Links (aus Profil-Beschreibungen via ProfileDescriptionParser) durchlaufen einen Approval-Workflow. Genehmigte Links (approved_at IS NOT NULL) werden in Explore-Profil-Cards angezeigt und fliessen ins Related-Scoring ein (approved=30pt, auto-parsed=25pt).


Watchers\CrossPlatformRelated

Cross-Platform Related Profiles: zeigt die Verknuepfung zwischen YouTube- und Instagram-Profilen desselben Creators.

View: resources/views/livewire/watchers/cross-platform-related.blade.php Location: app/Livewire/Watchers/CrossPlatformRelated.php

Public Properties

PropertyTypDefaultBeschreibung
$watcherWatcher--Eltern-Watcher
$relatedProfilesCollection--Cross-Platform Profile

Nutzt das CrossPlatformRelatedProfile-Model. Ausschluss-Filter (blocked_at, sanitized_at, archived_at) werden auf die Related-Profile angewendet.


Watchers\RelatedChannels

Aehnliche YouTube-Kanaele zu einem Watcher. Berechnung ueber die YouTube Data API (Related Topics, Keywords).

View: resources/views/livewire/watchers/related-channels.blade.php Location: app/Livewire/Watchers/RelatedChannels.php

Public Properties

PropertyTypDefaultBeschreibung
$watcherWatcher--Eltern-Watcher
$relatedChannelsCollection--Berechnete Related Channels

Actions

MethodBeschreibung
calculate()FindRelatedYouTubeChannels-Job dispatchen

Der Button loest einen Background-Job aus (youtube-related-channels-Queue). Ergebnisse werden via RelatedProfilesCalculated-Event ueber Reverb zurueckgemeldet.

Ausschluss-Filter Pattern:

->whereHas('relatedProfile', fn ($q) => $q
->whereNull('blocked_at')
->whereNull('sanitized_at')
->whereNull('archived_at')
)

Location: app/Livewire/Watchers/RelatedChannels.php


Watchers\RelatedProfiles

Aehnliche Instagram-Profile zu einem Watcher. Matching basiert auf Keywords und Bio-Analyse.

View: resources/views/livewire/watchers/related-profiles.blade.php Location: app/Livewire/Watchers/RelatedProfiles.php

Public Properties

PropertyTypDefaultBeschreibung
$watcherWatcher--Eltern-Watcher
$relatedProfilesCollection--Berechnete Related Profiles

Actions

MethodBeschreibung
calculate()FindRelatedInstagramProfiles-Job dispatchen

Gleiche Architektur wie RelatedChannels: Job-Dispatch, Reverb-Event, Ausschluss-Filter.


Watchers\ScoreChart

Postbox Score Verlauf mit Komponenten-Breakdown (Growth, Momentum, Consistency, Engagement).

View: resources/views/livewire/watchers/score-chart.blade.php Location: app/Livewire/Watchers/ScoreChart.php

Public Properties

PropertyTypDefaultBeschreibung
$watcherWatcher--Eltern-Watcher
$scoreDataarray[]Score-Verlauf (date, score)
$componentScoresarray[]Komponenten-Breakdown

Datenquelle: SocialProfileScore-Model. Der Chart zeigt den Gesamtscore (0-100) als Linie und die vier Komponenten als gestapelte Bereiche:

KomponenteGewichtung
Growth40%
Momentum30%
Consistency20%
Engagement10%

Score-Status wird farblich kodiert: pending (grau), preliminary (gelb), stable (gruen), no_data (rot).


Watchers\VideoScoreChart

Video Performance Score Chart pro YouTube-Video auf der Watcher-Detailseite. Zeigt den Score-Verlauf der letzten 30 Tage mit Gap-Interpolation.

View: resources/views/livewire/watchers/video-score-chart.blade.php Location: app/Livewire/Watchers/VideoScoreChart.php

Pattern

Adaptiert vom ScoreChart-Component (1:1 Pattern). Wesentliche Unterschiede:

AspektScoreChartVideoScoreChart
DatenquelleSocialProfileScoreYouTubeVideoScore
Parameter$socialProfileId (int)$videoId (string)
"Aehnliche anzeigen"-ButtonJaNein
Followers-CheckJa (Mindest-Follower)Nein
Score-ErklaerungGrowth 40%, Momentum 30%, Consistency 20%, Engagement 10%View Performance 40%, Engagement 25%, Growth 20%, Freshness 15%

Public Properties

PropertyTypDefaultBeschreibung
$videoIdstring--YouTube Video ID
$daysint30Zeitraum in Tagen

Computed Properties

PropertyBeschreibung
currentScoreNeuester YouTubeVideoScore-Eintrag mit Score
scoreDeltaDifferenz zwischen den letzten zwei Score-Werten
chartEligibletrue wenn >= 2 Score-Datenpunkte existieren
chartDataZwei Serien: solid (echte Datenpunkte) + dashed (interpolierte Luecken)

Chart-Rendering

ApexCharts-Liniendiagramm (CDN) mit:

  • Solid-Serie: Echte Score-Datenpunkte mit Delta-Berechnung
  • Dashed-Serie: Linear interpolierte Werte fuer Tage ohne Score. Boundary-Points ueberlappen mit Solid-Serie fuer nahtlose Verbindung.
  • Y-Achse: Fest 0-100
  • Tooltip: Datum, Score, Delta zum Vortag
  • Erklaerungsmodal: Video-spezifische Score-Gewichtung (40/25/20/15)

Integration

Eingebettet unterhalb jedes Video-Cards auf der Watcher-Detailseite:

<livewire:watchers.video-score-chart
:video-id="$video->video_id"
:key="'video-score-chart-' . $video->video_id"
/>

Wird nur gerendert wenn chartEligible (>= 2 Datenpunkte) UND currentScore existiert.

Tests

Location: tests/Feature/Livewire/Watchers/VideoScoreChartTest.php

TestBeschreibung
renders without errors (no scores)Leerer State ohne Fehler
not eligible with < 2 data pointschartEligible = false bei 1 Datenpunkt
eligible with >= 2 data pointschartEligible = true, currentScore, scoreDelta korrekt
calculates score delta correctlyDelta = neuester Score - vorheriger Score
provides solid and dashed seriesGap-Interpolation erzeugt beide Serien