Zum Hauptinhalt springen

Explore Components

Der Explore-Bereich umfasst sechs Components fuer die oeffentliche Profil-Entdeckung.

Explore/Index

Startseite mit kuratierten Sektionen: Trending, Fastest Growing, Rising Stars, Top Scores, Score Breakouts, Neue Profile, Trending Videos und Top Performing Videos.

Route: /explore View: resources/views/livewire/explore/index.blade.php Location: app/Livewire/Explore/Index.php

Public Properties

PropertyTypDefaultURL-ParamBeschreibung
$platformstring'all'jaPlattform-Filter
$countrystring'all'jaLaender-Filter (ISO 3166-1 alpha-2)
$categorystring'all'jaKategorie-Filter (ExploreCategory)
$tierstring'all'jaFollower-Tier: micro/small/medium/large/mega
$verifiedstring'all'jaVerifizierungs-Filter: verified/unverified
$sortstring'trending'jaSortierung (nur fuer Browse)
$perPageint24neinKarten pro Sektion
$addedProfileIdsarray[]neinprofileId => watcherId Mapping

Computed Properties

PropertyCacheBeschreibung
trendingProfilesneinTop 12 nach trending_score
fastestGrowingneinTop 12 nach growth_score
risingStarsneinTop 12 mit is_rising_star = true
topScoresneinTop 12 nach social_profile_scores.score
scoreBreakoutsneinProfile die in den letzten 7 Tagen Score >= 70 erreicht haben
newProfilesneinTop 12 mit is_new = true
trendingVideosneinTop 8 nach trending_score (ExploreTrendingVideo)
topPerformingVideosneinTop 8 nach Video Performance Score (YouTubeVideoScore)
availableCountries24hLaender mit >= 10 Profilen
availableCategories24hAktive Kategorien mit > 0 Profilen
stats24hGesamtstatistik (total, trending, rising_stars, countries)

Actions

MethodBeschreibung
addToWorkspace(int $socialProfileId)Profil als Watcher zum ersten regulaeren Workspace hinzufuegen
resetFilters()Alle Filter zuruecksetzen
loadAddedProfileIds(array $ids)Pruefen welche Profile bereits getrackt werden

Base Query

Die getProfilesQuery() baut die Standard-Query mit:

  • JOIN auf explore_profile_metrics
  • Ausschluss-Filter: blocked_at, sanitized_at jeweils IS NULL
  • Minimum-Follower-Filter: ExploreProfileMetric::MIN_FOLLOWERS_FOR_EXPLORE
  • Subquery fuer avg_video_score (letzten 7 Tage)
  • Subquery fuer approved_contact_count (genehmigte Links)

Explore/Browse

Paginierter Profil-Browser mit erweiterten Filtern (zusaetzlich zu Index: Tag, Score-Range).

Route: /explore/browse View: resources/views/livewire/explore/browse.blade.php Location: app/Livewire/Explore/Browse.php

Zusaetzliche Properties

PropertyTypDefaultURL-ParamBeschreibung
$tagstring''jaAI-generiertes Keyword filtern
$minScoreint0jaMinimum Postbox Score
$maxScoreint100jaMaximum Postbox Score
$scoreRangearray[0, 100]neinFlux UI Range Slider State

Computed Properties

PropertyCacheBeschreibung
availableTags24hAlle Tags mit >= 2 Verwendungen, ohne Stop-Words und Blocked Tags

Actions

MethodBeschreibung
resetFilters()Alle Filter inkl. Tag und Score-Range zuruecksetzen
clearTag()Tag-Filter loeschen
addToWorkspace(int $socialProfileId)Profil in Workspace aufnehmen

Explore/TrendingVideos

Trending YouTube Videos mit Velocity- und Trending-Score.

Route: /explore/trending-videos View: resources/views/livewire/explore/trending-videos.blade.php Location: app/Livewire/Explore/TrendingVideos.php

Nutzt ExploreTrendingVideo-Model mit Recency-Filter. Sortierung nach trending_score * velocity_score.


Lazy Loading (Explore)

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

ComponentPlaceholderBeschreibung
ProfileGridShared Grid: livewire.partials.grid-placeholder (14 Items)Profil-Card-Skeletons mit Bild, Info, Score-Bar, Button
TrendingSectionShared Grid: livewire.partials.grid-placeholder (7 Items + Heading)Wie ProfileGrid, mit Ueberschrift-Skeleton
TagCloudlivewire.partials.tag-cloud-placeholder20 Pill-Skeletons mit zufaelliger Breite
CategoryCloudlivewire.partials.category-cloud-placeholder12 Badge-Skeletons mit zufaelliger Breite

Alle Placeholder nutzen <flux:skeleton.group animate="shimmer"> fuer einheitliche Shimmer-Animation. Die Grid-Placeholder sind parametrisierbar via $count (Anzahl Karten) und $heading (Ueberschrift-Skeleton).

Shared Placeholder Views: resources/views/livewire/partials/


Explore/ProfileGrid

Wiederverwendbare Grid-Komponente fuer Profil-Karten.

View: resources/views/livewire/explore/profile-grid.blade.php Location: app/Livewire/Explore/ProfileGrid.php

Empfaengt Profil-Collection vom Eltern-Component und rendert ExploreProfileCards. Dispatcht addToWorkspace-Events an den Eltern.


Explore/TagCloud

Tag-Wolke mit gewichteter Darstellung basierend auf AI-generierten Keywords.

View: resources/views/livewire/explore/tag-cloud.blade.php Location: app/Livewire/Explore/TagCloud.php

Daten aus social_profiles.ai_keywords (JSONB), gecacht fuer 24h. Tags werden nach Haeufigkeit gewichtet (Schriftgroesse). Blocked Tags (BlockedTag) und Stop-Words (config('postbox.tag_stop_words')) werden gefiltert.


VideoTrends\Index

Top-Level-Seite fuer Video-spezifische Leaderboards und Trends. Zeigt Score-Rankings, View-Growth-Rankings und aktuell trendende Videos.

Route: /video-trends View: resources/views/livewire/video-trends/index.blade.php Location: app/Livewire/VideoTrends/Index.php

Public Properties

PropertyTypDefaultBeschreibung
$durationstring'all'Duration-Filter: all/short/medium/long/extended/extra_long
$categorystring'all'Kategorie-Filter (ExploreCategory slug)
$countrystring'all'Laender-Filter (ISO 3166-1 alpha-2, URL-synced)
$favoritesFilterstring'all'Favoriten-Filter: all/favorites
$periodstring'7d'Zeitraum: 7d/30d (fuer Growth-Berechnung)
$hasFavoritesboolfalseOb der User Favoriten hat
$kpisarray[]KPI-Daten (total_scored, avg_score, top_performers, trending_count)
$topPerformersarray[]Top 10 Videos nach hoechstem Score
$flopPerformersarray[]Top 10 Videos nach niedrigstem Score
$growthWinnersarray[]Top 10 Videos nach hoechstem View-Growth
$growthLosersarray[]Top 10 Videos nach niedrigstem View-Growth
$trendingVideosarray[]Top 10 Videos nach Trending Score

Computed Properties

PropertyCacheBeschreibung
availableCategories24hAktive Kategorien mit profile_count > 0
availableCountries24hLaender mit >= 5 Videos (Join ueber SocialProfile)
durationLabels--Duration-Filter-Labels (deutsch)

Sektionen

KPI-Boxen (4 Cards)

BoxWertQuelle
Videos mit ScoreAnzahl Videos mit aktivem ScoreYouTubeVideoScore (latest date, score NOT NULL)
Ø Video ScoreDurchschnittlicher ScoreYouTubeVideoScore (latest date)
Top PerformerVideos mit Score >= 80YouTubeVideoScore (latest date)
Im TrendTrending-VideosExploreTrendingVideo::count()

Alle 4 KPI-Boxen nutzen flux:tooltip mit position="bottom" und erklaerenden Texten (z.B. "Videos mit einem Performance Score von 80 oder hoeher").

Top / Flop Performer (2 Spalten)

Score-basierte Leaderboards: Top 10 nach hoechstem Score (links) und Top 10 nach niedrigstem Score (rechts). Pro Video: Thumbnail, Titel, Kanal, Score-Badge, Duration-Badge.

Datenquelle: YouTubeVideoScore (latest date) + YouTubeVideo + SocialProfile

Groesstes Wachstum / Groesster Rueckgang (2 Spalten)

View-Growth-basierte Leaderboards. Wachstumsfeld abhaengig vom Period-Filter: view_growth_7d (bei 7d) oder view_growth_24h (bei 30d).

Datenquelle: ExploreTrendingVideo (pre-calculated)

Aktuell im Trend (2-Spalten-Grid)

Top 10 Videos nach trending_score. Mit Velocity-Badges und Trending-Score-Bars.

Datenquelle: ExploreTrendingVideo (Scope trending())

Filter-Implementierung

FilterImplementierung
DurationSubquery auf YouTubeVideo.duration_seconds (bei Score-Queries) oder direkte WHERE-Klausel (bei Trending-Queries)
KategorieSubquery auf ExploreProfileMetric.primary_category via social_profile_id
LandJoin ueber SocialProfile (country + detected_country via COALESCE). Min. 5 Videos/Land fuer Dropdown. 24h-Cache.
FavoritenwhereIn('social_profile_id', $favoriteProfileIds). User-spezifisch, nicht gecacht.
ZeitraumBestimmt welches Growth-Feld verwendet wird (view_growth_7d vs. view_growth_24h)

Duration-Filter-Schwellenwerte

KeyBereich (Sekunden)Label
short0 - 60Kurz (< 1 Min)
medium0 - 180Mittel (< 3 Min)
long0 - 900Lang (< 15 Min)
extended0 - 1800Erweitert (< 30 Min)
extra_long1800+Sehr lang (>= 30 Min)

Caching

Cache::remember("video-trends:kpi:{$duration}:{$category}", 3600, fn() => ...)

Globaler 1h-Cache fuer KPIs und Leaderboards (ohne Favorites-Filter). Favorites-Filter wird in-memory angewendet (user-spezifisch, nicht cachebar).

Neuer Eintrag "Video Trends" mit Icon play-circle zwischen "Tops & Flops" und "Explore".


Cache-Strategie

Alle Explore-Daten basieren auf ExploreProfileMetric, das taeglich um 03:00 UTC durch explore:calculate --type=all neu berechnet wird. Computed Properties mit persist: true und Cache::remember() (24h TTL) verhindern redundante Queries.


Suche & Discover (Discover/Index)

Globale Profil-Suche mit volltext-Matching (PostgreSQL ILIKE), Filter, Bulk-Select, und Workspace-Modal. UI-Label: „Suche & Discover" (DE) / „Search & Discover" (EN).

Route: /discover View: resources/views/livewire/discover/index.blade.php Location: app/Livewire/Discover/Index.php

Public Properties

PropertyTypDefaultURL-ParamBeschreibung
$searchstring''jaSuchbegriff (min. 2 Zeichen)
$platformstring'all'jaPlattform-Filter (all/youtube/instagram)
$countrystring'all'jaLand-Filter (ISO 3166-1 alpha-2)
$categorystring'all'jaKategorie-Filter
$tierstring'all'jaFollower-Tier: micro/small/medium/large/mega
$limitint24neinMaximale Resultat-Anzahl
$bulkModeboolfalseneinMulti-Select aktiv
$bulkSelectedIdsarray[]neinIDs ausgewaehlter Profile
$selectedProfileIdint|nullnullneinProfil fuer Workspace-Modal
$selectedWorkspaceIdint|nullnullneinWorkspace aus Modal-Auswahl
$showWorkspaceModalboolfalseneinModal sichtbar
$rateLimitedboolfalseneinRate Limiter aktiv

Computed Properties

PropertyCacheBeschreibung
resultsneinSuchresultate (ProfileSearchService)
addedProfileIdsneinMap profileId => watcherId (bereits getracked)
recentSearchesneinLetzte 10 Suchanfragen des Users
availableCountries24hLaender mit >= 10 Profilen
availableCategories24hAktive Kategorien mit Profile-Count
userWorkspacesneinRegulaere Workspaces (keine Favorites/Admin)

Actions

MethodBeschreibung
addToWorkspace(int $profileId)Single-Add mit Modal (falls >1 Workspace)
confirmAddToWorkspace()Modal bestaetigt, fuehrt Add aus
bulkAddToWorkspace()Alle selected Profile adden
confirmBulkAddToWorkspace()Bulk Modal-Bestaetigung
toggleBulkSelect(int $profileId)Profil togglen in Auswahl
toggleBulkMode()Bulk-Mode an/aus, cleared Selection
applySearch(string $query)Recent Search uebernehmen
loadMore()Limit um 24 erhoehen (pagination)
resetFilters()Alle Filter zurueck auf defaults

Suchverhalten

Rate Limiting: 5 neue Suchanfragen pro 10 Sekunden pro User.

  • Wird nur fuer neue Queries gezaehlt, nicht fuer Filter-Aenderungen
  • updatedSearch() prueft MinLength, rateLimiter Key ist discover-search:{userId}
  • Bei Limit: Toast-Warning, $rateLimited = true, Results = empty

Suchhistorie:

  • Nur nicht-leere Ergebnisse werden in SearchHistory aufgezeichnet
  • SearchHistory::record($userId, $query) mit Deduplizierung
  • recentSearches() liefert letzte 10 unique Queries per User

Filter & Multisearch

Alle Filter sind URL-Parameter:

/discover?search=tech&platform=youtube&country=DE&tier=large&category=technology

Filter-Aenderungen triggern:

  • limit = 24 (paginierung reset)
  • bulkSelectedIds = [] (auswahl clear)

Workspace-Modal

Zeigt sich, wenn:

  1. User hat >1 Workspace → Modal fordert Workspace-Auswahl
  2. Single-Add oder Bulk-Add zu Multi-Workspace-User
  3. Unterschiedliches Template fuer Single vs. Bulk (selectedProfileId null bei Bulk)

Flow:

  1. User klickt "+ Workspace" auf Profil-Karte
  2. addToWorkspace($id) checkt Workspace-Count
  3. Falls >1: showWorkspaceModal = true, Modal oeffnet
  4. User waehlt Workspace, klickt bestaetigen
  5. confirmAddToWorkspace() oder confirmBulkAddToWorkspace()
  6. performAdd() / performBulkAdd() erstellen Watcher + WatcherSource
  7. Toast, Modal schliesst, addedProfileIds recomputed

Transaktionen & Duplikat-Check

Beide performAdd() und performBulkAdd() nutzen:

  • DB::transaction() fuer Atomitaet (Watcher + Source zusammen)
  • WatcherSource::query()->whereHas('watcher', ...) Check ob bereits existiert
  • Skip bei existierendem Watcher im Workspace

Keyboad Navigation

Frontend-Feature (view): Arrow Keys + Enter in Search-Input, Escape zum Schliessen.

Location: resources/views/livewire/discover/index.blade.php

Matomo Tracking

Nutzt TracksMatomo Concern:

$this->trackEvent('discover', 'add_to_workspace', $profile->handle ?? '', $profileId);

ProfileSearchService

Service fuer Volltextsuche ueber alle Profiles.

Location: app/Services/Search/ProfileSearchService.php

Public Interface

public function search(string $query, array $filters = [], int $limit = 24): Builder

Parameter:

  • $query – Suchstring (wird normalisiert)
  • $filters – Optionale Filter: ['platform' => '...', 'country' => '...', 'tier' => '...', 'category' => '...']
  • $limit – Max. Resultate (default 24)

Return: Eloquent Builder (nicht executed, kann weiter gefiltert werden).

Implementierung

Nutzt PostgreSQL ILIKE fuer Case-Insensitive Matching:

  • Sucht in: handle, title, description
  • Filter werden per Scope angewendet
  • Ausschluss: blocked_at, sanitized_at, archived_at (immer)
  • Min. Follower-Threshold fuer Discovery

Min Query Length

Konstante ProfileSearchService::MIN_QUERY_LENGTH (default: 2 Zeichen).