import { useState, type ReactNode } from 'react' import { Link } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { ArrowRight, Check, Copy, X } from 'lucide-react' import { apiFetch } from '@/lib/api' import { copyText } from '@/lib/clipboard' import { toast } from '@/lib/toast' import { apiBaseUrl } from '@/components/api-usage' import { Button } from '@/components/ui/button' import { useI18n } from '@/i18n' const DISMISS_KEY = 'freellmapi.setup.dismissed' const CONNECT_KEY = 'freellmapi.setup.connected' function readFlag(key: string): boolean { try { return localStorage.getItem(key) === '1' } catch { return false } } function writeFlag(key: string) { try { localStorage.setItem(key, '1') } catch { /* ignore */ } } // First-run checklist on the Models landing page. A fresh install lands on an // empty routing table with no hint of what to do; this walks the three steps // that make the router actually useful (key -> test -> connect) and disappears // once the install is clearly set up (has keys and has served a request), or // when dismissed. export function GettingStarted() { const { t } = useI18n() const [dismissed, setDismissed] = useState(() => readFlag(DISMISS_KEY)) const [connected, setConnected] = useState(() => readFlag(CONNECT_KEY)) const { data: keys } = useQuery({ queryKey: ['keys'], queryFn: () => apiFetch('/api/keys'), }) const { data: summary } = useQuery<{ totalRequests?: number }>({ queryKey: ['setup-summary'], queryFn: () => apiFetch('/api/analytics/summary?range=30d'), staleTime: 30_000, }) const { data: keyData } = useQuery<{ apiKey: string }>({ queryKey: ['unified-key'], queryFn: () => apiFetch('/api/settings/api-key'), }) // Wait for real data before deciding: rendering on undefined would flash the // checklist at every established install for a moment on each visit. if (dismissed || !keys || !summary) return null const hasKeys = keys.length > 0 const hasRequest = (summary.totalRequests ?? 0) > 0 if (hasKeys && hasRequest) return null const doneCount = [hasKeys, hasRequest, connected].filter(Boolean).length function copyValue(value: string, message: string) { void copyText(value).then(ok => { if (!ok) { toast.error(t('common.copyFailed')) return } toast.success(message) setConnected(true) writeFlag(CONNECT_KEY) }) } return (

{t('setup.title')}

{t('setup.subtitle')}

{t('setup.progress', { done: doneCount, total: 3 })}
    } /> } /> } />
) } function Step({ index, done, title, description, action, }: { index: number done: boolean title: string description: string action: ReactNode }) { return (
  • {done ? : index}

    {title}

    {!done &&

    {description}

    }
    {!done &&
    {action}
    }
  • ) }