Zum Hauptinhalt springen

Dashboard/Index

Zentrales Dashboard mit Follower-Verlauf, Winners/Losers-Listen und Top-Ranglisten fuer den aktuellen Workspace.

Route: /dashboard View: resources/views/livewire/dashboard/index.blade.php Location: app/Livewire/Dashboard/Index.php

Public Properties

PropertyTypDefaultQuery StringBeschreibung
$workspaceWorkspace|null--neinAktueller Workspace (Route-Model oder Default)
$periodstring'7d'jaZeitraum: '3d', '7d', '14d'
$chartSeriesstring'followers'jaChart-Metrik: followers, views, videos, etc.
$onlyFavoritesboolfalsejaNur Favoriten anzeigen
$winnersarray[]neinTop-Gewinner des Zeitraums
$losersarray[]neinTop-Verlierer des Zeitraums
$topFollowersarray[]neinTop-Profile nach Followern
$topGrowtharray[]neinTop-Profile nach prozentualem Wachstum
$chartDataarray[]neinKumulative Chart-Daten
$chartXTickValuesarray[]neinX-Achsen-Ticks (Datumswerte)
$chartYTickValuesarray[]neinY-Achsen-Ticks (gerundete Schrittweiten)
$chartMaxint0neinMaximaler Chart-Wert
$rangeEndDatestring|nullnullneinEnd-Datum fuer Navigation
$canGoPreviousboolfalseneinRueckwaerts-Navigation verfuegbar
$canGoNextboolfalseneinVorwaerts-Navigation verfuegbar

Verfuegbare Metriken

MetrikLabelVerfuegbar fuer
followersFollowerYouTube, Instagram
viewsViewsYouTube
videosVideosYouTube
postsBeitraegeInstagram
followingFollowingInstagram

Actions

MethodBeschreibung
updatedPeriod()Daten fuer neuen Zeitraum laden
updatedChartSeries()Chart-Daten fuer neue Metrik laden
updatedOnlyFavorites()Filter umschalten, Daten neu laden
goPreviousRange()Zeitfenster zurueck verschieben
goNextRange()Zeitfenster vor verschieben

Datenquellen

Die Dashboard-Daten werden aus vorberechneten Rollups geladen -- nicht live aus den Metriken-Tabellen:

  1. dashboard_workspace_snapshots: Workspace-spezifische Aggregate pro Periode
  2. SocialProfileDailyMetric: Fallback fuer Chart-Daten falls kein Snapshot

Die Rollups werden taeglich um 00:10 UTC durch dashboard:rollup-daily-metrics erzeugt.

Workspace-Switcher

Der Workspace-Kontext wird ueber die separate WorkspaceSwitcher-Komponente im Header gesetzt. Dashboard reagiert auf Workspace-Wechsel durch Neuinitialisierung in mount().

Date-Range-Navigation

Der Zeitraum laesst sich in Schritten der gewaehlten Periodenlaenge (3/7/14 Tage) vor- und zurueckblaetern. Die Navigation wird begrenzt durch:

  • Rueckwaerts: Fruehester verfuegbarer Datenpunkt
  • Vorwaerts: Gestern (aktuellster vollstaendiger Tag)

Events

RichtungEventBeschreibung
dispatchshow-toastFehlermeldungen bei Datenlade-Problemen
listendaily-scrape-completedDaten nach Scrape-Abschluss neu laden

Performance

  • Rollup-basiert statt Live-Queries
  • chunkById() fuer grosse Workspace-Aggregationen
  • withCount() statt separate Queries
  • Charts nutzen native Flux UI <flux:chart> Komponenten (Achsen-Skalierung automatisch)

Sub-Components

Das Dashboard bindet zwei eigenstaendige Slider-Components ein, die unterhalb des Haupt-Charts angezeigt werden.

Dashboard/TrendingSlider

Horizontaler Slider mit aktuell trendigen Profilen (hoher Postbox Score 80–100), unabhaengig vom User-Tracking.

Route: Eingebettet in /dashboard View: resources/views/livewire/dashboard/trending-slider.blade.php Location: app/Livewire/Dashboard/TrendingSlider.php

Public Properties

PropertyTypDefaultBeschreibung
$trendingProfilesarray[]Bis zu 25 Trending-Profile
$addedProfileIdsarray[]Map social_profile_id → watcher_id fuer bereits getrackte Profile

Actions

MethodBeschreibung
mount()Laedt Trending-Profile aus Cache (1h TTL)
addToWorkspace(int $socialProfileId)Profil zum ersten regulaeren Workspace hinzufuegen (mit Kapazitaetspruefung)

Datenquelle und Algorithmus

  1. Aktuelle Score-Berechnung aus SocialProfileScore ermitteln (letztes calculated_at Datum)
  2. Profile mit Score 80–100, tracking_enabled=true und notExcluded()-Filter
  3. Zufaellige Auswahl von 25 Profilen (inRandomOrder()->limit(25))
  4. Sortierung nach Score absteigend fuer Anzeige
  5. Konditionale Ausschluss-Logik: User mit ≤1.000 Watchern sehen eigene Profile nicht; bei >1.000 keine Ausschlussfilterung

Cache: 1h TTL, Key "trending_slider:{$user->id}"

UI-Elemente pro Karte

  • Profilbild mit Platform-Badge (YouTube/Instagram)
  • Postbox Score Badge (oben links)
  • Verified-/PRO-Badge (oben rechts)
  • Handle, Follower-Count
  • "Hinzufuegen"- oder "Ansehen"-Button

Dashboard/RecommendedProfiles

Personalisierter Empfehlungs-Slider ("Koennte dich interessieren") basierend auf den Kategorien der vom User getrackten Profile.

Route: Eingebettet in /dashboard View: resources/views/livewire/dashboard/recommended-profiles.blade.php Location: app/Livewire/Dashboard/RecommendedProfiles.php

Public Properties

PropertyTypDefaultBeschreibung
$recommendedProfilesarray[]Bis zu 25 empfohlene Profile
$addedProfileIdsarray[]Map social_profile_id → watcher_id

Actions

MethodBeschreibung
mount()Laedt Empfehlungen aus Cache (6h TTL)
addToWorkspace(int $socialProfileId)Profil zum ersten regulaeren Workspace hinzufuegen (mit Kapazitaetspruefung)

Architektur: Global Pool + Per-User Personalisierung

Die Empfehlungen nutzen eine zweistufige Architektur fuer maximale Performance:

Stufe 1 — Globaler Pool (geteilt, 6h Cache):

  • Top 1.000 Profile nach Postbox Score (≥50)
  • Filter: notExcluded(), tracking_enabled, keine "other"-Kategorie, Mindest-Follower
  • JOIN auf explore_profile_metrics (Kategorie) + social_profile_scores (Score)
  • Inklusive vorberechneter Kategorie-Metadata (Icon, deutsches Label)
  • Cache-Key: recommended_profiles_global_pool

Stufe 2 — Per-User Personalisierung (6h Cache pro User):

  • 1 Query: User's getrackte Profile-IDs laden (als Hash-Map fuer O(1)-Lookup)
  • 1 Query: User's Kategorie-Praeferenzen (GROUP BY + ORDER BY auf explore_profile_metrics)
  • Reines PHP: Pool filtern → getrackte Profile raus → nach User-Kategorien sortieren → 5 pro Kategorie → 25 total → Score-absteigend
graph TD
A["Globaler Pool (6h Cache)"] --> B["Top 1.000 nach Score"]
B --> C["Per-User Cache (6h)"]
C --> D["Getrackte Profile-IDs laden"]
D --> E["Getrackte aus Pool entfernen"]
E --> F["User-Kategorien ermitteln"]
F --> G["Nach Kategorien sortieren"]
G --> H["Max. 5 pro Kategorie"]
H --> I["Max. 25 total, Score desc"]

Performance: ~5ms pro User statt ~300ms+ (normal) / TIMEOUT (Admin mit 469k Watchern).

Cache:

  • Global Pool: 6h TTL, Key recommended_profiles_global_pool
  • Per-User: 6h TTL, Key recommended_profiles:{$user->id}

UI-Elemente pro Karte

  • Profilbild mit Platform-Badge
  • Postbox Score Badge (oben links)
  • Kategorie-Icon (oben rechts)
  • Handle, Follower-Count, Kategorie-Label mit Icon
  • "Hinzufuegen"- oder "Ansehen"-Button

Slider-Navigation (Alpine.js)

Beide Slider nutzen inline Alpine.js fuer horizontales Scrollen. Navigation per Pfeil-Buttons (links/rechts), die sich automatisch ein-/ausblenden:

  • Linker Pfeil: Sichtbar wenn scrollLeft > 0
  • Rechter Pfeil: Sichtbar wenn scrollLeft < maxScroll
  • Scroll-Logik: scrollRight() nutzt Math.min(rawScroll, maxScroll) um sicherzustellen, dass der letzte Slide korrekt maxScroll erreicht und der Pfeil verschwindet
  • Keine Tooltips: Karten zeigen keine nativen Browser-Tooltips (flux:tooltip entfernt)

Lazy Loading + Skeleton

Beide Slider-Components (TrendingSlider, RecommendedProfiles) nutzen #[Lazy] fuer asynchrones Laden per XHR. Waehrend des Ladens wird der gemeinsame Flux UI Skeleton-Platzhalter mit Shimmer-Animation angezeigt. Layout der Skeletons entspricht den tatsaechlichen Karten (Bildbereich, Textzeilen, Metriken, Button).

Shared Placeholder View: resources/views/livewire/partials/slider-placeholder.blade.php

Derselbe Placeholder wird auch von den Watcher-Slidern (RelatedChannels, RelatedProfiles, CrossPlatformRelated) verwendet. Alle horizontalen Profil-Slider teilen sich eine einzige View-Datei mit <flux:skeleton.group animate="shimmer"> und 7 Karten-Skeletons im w-48-Format.

Vergleich TrendingSlider vs. RecommendedProfiles

AspektTrendingSliderRecommendedProfiles
PersonalisierungKeine (global)Kategorie-basiert (Global Pool + User-Filter)
Cache TTL1 Stunde (per User)6h global + 6h per User
Score-Range80–100≥50
AuswahlZufaelligKategorie-gruppiert (max. 5/Kat.)
BadgesVerified/PROKategorie-Icon
Max. Profile2525
Lazy LoadingJa (#[Lazy])Ja (#[Lazy])