content->providers()); $moduleBreakdown = $providers->map(function (CreatorStudioProvider $provider) use ($user): array { $summary = $provider->summary($user); $analytics = $provider->analytics($user); return [ 'key' => $provider->key(), 'label' => $provider->label(), 'icon' => $provider->icon(), 'count' => $summary['count'], 'draft_count' => $summary['draft_count'], 'published_count' => $summary['published_count'], 'archived_count' => $summary['archived_count'], 'views' => $analytics['views'], 'appreciation' => $analytics['appreciation'], 'shares' => $analytics['shares'], 'comments' => $analytics['comments'], 'saves' => $analytics['saves'], 'index_url' => $provider->indexUrl(), ]; })->values(); $followers = (int) DB::table('user_followers')->where('user_id', $user->id)->count(); $totals = [ 'views' => (int) $moduleBreakdown->sum('views'), 'appreciation' => (int) $moduleBreakdown->sum('appreciation'), 'shares' => (int) $moduleBreakdown->sum('shares'), 'comments' => (int) $moduleBreakdown->sum('comments'), 'saves' => (int) $moduleBreakdown->sum('saves'), 'followers' => $followers, 'content_count' => (int) $moduleBreakdown->sum('count'), ]; $topContent = $providers ->flatMap(fn (CreatorStudioProvider $provider) => $provider->topItems($user, 4)) ->sortByDesc(fn (array $item): int => (int) ($item['engagement_score'] ?? 0)) ->take(8) ->values() ->all(); return [ 'totals' => $totals, 'module_breakdown' => $moduleBreakdown->all(), 'top_content' => $topContent, 'views_trend' => $this->trendSeries($user, $days, 'views'), 'engagement_trend' => $this->trendSeries($user, $days, 'engagement'), 'publishing_timeline' => $this->publishingTimeline($user, $days), 'comparison' => $this->comparison($user, $days), 'insight_blocks' => $this->insightBlocks($moduleBreakdown, $totals, $days), 'range_days' => $days, ]; } private function insightBlocks($moduleBreakdown, array $totals, int $days): array { $strongest = $moduleBreakdown->sortByDesc('appreciation')->first(); $busiest = $moduleBreakdown->sortByDesc('count')->first(); $conversation = $moduleBreakdown->sortByDesc('comments')->first(); $insights = []; if ($strongest) { $insights[] = [ 'key' => 'strongest_module', 'title' => $strongest['label'] . ' is driving the strongest reaction', 'body' => sprintf( '%s generated %s reactions in the last %d days, making it the strongest appreciation surface in Studio right now.', $strongest['label'], number_format((int) $strongest['appreciation']), $days, ), 'tone' => 'positive', 'icon' => 'fa-solid fa-sparkles', 'href' => $strongest['index_url'], 'cta' => 'Open module', ]; } if ($conversation && (int) ($conversation['comments'] ?? 0) > 0) { $insights[] = [ 'key' => 'conversation_module', 'title' => 'Conversation is concentrating in ' . $conversation['label'], 'body' => sprintf( '%s collected %s comments in this window. That is the clearest place to check for follow-up and community signals.', $conversation['label'], number_format((int) $conversation['comments']), ), 'tone' => 'action', 'icon' => 'fa-solid fa-comments', 'href' => route('studio.inbox', ['module' => $conversation['key'], 'type' => 'comment']), 'cta' => 'Open inbox', ]; } if ($busiest && (int) ($busiest['draft_count'] ?? 0) > 0) { $insights[] = [ 'key' => 'draft_pressure', 'title' => $busiest['label'] . ' has the heaviest backlog', 'body' => sprintf( '%s currently has %s drafts. That is the best candidate for your next cleanup, publish, or scheduling pass.', $busiest['label'], number_format((int) $busiest['draft_count']), ), 'tone' => 'warning', 'icon' => 'fa-solid fa-layer-group', 'href' => route('studio.drafts', ['module' => $busiest['key']]), 'cta' => 'Review drafts', ]; } if ((int) ($totals['followers'] ?? 0) > 0) { $insights[] = [ 'key' => 'audience_presence', 'title' => 'Audience footprint is active across the workspace', 'body' => sprintf( 'You now have %s followers connected to this creator profile. Keep featured content and your publishing cadence aligned with that audience.', number_format((int) $totals['followers']), ), 'tone' => 'neutral', 'icon' => 'fa-solid fa-user-group', 'href' => route('studio.featured'), 'cta' => 'Manage featured', ]; } return collect($insights)->take(4)->values()->all(); } private function publishingTimeline(User $user, int $days): array { $timeline = collect(range($days - 1, 0))->map(function (int $offset): array { $date = now()->subDays($offset)->startOfDay(); return [ 'date' => $date->toDateString(), 'count' => 0, ]; })->keyBy('date'); collect($this->content->recentPublished($user, 120)) ->each(function (array $item) use ($timeline): void { $publishedAt = $item['published_at'] ?? $item['updated_at'] ?? null; if (! $publishedAt) { return; } $date = date('Y-m-d', strtotime((string) $publishedAt)); if (! $timeline->has($date)) { return; } $row = $timeline->get($date); $row['count']++; $timeline->put($date, $row); }); return $timeline->values()->all(); } private function trendSeries(User $user, int $days, string $metric): array { $series = collect(range($days - 1, 0))->map(function (int $offset): array { $date = now()->subDays($offset)->toDateString(); return [ 'date' => $date, 'value' => 0, ]; })->keyBy('date'); collect($this->content->providers()) ->flatMap(fn (CreatorStudioProvider $provider) => $provider->items($user, 'all', 240)) ->each(function (array $item) use ($series, $metric): void { $dateSource = $item['published_at'] ?? $item['updated_at'] ?? $item['created_at'] ?? null; if (! $dateSource) { return; } $date = date('Y-m-d', strtotime((string) $dateSource)); if (! $series->has($date)) { return; } $row = $series->get($date); $increment = $metric === 'views' ? (int) ($item['metrics']['views'] ?? 0) : (int) ($item['engagement_score'] ?? 0); $row['value'] += $increment; $series->put($date, $row); }); return $series->values()->all(); } private function comparison(User $user, int $days): array { $windowStart = now()->subDays($days)->getTimestamp(); return collect($this->content->providers()) ->map(function (CreatorStudioProvider $provider) use ($user, $windowStart): array { $items = $provider->items($user, 'all', 240) ->filter(function (array $item) use ($windowStart): bool { $date = $item['published_at'] ?? $item['updated_at'] ?? $item['created_at'] ?? null; return $date !== null && strtotime((string) $date) >= $windowStart; }); return [ 'key' => $provider->key(), 'label' => $provider->label(), 'icon' => $provider->icon(), 'views' => (int) $items->sum(fn (array $item): int => (int) ($item['metrics']['views'] ?? 0)), 'engagement' => (int) $items->sum(fn (array $item): int => (int) ($item['engagement_score'] ?? 0)), 'published_count' => (int) $items->filter(fn (array $item): bool => ($item['status'] ?? null) === 'published')->count(), ]; }) ->values() ->all(); } }