import { useEffect, useState } from "react"; import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer, LineChart, Line, XAxis, YAxis, CartesianGrid, } from "recharts"; import { Trash2 } from "lucide-react"; import { api } from "@/api/client"; import type { Transaction, Snapshot, CategorySummary } from "@/api/types"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; const COLORS = [ "hsl(220 90% 56%)", "hsl(150 60% 50%)", "hsl(40 90% 56%)", "hsl(0 84% 60%)", "hsl(270 60% 56%)", "hsl(180 60% 45%)", "hsl(30 80% 55%)", "hsl(320 60% 50%)", ]; export default function Finances() { const [transactions, setTransactions] = useState([]); const [snapshots, setSnapshots] = useState([]); const [summary, setSummary] = useState([]); const [tab, setTab] = useState<"transactions" | "networth">("transactions"); // Transaction form const [tDate, setTDate] = useState(today()); const [tAmount, setTAmount] = useState(""); const [tCategory, setTCategory] = useState(""); const [tDesc, setTDesc] = useState(""); // Snapshot form const [sDate, setSDate] = useState(today()); const [sNetWorth, setSNetWorth] = useState(""); const [sNotes, setSNotes] = useState(""); const load = () => { api.get("/finances/transactions").then(setTransactions); api.get("/finances/snapshots").then(setSnapshots); api.get("/finances/summary?period=month").then(setSummary); }; useEffect(() => { load(); }, []); const handleAddTransaction = async (e: React.FormEvent) => { e.preventDefault(); await api.post("/finances/transactions", { date: tDate, amount: parseFloat(tAmount), category: tCategory, description: tDesc || null, }); setTAmount(""); setTCategory(""); setTDesc(""); load(); }; const handleDeleteTransaction = async (id: number) => { await api.delete(`/finances/transactions/${id}`); load(); }; const handleAddSnapshot = async (e: React.FormEvent) => { e.preventDefault(); await api.post("/finances/snapshots", { date: sDate, net_worth: parseFloat(sNetWorth), notes: sNotes || null, }); setSNetWorth(""); setSNotes(""); load(); }; const pieData = summary.map((s) => ({ name: s.category, value: Math.abs(s.total), })); const nwData = [...snapshots].reverse(); return (

Finances

{/* Tab switcher */}
{tab === "transactions" && ( <>
{/* Transaction form */} Add Transaction
setTDate(e.target.value)} required />
setTAmount(e.target.value)} required />
setTCategory(e.target.value)} placeholder="e.g. food, rent" required />
setTDesc(e.target.value)} />
{/* Category pie chart */} Monthly Spending by Category {pieData.length > 0 ? ( `${name}: $${value.toFixed(0)}`} > {pieData.map((_, i) => ( ))} `$${v.toFixed(2)}`} /> ) : (

No spending data this month.

)}
{/* Transactions table */} Recent Transactions
{transactions.map((t) => ( ))}
Date Amount Category Description
{t.date} = 0 ? "text-green-600" : "text-red-500"}`}> {t.amount >= 0 ? "+" : ""}${Math.abs(t.amount).toFixed(2)} {t.category} {t.description || "—"}
)} {tab === "networth" && (
{/* Snapshot form */} Log Net Worth
setSDate(e.target.value)} required />
setSNetWorth(e.target.value)} required />
setSNotes(e.target.value)} />
{/* Net worth chart */} Net Worth Over Time {nwData.length > 0 ? ( `$${v.toLocaleString()}`} /> ) : (

No snapshots yet.

)}
)}
); } function today() { return new Date().toISOString().slice(0, 10); }