diff --git a/src/app/budget/page.tsx b/src/app/budget/page.tsx index 20818a5..7868453 100644 --- a/src/app/budget/page.tsx +++ b/src/app/budget/page.tsx @@ -1,9 +1,11 @@ "use client"; -import { useState, Fragment, useMemo } from "react"; +import { useState, useEffect, Fragment, useMemo } from "react"; import { ComposedChart, LineChart, + AreaChart, + Area, Bar, Line, XAxis, @@ -12,12 +14,11 @@ import { ResponsiveContainer, Cell, ReferenceLine, - Legend, } from "recharts"; import { useQueryClient } from "@tanstack/react-query"; import { useMonthlyAnalytics, useTransactions, useUpdateTransaction } from "@/lib/hooks"; import { formatCategory, CATEGORIES } from "@/lib/categories"; -import { CATEGORY_COLORS, TOOLTIP_STYLE } from "@/lib/category-colors"; +import { CATEGORY_COLORS, CHART, TOOLTIP_STYLE } from "@/lib/category-colors"; function currentMonthStr(): string { const now = new Date(); @@ -28,11 +29,6 @@ function prevMonth(m: string): string { const d = new Date(year, month - 2, 1); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; } -function nextMonth(m: string): string { - const [year, month] = m.split("-").map(Number); - const d = new Date(year, month, 1); - return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; -} function formatMonth(m: string): string { const [year, month] = m.split("-"); return new Date(Number(year), Number(month) - 1, 1).toLocaleString("default", { month: "long", year: "numeric" }); @@ -41,44 +37,21 @@ function formatShortMonth(m: string): string { const [year, month] = m.split("-"); return new Date(Number(year), Number(month) - 1, 1).toLocaleString("default", { month: "short" }); } -function fmt(n: number): string { return `$${n.toFixed(0)}`; } +function fmt(n: number): string { return `$${Math.round(n).toLocaleString()}`; } function fmtExact(n: number): string { return `$${n.toFixed(2)}`; } -function deltaColor(n: number): string { - if (n > 0) return "text-red-400"; - if (n < 0) return "text-emerald-400"; - return ""; -} - +function fmtSigned(n: number): string { return `${n >= 0 ? "+" : "−"}$${Math.abs(n) >= 100 ? Math.round(Math.abs(n)).toLocaleString() : Math.abs(n).toFixed(0)}`; } // ─── Tooltips ──────────────────────────────────────────────────────────────── -function TrendTooltip({ active, payload, label }: { active?: boolean; payload?: { dataKey: string; value: number; stroke: string }[]; label?: string }) { - if (!active || !payload?.length) return null; - const items = payload.filter((p) => p.value > 0.01).sort((a, b) => b.value - a.value); - if (!items.length) return null; - return ( -
{label}
{formatCategory(d.category)}
Loading...
Loading…
No transactions
Ledger · {formatMonth(selectedMonth)}
+ {fmt(totals.spent)} +
+ 3 ? "text-indigo-300" : avgDeltaPct < -3 ? "text-emerald-400" : "text-zinc-400"}> + {heroSentence} + +
Income
{fmtExact(totals.income)}
received
Expenses
{fmtExact(totals.spent)}
- {spentDelta === 0 || lastTotals.spent === 0 - ? split-adjusted - : `${spentDelta > 0 ? "+" : ""}${fmtExact(spentDelta)} vs ${formatShortMonth(prevMonth(selectedMonth))}`} -
{hasIncome ? fmt(totals.income) : "—"}
Invested
{fmtExact(totals.investments)}
shares / ETFs
{hasIncome ? "Net Cash" : "Largest Category"}
{fmt(totals.spent)}
{hasInvestments ? fmt(totals.investments) : "—"}
Net cash
= 0 ? "text-emerald-400" : "text-red-400"}`}> - {totals.net >= 0 ? "+" : ""}{fmtExact(totals.net)} -
income − expenses − invested
{fmtExact(largestCategory.spent)}
{formatCategory(largestCategory.category)}
= 0 ? "text-emerald-400" : "text-red-400"}`}>{totals.net >= 0 ? "+" : ""}{fmt(totals.net)}
—
Biggest category moves vs {formatShortMonth(prevMonth(selectedMonth))}
{fmt(s.thisMonth)}
Ledger · patterns
Individual fee / interest transactions
Monthly Spend
{merchants.length} merchants · last {months} months
No unreconciled manual transactions. Import a CSV to get started.
{pending.length} manual transaction{pending.length !== 1 ? "s" : ""} ·{" "} {withMatches.length} with potential matches diff --git a/src/app/rules/page.tsx b/src/app/rules/page.tsx index 8ea68c1..882f896 100644 --- a/src/app/rules/page.tsx +++ b/src/app/rules/page.tsx @@ -161,7 +161,7 @@ export default function RulesPage() { return (
Group and analyse expenses by trip