import { BACKEND_URL } from './config'; export type LoginResponse = { access_token: string; token_type: string; }; export type Category = { id: number; name: string; description?: string | null; }; export type Transaction = { id: number; amount: number; description?: string | null; category_ids: number[]; date?: string | null; // ISO date (YYYY-MM-DD) }; export async function deleteTransaction(id: number): Promise { const res = await fetch(`${getBaseUrl()}/transactions/${id}/delete`, { method: 'DELETE', headers: getHeaders('none'), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to delete transaction'); } } function getBaseUrl() { const base = BACKEND_URL?.replace(/\/$/, '') || ''; return base || ''; } function getHeaders(contentType: 'json' | 'form' | 'none' = 'json'): Record { const token = localStorage.getItem('token'); const headers: Record = {}; if (contentType === 'json') { headers['Content-Type'] = 'application/json'; } else if (contentType === 'form') { headers['Content-Type'] = 'application/x-www-form-urlencoded'; } if (token) { headers['Authorization'] = `Bearer ${token}`; } return headers; } export async function login(email: string, password: string): Promise { const body = new URLSearchParams(); body.set('username', email); body.set('password', password); const res = await fetch(`${getBaseUrl()}/auth/jwt/login`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: body.toString(), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Login failed'); } const data: LoginResponse = await res.json(); localStorage.setItem('token', data.access_token); } export async function register(email: string, password: string, first_name?: string, last_name?: string): Promise { const res = await fetch(`${getBaseUrl()}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password, first_name, last_name }), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Registration failed'); } } export async function getCategories(): Promise { const res = await fetch(`${getBaseUrl()}/categories/`, { headers: getHeaders(), }); if (!res.ok) throw new Error('Failed to load categories'); return res.json(); } export type CreateTransactionInput = { amount: number; description?: string; category_ids?: number[]; date?: string; // YYYY-MM-DD }; export async function createTransaction(input: CreateTransactionInput): Promise { const res = await fetch(`${getBaseUrl()}/transactions/create`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(input), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to create transaction'); } return res.json(); } export async function getTransactions(start_date?: string, end_date?: string): Promise { const params = new URLSearchParams(); if (start_date) params.set('start_date', start_date); if (end_date) params.set('end_date', end_date); const qs = params.toString(); const url = `${getBaseUrl()}/transactions/${qs ? `?${qs}` : ''}`; const res = await fetch(url, { headers: getHeaders(), }); if (!res.ok) throw new Error('Failed to load transactions'); return res.json(); } export type User = { id: string; email: string; first_name?: string | null; last_name?: string | null; is_active: boolean; is_superuser: boolean; is_verified: boolean; // Optional JSON config object for user-level integrations and settings // Example: { csas: "{\"expires_at\": 1761824615, ...}" } or { csas: { expires_at: 1761824615, ... } } config?: Record | null; }; export async function getMe(): Promise { const res = await fetch(`${getBaseUrl()}/users/me`, { headers: getHeaders(), }); if (!res.ok) throw new Error('Failed to load user'); return res.json(); } export type UpdateMeInput = Partial> & { password?: string }; export async function updateMe(input: UpdateMeInput): Promise { const res = await fetch(`${getBaseUrl()}/users/me`, { method: 'PATCH', headers: getHeaders(), body: JSON.stringify(input), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to update user'); } return res.json(); } export async function deleteMe(): Promise { const res = await fetch(`${getBaseUrl()}/users/me`, { method: 'DELETE', headers: getHeaders(), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to delete account'); } } export function logout() { localStorage.removeItem('token'); } // Categories export type CreateCategoryInput = { name: string; description?: string }; export async function createCategory(input: CreateCategoryInput): Promise { const res = await fetch(`${getBaseUrl()}/categories/create`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(input), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to create category'); } return res.json(); } export type UpdateCategoryInput = { name?: string; description?: string }; export async function updateCategory(category_id: number, input: UpdateCategoryInput): Promise { const res = await fetch(`${getBaseUrl()}/categories/${category_id}`, { method: 'PATCH', headers: getHeaders(), body: JSON.stringify(input), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to update category'); } return res.json(); } // Transactions update export type UpdateTransactionInput = { amount?: number; description?: string; date?: string; category_ids?: number[]; }; export async function updateTransaction(id: number, input: UpdateTransactionInput): Promise { const res = await fetch(`${getBaseUrl()}/transactions/${id}/edit`, { method: 'PATCH', headers: getHeaders(), body: JSON.stringify(input), }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to update transaction'); } return res.json(); } // Balance series export type BalancePoint = { date: string; balance: number }; export async function getBalanceSeries(start_date?: string, end_date?: string): Promise { const params = new URLSearchParams(); if (start_date) params.set('start_date', start_date); if (end_date) params.set('end_date', end_date); const qs = params.toString(); const url = `${getBaseUrl()}/transactions/balance_series${qs ? `?${qs}` : ''}`; const res = await fetch(url, { headers: getHeaders() }); if (!res.ok) { const text = await res.text(); throw new Error(text || 'Failed to load balance series'); } return res.json(); }