workspaceId = require_workspace()->id; } public function generate(): void { $this->error = ''; $this->insights = ''; if (! config('services.openai.api_key')) { $this->error = 'OPENAI_API_KEY is not configured. Add it to .env and restart.'; return; } try { $workspace = Workspace::findOrFail($this->workspaceId); $topLinks = Link::where('workspace_id', $workspace->id) ->withCount('clicks') ->orderByDesc('clicks_count') ->limit(10) ->get(['slug', 'target_url', 'clicks_count']); if ($topLinks->isEmpty()) { $this->error = 'No links with clicks found. Create and share some links first.'; return; } $locale = auth()->user()?->locale ?? 'en'; $langMap = ['de' => 'German', 'en' => 'English', 'fr' => 'French', 'es' => 'Spanish', 'it' => 'Italian', 'pt' => 'Portuguese']; $language = $langMap[$locale] ?? 'English'; $totalClicks = $topLinks->sum('clicks_count'); $linkCount = $topLinks->count(); $summary = $topLinks->map(fn ($l) => "- {$l->slug} ({$l->clicks_count} clicks) → {$l->target_url}")->implode("\n"); $prompt = <<name} Links analysed: {$linkCount} | Total clicks: {$totalClicks} Data: {$summary} Rules: - If fewer than 3 links or fewer than 10 total clicks exist, say so upfront and explain that reliable patterns need more data. Give 1–2 observations possible from the limited data, then stop. - Otherwise give 3–4 insights: best-performing destination types, domain patterns, what underperforming links share, one concrete next action. - Every claim must reference a specific slug or URL from the data above. - No generic marketing advice. No padding. Plain language, no excessive formatting. PROMPT; $result = app(AiService::class)->complete($prompt); if ($result === '') { $this->error = 'AI returned an empty response. Check your API key and try again.'; return; } $this->insights = $result; } catch (\Throwable $e) { $this->error = 'Failed to generate insights: ' . $e->getMessage(); } } public function render(): View { return view('livewire.pages.ai.insights') ->layout('layouts.nimuli-app', ['title' => 'AI Insights']); } }