import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { TimeSeriesChart } from "@/components/charts/TimeSeriesChart"; import { SegmentedControl } from "@/components/charts/SegmentedControl"; import { ChartTypeToggle } from "@/components/charts/ChartTypeToggle"; import { useFinanceData } from "@/context/FinanceDataContext"; import { useMemo, useState } from "react"; import { cn } from "@/lib/utils"; import { useMoney } from "@/hooks/useMoney"; import { periodChange } from "@/lib/balanceSheet"; import { cashBalanceSeries } from "@/lib/balanceHistory"; import { useParentSize } from "@visx/responsive"; import { TrendingDown, TrendingUp, LineChart as LineChartIcon, BarChart3 } from "lucide-react"; const CHART_TYPES = [ { value: "line", icon: LineChartIcon, label: "Line" }, { value: "bar", icon: BarChart3, label: "Bar" }, ] as const; const periods = [ { label: "30 days", days: 30 }, { label: "3 months", days: 90 }, { label: "6 months", days: 180 }, { label: "All time", days: 999 }, ]; const periodOptions = periods.map((p) => ({ value: p.label, label: p.label })); interface BalanceChartProps { inline?: boolean; } export function BalanceChart({ inline }: BalanceChartProps) { const { transactions, accounts } = useFinanceData({ summary: true }); const [period, setPeriod] = useState("3 months"); const [chartType, setChartType] = useState<"line" | "bar">("line"); const { compact, money, date: fmtDate } = useMoney(); // Spendable cash (checking + savings) over time: see cashBalanceSeries for // why cars, loans and brokerage accounts are left out and how the line is // smoothed into a trend instead of a heartbeat. const days = periods.find((p) => p.label === period)?.days ?? 90; const data = useMemo( () => cashBalanceSeries(transactions, accounts, days >= 999 ? null : days), [transactions, accounts, days] ); const change = useMemo(() => periodChange(data.map((d) => d.value)), [data]); const { parentRef: fillRef, height: fillHeight } = useParentSize({ debounceTime: 30 }); const chart = (
{change ? (

{change.amount > 0 ? ( ) : change.amount < 0 ? ( ) : null} 0 ? "text-chart-green" : change.amount < 0 ? "text-chart-red" : "text-foreground")}> {change.amount > 0 ? "+" : change.amount < 0 ? "-" : ""}{money(Math.abs(change.amount))} {change.pct !== null && ` (${change.pct > 0 ? "+" : ""}${change.pct.toFixed(1)}%)`} {period === "All time" ? "all time" : `over ${period}`}

) : ( )}
{/* Inline, the plot grows to fill its card when the card beside it is taller, instead of leaving a blank band under the chart. */}
{data.length ? fmtDate(d)} chartType={chartType} /> :

No transactions to chart for checking and savings.

}
); if (inline) return chart; return (
Balance Over Time Your total balance over time
{chart}
); }