import axios from "axios"; import type { Bill, BillAction, BillDetail, DashboardData, Follow, Member, MemberTrendScore, MemberNewsArticle, NewsArticle, PaginatedResponse, SettingsData, TrendScore, } from "./types"; const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || "", timeout: 30000, }); // Attach JWT from localStorage on every request apiClient.interceptors.request.use((config) => { if (typeof window !== "undefined") { try { const stored = localStorage.getItem("pocketveto-auth"); if (stored) { const { state } = JSON.parse(stored); if (state?.token) { config.headers.Authorization = `Bearer ${state.token}`; } } } catch { // ignore parse errors } } return config; }); interface AuthUser { id: number; email: string; is_admin: boolean; notification_prefs: Record; created_at: string; } interface TokenResponse { access_token: string; token_type: string; user: AuthUser; } // Auth export const authAPI = { register: (email: string, password: string) => apiClient.post("/api/auth/register", { email, password }).then((r) => r.data), login: (email: string, password: string) => apiClient.post("/api/auth/login", { email, password }).then((r) => r.data), me: () => apiClient.get("/api/auth/me").then((r) => r.data), }; // Bills export const billsAPI = { list: (params?: Record) => apiClient.get>("/api/bills", { params }).then((r) => r.data), get: (id: string) => apiClient.get(`/api/bills/${id}`).then((r) => r.data), getActions: (id: string) => apiClient.get(`/api/bills/${id}/actions`).then((r) => r.data), getNews: (id: string) => apiClient.get(`/api/bills/${id}/news`).then((r) => r.data), getTrend: (id: string, days?: number) => apiClient.get(`/api/bills/${id}/trend`, { params: { days } }).then((r) => r.data), }; // Members export const membersAPI = { list: (params?: Record) => apiClient.get>("/api/members", { params }).then((r) => r.data), get: (id: string) => apiClient.get(`/api/members/${id}`).then((r) => r.data), getBills: (id: string, params?: Record) => apiClient.get>(`/api/members/${id}/bills`, { params }).then((r) => r.data), getTrend: (id: string, days?: number) => apiClient.get(`/api/members/${id}/trend`, { params: { days } }).then((r) => r.data), getNews: (id: string) => apiClient.get(`/api/members/${id}/news`).then((r) => r.data), }; // Follows export const followsAPI = { list: () => apiClient.get("/api/follows").then((r) => r.data), add: (follow_type: string, follow_value: string) => apiClient.post("/api/follows", { follow_type, follow_value }).then((r) => r.data), remove: (id: number) => apiClient.delete(`/api/follows/${id}`), }; // Dashboard export const dashboardAPI = { get: () => apiClient.get("/api/dashboard").then((r) => r.data), }; // Search export const searchAPI = { search: (q: string) => apiClient.get<{ bills: Bill[]; members: Member[] }>("/api/search", { params: { q } }).then((r) => r.data), }; // Settings export const settingsAPI = { get: () => apiClient.get("/api/settings").then((r) => r.data), update: (key: string, value: string) => apiClient.put("/api/settings", { key, value }).then((r) => r.data), testLLM: () => apiClient.post("/api/settings/test-llm").then((r) => r.data), }; export interface AdminUser { id: number; email: string; is_admin: boolean; follow_count: number; created_at: string; } export interface AnalysisStats { total_bills: number; docs_fetched: number; briefs_generated: number; full_briefs: number; amendment_briefs: number; uncited_briefs: number; remaining: number; } // Admin export const adminAPI = { // Stats getStats: () => apiClient.get("/api/admin/stats").then((r) => r.data), // Users listUsers: () => apiClient.get("/api/admin/users").then((r) => r.data), deleteUser: (id: number) => apiClient.delete(`/api/admin/users/${id}`), toggleAdmin: (id: number) => apiClient.patch(`/api/admin/users/${id}/toggle-admin`).then((r) => r.data), // Tasks triggerPoll: () => apiClient.post("/api/admin/trigger-poll").then((r) => r.data), triggerMemberSync: () => apiClient.post("/api/admin/trigger-member-sync").then((r) => r.data), triggerTrendScores: () => apiClient.post("/api/admin/trigger-trend-scores").then((r) => r.data), backfillSponsors: () => apiClient.post("/api/admin/backfill-sponsors").then((r) => r.data), backfillCitations: () => apiClient.post("/api/admin/backfill-citations").then((r) => r.data), triggerFetchActions: () => apiClient.post("/api/admin/trigger-fetch-actions").then((r) => r.data), getTaskStatus: (taskId: string) => apiClient.get(`/api/admin/task-status/${taskId}`).then((r) => r.data), };