Full-stack self-hosted finance app with FastAPI backend and React frontend. Features: - Accounts, transactions, budgets, investments with GBP base currency - CSV import with auto-detection for 10 UK bank formats - ML predictions: spending forecast, net worth projection, Monte Carlo - 7 selectable themes (Obsidian, Arctic, Midnight, Vault, Terminal, Synthwave, Ledger) - Receipt/document attachments on transactions (JPEG, PNG, WebP, PDF) - AES-256-GCM field encryption, RS256 JWT, TOTP 2FA, RLS, audit log - Encrypted nightly backups + key rotation script - Mobile-responsive layout with bottom nav Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
2.7 KiB
TypeScript
107 lines
2.7 KiB
TypeScript
import { api } from "./client";
|
|
|
|
export interface Account {
|
|
id: string;
|
|
name: string;
|
|
institution: string | null;
|
|
type: string;
|
|
currency: string;
|
|
current_balance: number;
|
|
credit_limit: number | null;
|
|
interest_rate: number | null;
|
|
is_active: boolean;
|
|
include_in_net_worth: boolean;
|
|
color: string;
|
|
icon: string | null;
|
|
notes: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface AccountCreate {
|
|
name: string;
|
|
institution?: string;
|
|
type: string;
|
|
currency?: string;
|
|
credit_limit?: number;
|
|
interest_rate?: number;
|
|
include_in_net_worth?: boolean;
|
|
color?: string;
|
|
opening_balance?: number;
|
|
notes?: string;
|
|
}
|
|
|
|
export async function getAccounts(): Promise<Account[]> {
|
|
const res = await api.get("/accounts");
|
|
return res.data;
|
|
}
|
|
|
|
export async function createAccount(data: AccountCreate): Promise<Account> {
|
|
const res = await api.post("/accounts", data);
|
|
return res.data;
|
|
}
|
|
|
|
export async function updateAccount(id: string, data: Partial<AccountCreate>): Promise<Account> {
|
|
const res = await api.put(`/accounts/${id}`, data);
|
|
return res.data;
|
|
}
|
|
|
|
export async function deleteAccount(id: string): Promise<void> {
|
|
await api.delete(`/accounts/${id}`);
|
|
}
|
|
|
|
export interface CsvMapping {
|
|
date: string;
|
|
description: string;
|
|
amount: string | null;
|
|
debit: string | null;
|
|
credit: string | null;
|
|
balance: string | null;
|
|
reference: string | null;
|
|
}
|
|
|
|
export interface ImportPreview {
|
|
detected_format: string | null;
|
|
headers: string[];
|
|
mapping: CsvMapping;
|
|
total_rows: number;
|
|
preview: {
|
|
date_raw: string;
|
|
description_raw: string;
|
|
amount_raw: number | null;
|
|
balance_raw?: string;
|
|
}[];
|
|
}
|
|
|
|
export async function previewImport(accountId: string, file: File): Promise<ImportPreview> {
|
|
const form = new FormData();
|
|
form.append("file", file);
|
|
const res = await api.post(`/accounts/${accountId}/import/preview`, form);
|
|
return res.data;
|
|
}
|
|
|
|
export async function importCsvToAccount(
|
|
accountId: string,
|
|
file: File,
|
|
mapping: CsvMapping
|
|
): Promise<{ imported: number; skipped: number }> {
|
|
const form = new FormData();
|
|
form.append("file", file);
|
|
form.append("date_col", mapping.date);
|
|
form.append("description_col", mapping.description);
|
|
form.append("amount_col", mapping.amount ?? "");
|
|
form.append("debit_col", mapping.debit ?? "");
|
|
form.append("credit_col", mapping.credit ?? "");
|
|
const res = await api.post(`/accounts/${accountId}/import`, form);
|
|
return res.data;
|
|
}
|
|
|
|
export async function getNetWorth(): Promise<{
|
|
total_assets: number;
|
|
total_liabilities: number;
|
|
net_worth: number;
|
|
base_currency: string;
|
|
}> {
|
|
const res = await api.get("/accounts/net-worth");
|
|
return res.data;
|
|
}
|