where('creator_id', $user->id); $count = (clone $baseQuery) ->whereNotIn('status', ['archived']) ->count(); $draftCount = (clone $baseQuery) ->whereIn('status', ['draft', 'pending_review', 'rejected']) ->count(); $publishedCount = (clone $baseQuery) ->whereIn('status', ['published', 'scheduled']) ->count(); $recentPublishedCount = (clone $baseQuery) ->whereIn('status', ['published', 'scheduled']) ->where('published_at', '>=', now()->subDays(30)) ->count(); $archivedCount = (clone $baseQuery) ->where('status', 'archived') ->count(); return [ 'key' => $this->key(), 'label' => $this->label(), 'icon' => $this->icon(), 'count' => $count, 'draft_count' => $draftCount, 'published_count' => $publishedCount, 'archived_count' => $archivedCount, 'trend_value' => $recentPublishedCount, 'trend_label' => 'published in 30d', 'quick_action_label' => 'Create story', 'index_url' => $this->indexUrl(), 'create_url' => $this->createUrl(), ]; } public function items(User $user, string $bucket = 'all', int $limit = 200): Collection { $query = Story::query() ->where('creator_id', $user->id) ->with(['tags']) ->orderByDesc('updated_at') ->limit($limit); if ($bucket === 'drafts') { $query->whereIn('status', ['draft', 'pending_review', 'rejected']); } elseif ($bucket === 'scheduled') { $query->where('status', 'scheduled'); } elseif ($bucket === 'archived') { $query->where('status', 'archived'); } elseif ($bucket === 'published') { $query->whereIn('status', ['published', 'scheduled']); } else { $query->where('status', '!=', 'archived'); } return $query->get()->map(fn (Story $story): array => $this->mapItem($story)); } public function topItems(User $user, int $limit = 5): Collection { return Story::query() ->where('creator_id', $user->id) ->whereIn('status', ['published', 'scheduled']) ->orderByDesc('views') ->orderByDesc('likes_count') ->limit($limit) ->get() ->map(fn (Story $story): array => $this->mapItem($story)); } public function analytics(User $user): array { $totals = Story::query() ->where('creator_id', $user->id) ->where('status', '!=', 'archived') ->selectRaw('COALESCE(SUM(views), 0) as views') ->selectRaw('COALESCE(SUM(likes_count), 0) as appreciation') ->selectRaw('0 as shares') ->selectRaw('COALESCE(SUM(comments_count), 0) as comments') ->selectRaw('0 as saves') ->first(); return [ 'views' => (int) ($totals->views ?? 0), 'appreciation' => (int) ($totals->appreciation ?? 0), 'shares' => 0, 'comments' => (int) ($totals->comments ?? 0), 'saves' => 0, ]; } public function scheduledItems(User $user, int $limit = 50): Collection { return $this->items($user, 'scheduled', $limit); } private function mapItem(Story $story): array { $subtitle = $story->story_type ? ucfirst(str_replace('_', ' ', (string) $story->story_type)) : null; $viewUrl = in_array($story->status, ['published', 'scheduled'], true) ? route('stories.show', ['slug' => $story->slug]) : route('creator.stories.preview', ['story' => $story->id]); return [ 'id' => sprintf('%s:%d', $this->key(), (int) $story->id), 'numeric_id' => (int) $story->id, 'module' => $this->key(), 'module_label' => $this->label(), 'module_icon' => $this->icon(), 'title' => $story->title, 'subtitle' => $subtitle, 'description' => $story->excerpt, 'status' => $story->status, 'visibility' => $story->status === 'published' ? 'public' : 'private', 'image_url' => $story->cover_url, 'preview_url' => route('creator.stories.preview', ['story' => $story->id]), 'view_url' => $viewUrl, 'edit_url' => route('creator.stories.edit', ['story' => $story->id]), 'manage_url' => route('creator.stories.edit', ['story' => $story->id]), 'analytics_url' => route('creator.stories.analytics', ['story' => $story->id]), 'create_url' => $this->createUrl(), 'actions' => $this->actionsFor($story, $story->status), 'created_at' => $story->created_at?->toIso8601String(), 'updated_at' => $story->updated_at?->toIso8601String(), 'published_at' => $story->published_at?->toIso8601String(), 'scheduled_at' => $story->scheduled_for?->toIso8601String(), 'featured' => (bool) $story->featured, 'activity_state' => in_array($story->status, ['published', 'scheduled'], true) ? 'active' : ($story->status === 'archived' ? 'archived' : 'inactive'), 'metrics' => [ 'views' => (int) $story->views, 'appreciation' => (int) $story->likes_count, 'shares' => 0, 'comments' => (int) $story->comments_count, 'saves' => 0, ], 'engagement_score' => (int) $story->views + ((int) $story->likes_count * 2) + ((int) $story->comments_count * 3), 'taxonomies' => [ 'categories' => [], 'tags' => $story->tags->map(fn ($entry): array => [ 'id' => (int) $entry->id, 'name' => (string) $entry->name, 'slug' => (string) $entry->slug, ])->values()->all(), ], ]; } private function actionsFor(Story $story, string $status): array { $actions = []; if (in_array($status, ['draft', 'pending_review', 'rejected'], true)) { $actions[] = $this->requestAction( 'publish', 'Publish', 'fa-solid fa-rocket', route('api.stories.update'), [ 'story_id' => (int) $story->id, 'status' => 'published', ], null, 'put' ); } if (in_array($status, ['draft', 'pending_review', 'rejected', 'published', 'scheduled'], true)) { $actions[] = $this->requestAction( 'archive', 'Archive', 'fa-solid fa-box-archive', route('api.stories.update'), [ 'story_id' => (int) $story->id, 'status' => 'archived', ], null, 'put' ); } if ($status === 'scheduled') { $actions[] = $this->requestAction('publish_now', 'Publish now', 'fa-solid fa-bolt', route('api.studio.schedule.publishNow', ['module' => 'stories', 'id' => $story->id]), []); $actions[] = $this->requestAction('unschedule', 'Unschedule', 'fa-solid fa-calendar-xmark', route('api.studio.schedule.unschedule', ['module' => 'stories', 'id' => $story->id]), []); } if ($status === 'archived') { $actions[] = $this->requestAction( 'restore', 'Restore', 'fa-solid fa-rotate-left', route('api.stories.update'), [ 'story_id' => (int) $story->id, 'status' => 'draft', ], null, 'put' ); } $actions[] = $this->requestAction( 'delete', 'Delete', 'fa-solid fa-trash', route('creator.stories.destroy', ['story' => $story->id]), [], 'Delete this story permanently?', 'delete' ); return $actions; } private function requestAction(string $key, string $label, string $icon, string $url, array $payload = [], ?string $confirm = null, string $method = 'post'): array { return [ 'key' => $key, 'label' => $label, 'icon' => $icon, 'type' => 'request', 'method' => $method, 'url' => $url, 'payload' => $payload, 'confirm' => $confirm, ]; } }