"use client"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Settings, Cpu, RefreshCw, CheckCircle, XCircle, Play, Users, Trash2, ShieldCheck, ShieldOff, FileText, Brain, BarChart3, } from "lucide-react"; import { settingsAPI, adminAPI, type AdminUser } from "@/lib/api"; import { useAuthStore } from "@/stores/authStore"; const LLM_PROVIDERS = [ { value: "openai", label: "OpenAI (GPT-4o)", hint: "Requires OPENAI_API_KEY in .env" }, { value: "anthropic", label: "Anthropic (Claude)", hint: "Requires ANTHROPIC_API_KEY in .env" }, { value: "gemini", label: "Google Gemini", hint: "Requires GEMINI_API_KEY in .env" }, { value: "ollama", label: "Ollama (Local)", hint: "Requires Ollama running on host" }, ]; export default function SettingsPage() { const qc = useQueryClient(); const currentUser = useAuthStore((s) => s.user); const { data: settings, isLoading: settingsLoading } = useQuery({ queryKey: ["settings"], queryFn: () => settingsAPI.get(), }); const { data: stats } = useQuery({ queryKey: ["admin-stats"], queryFn: () => adminAPI.getStats(), enabled: !!currentUser?.is_admin, refetchInterval: 30_000, }); const { data: users, isLoading: usersLoading } = useQuery({ queryKey: ["admin-users"], queryFn: () => adminAPI.listUsers(), enabled: !!currentUser?.is_admin, }); const updateSetting = useMutation({ mutationFn: ({ key, value }: { key: string; value: string }) => settingsAPI.update(key, value), onSuccess: () => qc.invalidateQueries({ queryKey: ["settings"] }), }); const deleteUser = useMutation({ mutationFn: (id: number) => adminAPI.deleteUser(id), onSuccess: () => qc.invalidateQueries({ queryKey: ["admin-users"] }), }); const toggleAdmin = useMutation({ mutationFn: (id: number) => adminAPI.toggleAdmin(id), onSuccess: () => qc.invalidateQueries({ queryKey: ["admin-users"] }), }); const [testResult, setTestResult] = useState<{ status: string; detail?: string; summary_preview?: string; provider?: string; } | null>(null); const [testing, setTesting] = useState(false); const [taskIds, setTaskIds] = useState>({}); const [confirmDelete, setConfirmDelete] = useState(null); const testLLM = async () => { setTesting(true); setTestResult(null); try { const result = await settingsAPI.testLLM(); setTestResult(result); } catch (e: unknown) { setTestResult({ status: "error", detail: e instanceof Error ? e.message : String(e) }); } finally { setTesting(false); } }; const trigger = async (name: string, fn: () => Promise<{ task_id: string }>) => { const result = await fn(); setTaskIds((prev) => ({ ...prev, [name]: result.task_id })); }; if (settingsLoading) return
Loading...
; if (!currentUser?.is_admin) { return
Admin access required.
; } const pct = stats && stats.total_bills > 0 ? Math.round((stats.briefs_generated / stats.total_bills) * 100) : 0; return (

Admin

Manage users, LLM provider, and system settings

{/* Analysis Status */}

Analysis Status refreshes every 30s

{stats ? ( <>
{stats.total_bills.toLocaleString()}
Total Bills
{stats.docs_fetched.toLocaleString()}
Docs Fetched
{stats.briefs_generated.toLocaleString()}
Briefs Generated
{/* Progress bar */}
{stats.full_briefs} full · {stats.amendment_briefs} amendments {pct}% analyzed · {stats.remaining.toLocaleString()} remaining
) : (

Loading stats...

)}
{/* User Management */}

Users

{usersLoading ? (

Loading users...

) : (
{(users ?? []).map((u: AdminUser) => (
{u.email} {u.is_admin && ( admin )} {u.id === currentUser.id && ( (you) )}
{u.follow_count} follow{u.follow_count !== 1 ? "s" : ""} ·{" "} joined {new Date(u.created_at).toLocaleDateString()}
{u.id !== currentUser.id && (
{confirmDelete === u.id ? (
) : ( )}
)}
))}
)}
{/* LLM Provider */}

LLM Provider

Current: {settings?.llm_provider} / {settings?.llm_model}

{LLM_PROVIDERS.map(({ value, label, hint }) => ( ))}
{testResult && (
{testResult.status === "ok" ? ( <> {testResult.provider} — {testResult.summary_preview?.slice(0, 60)}... ) : ( <> {testResult.detail} )}
)}
{/* Data Sources */}

Data Sources

Congress.gov Poll Interval
How often to check for new bills
NewsAPI.org
100 requests/day free tier
{settings?.newsapi_enabled ? "Configured" : "Not configured"}
Google Trends
Zeitgeist scoring via pytrends
{settings?.pytrends_enabled ? "Enabled" : "Disabled"}
{/* Manual Controls */}

Manual Controls

{Object.entries(taskIds).map(([name, id]) => (

{name}: task {id} queued

))}
); }