Spaces:
Sleeping
Sleeping
| /** | |
| * BankBot API client β typed fetch wrapper for all backend endpoints. | |
| * Handles auth headers, token refresh, and error normalization. | |
| * | |
| * HF Spaces mode: when NEXT_PUBLIC_API_URL is empty, all /api/* calls | |
| * go to the same origin β Nginx routes them to FastAPI internally. | |
| * WebSocket also uses the same origin with ws:// or wss://. | |
| */ | |
| // In HF mode NEXT_PUBLIC_API_URL is "" β use empty string so fetch uses same origin | |
| const API_BASE = process.env.NEXT_PUBLIC_API_URL || ""; | |
| // βββ WebSocket URL derivation βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // HF mode (API_BASE=""): derive from window.location at runtime | |
| // External mode: convert httpβws, httpsβwss | |
| function getWsBase(): string { | |
| if (typeof window === "undefined") return ""; | |
| if (!API_BASE) { | |
| // Same-origin WebSocket β use current page protocol | |
| const proto = window.location.protocol === "https:" ? "wss:" : "ws:"; | |
| return `${proto}//${window.location.host}`; | |
| } | |
| return API_BASE.replace(/^https/, "wss").replace(/^http/, "ws"); | |
| } | |
| // βββ Token management (browser-only) βββββββββββββββββββββββββββββββββββββββββ | |
| export const tokenStore = { | |
| getAccess: (): string | null => | |
| typeof window !== "undefined" ? localStorage.getItem("bb_access_token") : null, | |
| getRefresh: (): string | null => | |
| typeof window !== "undefined" ? localStorage.getItem("bb_refresh_token") : null, | |
| setTokens: (access: string, refresh?: string) => { | |
| if (typeof window === "undefined") return; | |
| localStorage.setItem("bb_access_token", access); | |
| if (refresh) localStorage.setItem("bb_refresh_token", refresh); | |
| }, | |
| clear: () => { | |
| if (typeof window === "undefined") return; | |
| localStorage.removeItem("bb_access_token"); | |
| localStorage.removeItem("bb_refresh_token"); | |
| localStorage.removeItem("bb_user"); | |
| }, | |
| }; | |
| // βββ Core fetch wrapper βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async function apiFetch<T>( | |
| path: string, | |
| options: RequestInit = {}, | |
| retry = true | |
| ): Promise<T> { | |
| const token = tokenStore.getAccess(); | |
| const headers: Record<string, string> = { | |
| "Content-Type": "application/json", | |
| ...(options.headers as Record<string, string>), | |
| }; | |
| if (token) headers["Authorization"] = `Bearer ${token}`; | |
| const res = await fetch(`${API_BASE}${path}`, { ...options, headers }); | |
| // Auto-refresh on 401 | |
| if (res.status === 401 && retry) { | |
| const refreshToken = tokenStore.getRefresh(); | |
| if (refreshToken) { | |
| try { | |
| const refreshRes = await fetch(`${API_BASE}/api/auth/refresh`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ refresh_token: refreshToken }), | |
| }); | |
| if (refreshRes.ok) { | |
| const data = await refreshRes.json(); | |
| tokenStore.setTokens(data.access_token); | |
| return apiFetch<T>(path, options, false); | |
| } | |
| } catch { | |
| // refresh failed β clear tokens | |
| } | |
| } | |
| tokenStore.clear(); | |
| throw new ApiError(401, "Session expired. Please log in again."); | |
| } | |
| if (!res.ok) { | |
| let detail = `HTTP ${res.status}`; | |
| try { | |
| const err = await res.json(); | |
| detail = err.detail || detail; | |
| } catch { /* ignore */ } | |
| throw new ApiError(res.status, detail); | |
| } | |
| return res.json() as Promise<T>; | |
| } | |
| export class ApiError extends Error { | |
| constructor(public status: number, message: string) { | |
| super(message); | |
| this.name = "ApiError"; | |
| } | |
| } | |
| // βββ Auth βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface LoginResponse { | |
| access_token: string; | |
| refresh_token: string; | |
| token_type: string; | |
| user_id: string; | |
| name: string; | |
| email: string; | |
| } | |
| export const authApi = { | |
| login: (email: string, password: string) => { | |
| const form = new URLSearchParams({ username: email, password }); | |
| return apiFetch<LoginResponse>("/api/auth/login", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/x-www-form-urlencoded" }, | |
| body: form.toString(), | |
| }); | |
| }, | |
| register: (email: string, password: string, name: string) => | |
| apiFetch<LoginResponse>("/api/auth/register", { | |
| method: "POST", | |
| body: JSON.stringify({ email, password, name }), | |
| }), | |
| me: () => | |
| apiFetch<{ | |
| user_id: string; | |
| email: string; | |
| name: string; | |
| financial_personality: string; | |
| notifications_enabled?: boolean; | |
| ai_coaching_enabled?: boolean; | |
| fraud_alerts_enabled?: boolean; | |
| }>("/api/auth/me"), | |
| updateSettings: (data: { | |
| name?: string; | |
| financial_personality?: string; | |
| notifications_enabled?: boolean; | |
| ai_coaching_enabled?: boolean; | |
| fraud_alerts_enabled?: boolean; | |
| }) => | |
| apiFetch<{ | |
| user_id: string; | |
| email: string; | |
| name: string; | |
| financial_personality: string; | |
| notifications_enabled?: boolean; | |
| ai_coaching_enabled?: boolean; | |
| fraud_alerts_enabled?: boolean; | |
| }>("/api/auth/settings", { method: "PATCH", body: JSON.stringify(data) }), | |
| logout: () => { | |
| tokenStore.clear(); | |
| return Promise.resolve(); | |
| }, | |
| }; | |
| export interface FinancialGoal { | |
| id: string; | |
| title: string; | |
| target_amount: number; | |
| current_amount: number; | |
| progress_percent: number; | |
| target_date: string | null; | |
| days_left: number | null; | |
| monthly_contribution?: number; | |
| months_remaining?: number; | |
| on_track: boolean; | |
| } | |
| export const goalsApi = { | |
| list: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| goals: FinancialGoal[]; | |
| summary: { count: number; total_target: number; total_saved: number; overall_progress: number }; | |
| }>(`/api/goals${qs}`); | |
| }, | |
| contribute: (goalId: string, amount: number, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ id: string; current_amount: number; progress_percent: number; completed: boolean }>( | |
| `/api/goals/${goalId}/contribute${qs}`, | |
| { method: "POST", body: JSON.stringify({ amount }) } | |
| ); | |
| }, | |
| }; | |
| export const loansApi = { | |
| eligibility: (data: { | |
| salary: number; | |
| credit_score: number; | |
| existing_loans: number; | |
| employment_years: number; | |
| age: number; | |
| loan_amount: number; | |
| }, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| approval_probability: number; | |
| approval_status: string; | |
| risk_level: string; | |
| loan_score: number; | |
| emi: number; | |
| monthly_emi: number; | |
| recommendations: string[]; | |
| issues: string[]; | |
| comparison: Array<{ rate: string; tenure: string; emi: number; total_amount: number; interest: number }>; | |
| }>(`/api/loans/eligibility${qs}`, { method: "POST", body: JSON.stringify(data) }); | |
| }, | |
| }; | |
| // βββ Dashboard ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface DashboardOverview { | |
| total_balance: number; | |
| accounts: Array<{ id: string; type: string; balance: number; currency: string }>; | |
| monthly_income: number; | |
| monthly_expenses: number; | |
| savings_rate: number; | |
| spending_by_category: Array<{ name: string; value: number }>; | |
| recent_transactions: Array<{ | |
| id: string; merchant: string; category: string; | |
| amount: number; type: string; timestamp: string; | |
| }>; | |
| cash_flow: Array<{ month: string; income: number; expenses: number; savings: number }>; | |
| health_score: number; | |
| fraud_alert_count: number; | |
| ai_briefing: { summary?: string; briefing?: string; insights?: string[] }; | |
| } | |
| export const dashboardApi = { | |
| overview: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<DashboardOverview>(`/api/dashboard/overview${qs}`); | |
| }, | |
| }; | |
| // βββ AI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface HealthScore { | |
| overall_score: number; | |
| categories: Record<string, { score: number; max: number; label: string }>; | |
| } | |
| export interface ForecastResult { | |
| monthly_projections?: Array<{ month: number; balance: number; savings: number }>; | |
| scenarios?: { conservative: number; expected: number; optimistic: number }; | |
| } | |
| export const aiApi = { | |
| healthScore: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<HealthScore>(`/api/ai/coaching/score${qs}`); | |
| }, | |
| briefing: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ summary: string; insights: string[] }>(`/api/ai/coaching/briefing${qs}`); | |
| }, | |
| behaviorInsights: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ insights: string[]; metrics: Record<string, number> }>(`/api/ai/behavior/insights${qs}`); | |
| }, | |
| twinPredict: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<ForecastResult>(`/api/ai/twin/predict${qs}`); | |
| }, | |
| twinFuture: (months = 12, userId?: string) => { | |
| const qs = new URLSearchParams({ months: String(months), ...(userId ? { user_id: userId } : {}) }); | |
| return apiFetch<ForecastResult>(`/api/ai/twin/future?${qs}`); | |
| }, | |
| twinScenarios: (months = 6, userId?: string) => { | |
| const qs = new URLSearchParams({ months: String(months), ...(userId ? { user_id: userId } : {}) }); | |
| return apiFetch<{ scenarios: Record<string, number[]> }>(`/api/ai/twin/scenarios?${qs}`); | |
| }, | |
| fraudAnalysis: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ alerts: Array<{ id: string; risk_score: number; details: string }> }>(`/api/ai/fraud/analysis${qs}`); | |
| }, | |
| subscriptions: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ subscriptions: Array<{ merchant: string; amount: number; recommendation: string }> }>(`/api/ai/subscriptions/optimize${qs}`); | |
| }, | |
| chat: (message: string, userId?: string, sessionId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ response: string; session_id: string }>(`/api/ai/chat${qs}`, { | |
| method: "POST", | |
| body: JSON.stringify({ message, session_id: sessionId }), | |
| }); | |
| }, | |
| chatSessions: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| sessions: Array<{ | |
| id: string; | |
| title: string; | |
| created_at: string | null; | |
| updated_at: string | null; | |
| message_count: number; | |
| preview: string; | |
| }>; | |
| count: number; | |
| }>(`/api/ai/chat/sessions${qs}`); | |
| }, | |
| createChatSession: (userId?: string, title = "New chat") => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| id: string; | |
| title: string; | |
| created_at: string | null; | |
| updated_at: string | null; | |
| message_count: number; | |
| preview: string; | |
| }>(`/api/ai/chat/sessions${qs}`, { | |
| method: "POST", | |
| body: JSON.stringify({ title }), | |
| }); | |
| }, | |
| deleteChatSession: (sessionId: string, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ ok: boolean }>(`/api/ai/chat/sessions/${sessionId}${qs}`, { method: "DELETE" }); | |
| }, | |
| chatHistory: (sessionId: string, userId?: string, limit = 100) => { | |
| const params = new URLSearchParams({ session_id: sessionId, limit: String(limit) }); | |
| if (userId) params.set("user_id", userId); | |
| return apiFetch<{ | |
| session_id: string; | |
| messages: Array<{ id: string; role: "user" | "assistant"; content: string; created_at: string | null }>; | |
| count: number; | |
| }>(`/api/ai/chat/history?${params}`); | |
| }, | |
| clearChatHistory: (sessionId: string, userId?: string) => { | |
| const params = new URLSearchParams({ session_id: sessionId }); | |
| if (userId) params.set("user_id", userId); | |
| return apiFetch<{ ok: boolean; message: string; session_id: string }>( | |
| `/api/ai/chat/history?${params}`, | |
| { method: "DELETE" } | |
| ); | |
| }, | |
| simulatePurchase: (amount: number, merchant: string, category: string, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<Record<string, unknown>>(`/api/ai/simulate/purchase${qs}`, { | |
| method: "POST", | |
| body: JSON.stringify({ amount, merchant, category }), | |
| }); | |
| }, | |
| // ββ Coach Mode ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| weeklyCoaching: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| generated_at: string; | |
| week_start: string; | |
| week_end: string; | |
| health_score: number; | |
| week_spend: number; | |
| week_income: number; | |
| spend_delta_pct: number; | |
| top_categories: Array<{ name: string; amount: number }>; | |
| anomalies: string[]; | |
| coaching_report: string; | |
| improvements: string[]; | |
| }>(`/api/ai/coach/weekly${qs}`); | |
| }, | |
| // ββ Fraud Explanation βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| fraudExplain: (fraudLogId: string) => | |
| apiFetch<{ | |
| fraud_log_id: string; | |
| transaction_id: string; | |
| merchant: string; | |
| amount: number; | |
| timestamp: string; | |
| risk_score: number; | |
| raw_reasons: string[]; | |
| ai_explanation: string; | |
| user_avg_amount: number; | |
| amount_vs_avg: number; | |
| }>(`/api/ai/fraud/explain/${fraudLogId}`), | |
| // ββ Monthly Narrative βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| monthlyNarrative: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ | |
| month: string; | |
| generated_at: string; | |
| summary: { | |
| total_spend: number; | |
| total_income: number; | |
| spend_delta_pct: number; | |
| savings_balance: number; | |
| investment_value: number; | |
| investment_gain_pct: number; | |
| monthly_subscriptions: number; | |
| }; | |
| category_changes: Array<{ | |
| category: string; | |
| this_month: number; | |
| last_month: number; | |
| delta_pct: number; | |
| }>; | |
| narrative: string; | |
| }>(`/api/ai/narrative/monthly${qs}`); | |
| }, | |
| }; | |
| // βββ Notifications ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface NotificationItem { | |
| id: string; | |
| title: string; | |
| message: string; | |
| type: "alert" | "insight" | "warning" | string; | |
| read: boolean; | |
| created_at: string; | |
| } | |
| export const notificationsApi = { | |
| list: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ notifications: NotificationItem[]; unread_count: number }>(`/api/notifications/${qs}`); | |
| }, | |
| markRead: (id: string, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ success: boolean }>(`/api/notifications/${id}/read${qs}`, { method: "PATCH" }); | |
| }, | |
| markAllRead: (userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ success: boolean }>(`/api/notifications/read-all${qs}`, { method: "PATCH" }); | |
| }, | |
| dismiss: (id: string, userId?: string) => { | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return apiFetch<{ success: boolean }>(`/api/notifications/${id}${qs}`, { method: "DELETE" }); | |
| }, | |
| }; | |
| // βββ Transactions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const transactionsApi = { | |
| list: (params: { page?: number; limit?: number; category?: string; type?: string; userId?: string } = {}) => { | |
| const qs = new URLSearchParams(); | |
| if (params.page) qs.set("page", String(params.page)); | |
| if (params.limit) qs.set("limit", String(params.limit)); | |
| if (params.category) qs.set("category", params.category); | |
| if (params.type) qs.set("type", params.type); | |
| if (params.userId) qs.set("user_id", params.userId); | |
| return apiFetch<{ | |
| transactions: Array<{ id: string; merchant: string; category: string; amount: number; type: string; timestamp: string }>; | |
| total: number; page: number; pages: number; | |
| }>(`/api/transactions/?${qs}`); | |
| }, | |
| }; | |
| // βββ Payments βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface Payment { | |
| id: string; | |
| user_id: string; | |
| amount: number; | |
| currency: string; | |
| recipient_name: string; | |
| recipient_account: string; | |
| payment_type: string; | |
| status: "pending" | "completed" | "failed" | "flagged"; | |
| risk_score: number; | |
| fraud_flag: boolean; | |
| created_at: string; | |
| transaction_reference: string | null; | |
| note: string | null; | |
| ai_insight: string | null; | |
| } | |
| export interface PaymentHistoryResponse { | |
| payments: Payment[]; | |
| total: number; | |
| page: number; | |
| pages: number; | |
| stats: { | |
| total_sent: number; | |
| total_received: number; | |
| flagged_count: number; | |
| }; | |
| } | |
| export const paymentsApi = { | |
| create: (data: { | |
| amount: number; | |
| currency?: string; | |
| recipient_name: string; | |
| recipient_account: string; | |
| payment_type?: string; | |
| note?: string; | |
| }) => | |
| apiFetch<Payment>("/api/payments/create", { | |
| method: "POST", | |
| body: JSON.stringify(data), | |
| }), | |
| transfer: (data: { amount: number; to_account_type: string; note?: string }) => | |
| apiFetch<Payment>("/api/payments/transfer", { | |
| method: "POST", | |
| body: JSON.stringify(data), | |
| }), | |
| history: (params: { | |
| page?: number; | |
| limit?: number; | |
| payment_type?: string; | |
| status?: string; | |
| } = {}) => { | |
| const qs = new URLSearchParams(); | |
| if (params.page) qs.set("page", String(params.page)); | |
| if (params.limit) qs.set("limit", String(params.limit)); | |
| if (params.payment_type) qs.set("payment_type", params.payment_type); | |
| if (params.status) qs.set("status", params.status); | |
| return apiFetch<PaymentHistoryResponse>(`/api/payments/history?${qs}`); | |
| }, | |
| get: (id: string) => apiFetch<Payment>(`/api/payments/${id}`), | |
| verify: (payment_id: string, confirm: boolean) => | |
| apiFetch<{ message: string; payment: Payment }>("/api/payments/verify", { | |
| method: "POST", | |
| body: JSON.stringify({ payment_id, confirm }), | |
| }), | |
| cancel: (id: string) => | |
| apiFetch<{ message: string }>(`/api/payments/${id}`, { method: "DELETE" }), | |
| }; | |
| // βββ WebSocket factory ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export function createChatWebSocket(userId?: string): WebSocket { | |
| const wsBase = getWsBase(); | |
| const qs = userId ? `?user_id=${userId}` : ""; | |
| return new WebSocket(`${wsBase}/api/ai/chat/ws${qs}`); | |
| } | |
| // βββ Status βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export const statusApi = { | |
| check: () => apiFetch<{ ai_backend: string; ai_available: boolean; db_type: string; cache_type: string }>("/api/status"), | |
| }; | |
| // βββ Memory βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface ChatHistoryMessage { | |
| id: string; | |
| session_id: string | null; | |
| role: "user" | "assistant"; | |
| content: string; | |
| created_at: string; | |
| } | |
| export interface ChatSession { | |
| id: string; | |
| title: string; | |
| created_at: string; | |
| updated_at: string; | |
| } | |
| export const memoryApi = { | |
| history: (sessionId?: string) => { | |
| const qs = sessionId ? `?session_id=${sessionId}` : ""; | |
| return apiFetch<{ messages: ChatHistoryMessage[]; sessions: ChatSession[]; total: number }>(`/api/memory/history${qs}`); | |
| }, | |
| save: (data: { session_id?: string; role: string; content: string; session_title?: string }) => | |
| apiFetch<ChatHistoryMessage>("/api/memory/save", { method: "POST", body: JSON.stringify(data) }), | |
| clear: (sessionId?: string) => { | |
| const qs = sessionId ? `?session_id=${sessionId}` : ""; | |
| return apiFetch<{ deleted: number }>(`/api/memory/clear${qs}`, { method: "DELETE" }); | |
| }, | |
| getPreferences: () => | |
| apiFetch<{ theme: string; language: string }>("/api/memory/preferences"), | |
| updatePreferences: (data: { theme?: string; language?: string }) => | |
| apiFetch<{ theme: string; language: string }>("/api/memory/preferences", { | |
| method: "PATCH", | |
| body: JSON.stringify(data), | |
| }), | |
| }; | |
| // βββ Documents ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface DocumentRecord { | |
| id: string; | |
| filename: string; | |
| file_type: string; | |
| file_size: number; | |
| summary: string | null; | |
| insights: string[]; | |
| extracted_length: number; | |
| created_at: string; | |
| } | |
| export interface DocumentDetail extends DocumentRecord { | |
| messages: Array<{ id: string; role: string; content: string; language: string; created_at: string }>; | |
| } | |
| export const documentsApi = { | |
| upload: (file: File, language = "en") => { | |
| const form = new FormData(); | |
| form.append("file", file); | |
| const token = tokenStore.getAccess(); | |
| return fetch(`${API_BASE}/api/documents/upload?language=${language}`, { | |
| method: "POST", | |
| headers: token ? { Authorization: `Bearer ${token}` } : {}, | |
| body: form, | |
| }).then(async (res) => { | |
| if (!res.ok) { | |
| const err = await res.json().catch(() => ({})); | |
| throw new ApiError(res.status, err.detail || `HTTP ${res.status}`); | |
| } | |
| return res.json() as Promise<DocumentRecord & { suspicious: string[]; extracted_length: number }>; | |
| }); | |
| }, | |
| history: () => apiFetch<{ documents: DocumentRecord[] }>("/api/documents/history"), | |
| get: (id: string) => apiFetch<DocumentDetail>(`/api/documents/${id}`), | |
| chat: (id: string, question: string, language = "en") => | |
| apiFetch<{ question: string; answer: string; document_id: string; language: string }>( | |
| `/api/documents/chat/${id}`, | |
| { method: "POST", body: JSON.stringify({ question, language }) } | |
| ), | |
| analyze: (id: string, language = "en") => | |
| apiFetch<{ id: string; summary: string; insights: string[]; suspicious: string[] }>( | |
| `/api/documents/analyze/${id}?language=${language}`, | |
| { method: "POST" } | |
| ), | |
| delete: (id: string) => | |
| apiFetch<{ message: string }>(`/api/documents/${id}`, { method: "DELETE" }), | |
| }; | |