import axios from 'axios'; const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || '/api/v1', }); api.interceptors.request.use((config) => { const token = localStorage.getItem('token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); api.interceptors.response.use( (res) => res, (err) => { if (err.response?.status === 401) { localStorage.removeItem('token'); window.location.href = '/login'; } return Promise.reject(err); }, ); export default api; export async function login(username: string, password: string) { const form = new URLSearchParams(); form.append('username', username); form.append('password', password); const { data } = await api.post('/auth/login', form); localStorage.setItem('token', data.access_token); return data; } export interface Account { id: number; bank: string; currency: string; label: string; balance: number; account_type: string; next_payment: number | null; updated_at: string; } export interface Category { id: number; name: string; icon: string; auto_match_patterns: string | null; } export interface ImportResult { imported: number; duplicates: number; errors: string[]; } // --- User Settings --- export interface SectionSettings { label: string; color: string; cardColor: string; visible: boolean; order: number; expanded: boolean; } export interface DashboardSettings { sections: Record; } export interface UserSettingsData { dashboard: DashboardSettings; } export interface UserSettingsResponse { key: string; data: UserSettingsData; updated_at: string; } export const getSettings = () => api.get('/settings/'); export const updateSettings = (data: UserSettingsData) => api.patch('/settings/', { data }); export interface Transaction { id: number; amount: number; currency: string; merchant: string; city: string | null; date: string; card_type: string | null; card_last4: string | null; authorization_code: string | null; reference: string | null; transaction_type: string; source: string; bank: string; notes: string | null; category_id: number | null; category: Category | null; created_at: string; } // --- Budget / Recurring Items --- export type RecurringItemType = 'INCOME' | 'EXPENSE' | 'SAVINGS'; export type RecurringFrequency = 'WEEKLY' | 'MONTHLY' | 'QUARTERLY' | 'BIANNUAL' | 'YEARLY'; export interface RecurringItem { id: number; name: string; amount: number; currency: string; item_type: RecurringItemType; frequency: RecurringFrequency; day_of_month: number | null; month_of_year: number | null; override_amounts: Record | null; category_id: number | null; is_active: boolean; notes: string | null; created_at: string; category: Category | null; } export interface RecurringItemCreate { name: string; amount: number; currency?: string; item_type: RecurringItemType; frequency?: RecurringFrequency; day_of_month?: number | null; month_of_year?: number | null; override_amounts?: Record | null; category_id?: number | null; is_active?: boolean; notes?: string | null; } export interface RecurringItemUpdate { name?: string; amount?: number; currency?: string; item_type?: RecurringItemType; frequency?: RecurringFrequency; day_of_month?: number | null; month_of_year?: number | null; override_amounts?: Record | null; category_id?: number | null; is_active?: boolean; notes?: string | null; } export interface RecurringItemDetail { id: number; name: string; amount: number; projected_amount: number | null; used_actual: boolean; item_type: string; frequency: string; category_name: string | null; category_id: number | null; } export interface ActualsBySource { source: string; total_compra: number; total_devolucion: number; net: number; count: number; } export interface MonthlyProjection { month: number; year: number; projected_income: number; projected_fixed_expenses: number; projected_savings: number; actual_credit_card: number; actual_cash: number; actual_transfers: number; uncovered_actual: number; gran_total_egresos: number; net_balance: number; carryover_balance: number; cumulative_balance: number; balance_overridden: boolean; } export interface YearlyProjection { year: number; months: MonthlyProjection[]; annual_income: number; annual_expenses: number; annual_savings: number; annual_net: number; } export interface MonthlyDetail { year: number; month: number; income_items: RecurringItemDetail[]; expense_items: RecurringItemDetail[]; savings_items: RecurringItemDetail[]; actuals_by_source: ActualsBySource[]; total_projected_income: number; total_projected_expenses: number; total_projected_savings: number; uncovered_actual: number; gran_total_egresos: number; net_balance: number; } // Budget API functions export const getRecurringItems = (params?: { item_type?: string; is_active?: boolean }) => api.get('/budget/recurring', { params }); export const createRecurringItem = (data: RecurringItemCreate) => api.post('/budget/recurring', data); export const updateRecurringItem = (id: number, data: RecurringItemUpdate) => api.patch(`/budget/recurring/${id}`, data); export const deleteRecurringItem = (id: number) => api.delete(`/budget/recurring/${id}`); export const getYearlyProjection = (year: number) => api.get(`/budget/projection/${year}`); export const getMonthlyDetail = (year: number, month: number) => api.get(`/budget/month/${year}/${month}`); export const upsertBalanceOverride = (year: number, month: number, override_balance: number) => api.put(`/budget/balance-override/${year}/${month}`, { override_balance }); export const deleteBalanceOverride = (year: number, month: number) => api.delete(`/budget/balance-override/${year}/${month}`); // --- Salarios --- export interface SalariosSummary { count: number; total_amount: number; latest_date: string | null; } export const getSalarios = (params?: { limit?: number; offset?: number }) => api.get('/salarios/', { params }); export const getSalariosSummary = () => api.get('/salarios/summary'); // --- Pensions --- export interface PensionSnapshot { id: number; fund: string; contract_number: string; period_start: string; period_end: string; saldo_anterior: number; aportes: number; rendimientos: number; retiros: number; traslados: number; comision: number; correccion: number; bonificacion: number; saldo_final: number; source_filename: string; created_at: string; } export interface PensionUploadResult { imported: number; updated: number; duplicates: number; errors: string[]; snapshots: PensionSnapshot[]; } export interface PensionManualEntry { fund: string; period_start: string; period_end: string; saldo_anterior: number; aportes: number; rendimientos: number; retiros: number; traslados: number; comision: number; correccion: number; bonificacion: number; saldo_final: number; } export const uploadPensionPDFs = (files: File[]) => { const form = new FormData(); files.forEach((f) => form.append('files', f)); return api.post('/pensions/upload', form); }; export const getPensionSnapshots = () => api.get('/pensions/snapshots'); export const getPensionFundSummary = () => api.get('/pensions/fund-summary'); export const submitPensionManualEntries = (entries: PensionManualEntry[]) => api.post('/pensions/manual', { entries });