Clicking a slice on the Spending by Category chart navigates to
/transactions?categoryId=<id>. The Transactions page reads that param as
its initial filter on mount (and strips it from the URL afterwards so
refreshes behave predictably, matching the existing `new=1` handling).
Also adds an explicit Category filter Select next to the existing
Account / Type filters so the user can change or clear the filter
in-page, and gives the pie slices a pointer cursor to signal they're
interactive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous fix gave each point a unique x-axis timestamp so the tooltip
could disambiguate same-day entries, but that caused real-time-scale
spacing: transactions clustered minutes apart became impossible to click
on individually. The desired behavior is equidistant slots for display,
with the x value only needing to be unique for tooltip hit-testing.
Switches the balance chart to a categorical x-axis keyed on the point's
array index (computed on the frontend after fetching). The tick formatter
looks up the date from the history array at that index so labels still
show the calendar date. Each point occupies its own evenly-spaced slot
and hover correctly identifies the specific point. The backend's now-
unused `timestamp` field is removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recharts picks one payload entry per x-axis value for its tooltip, so
when multiple transactions shared the same YYYY-MM-DD date, hovering any
of them surfaced only the first one's description — making the new
transaction-context tooltip misleading for same-day entries.
Each point now carries a millisecond `timestamp` (from the transaction's
createdAt for transaction-driven charts, or the valuation's date for
market-value charts, plus the request time for the "today" bookend).
The balance chart x-axis switches to a numeric time scale keyed on
timestamp, so same-day points sit at distinct x positions and hover
correctly identifies the specific point. The tooltip reads the date
from the point's payload instead of the x-axis label.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the default white Recharts tooltip (which was jarring in dark
mode) with a themed ChartTooltip component that uses the popover /
foreground / muted-foreground tokens so it adapts to light and dark
themes. Applied to the Spending, Income vs Expense, Cash Flow, and
Balance over time charts for visual consistency.
The Balance over time tooltip now also includes, when the point
corresponds to a transaction, the transaction's description and the
signed balance change (green for inflow, red for outflow). The backend
endpoint was extended to return `description` and `change` alongside
each transaction point.
Also kills the mobile tap-highlight box and focus outline that Recharts
SVG elements render on tap — it was drawing a visible rectangle around
the hit region on touch devices. A short rule in index.css scoped to
.recharts-wrapper / .recharts-surface zeroes both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related bugs:
1. Same-day transactions were walked in arbitrary order. The balance-history
endpoint ordered by `date desc` only; since all same-day transactions
share a noon-UTC timestamp, Prisma returned them in an undefined order
and the reverse walk could place income after expenses, making the chart
dip below the pre-income balance. Adds `createdAt desc` as a secondary
sort so the walk processes newest-inserted transactions first, which
produces an oldest-first chart sequence that matches the order the user
entered them.
2. Chart tick labels and tooltip headers were off by one day. The XAxis
tickFormatter and Tooltip labelFormatter called `new Date("YYYY-MM-DD")`,
which parses as midnight UTC — in timezones west of UTC this renders as
the previous day. Swaps to local-component parsing (tick) and the
existing `formatDate` helper (tooltip) so labels always match the stored
calendar date.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Transactions are stored at noon UTC (per parseDateInput) so the calendar
date survives timezone shifts, but parseDateRange was constructing the
end boundary via `new Date("YYYY-MM-DD")`, which parses as midnight UTC.
A transaction dated "2026-04-30" (2026-04-30T12:00:00Z) therefore fell
AFTER the filter's `lte` boundary and was silently excluded from the
income/expense, spending-by-category, and cash-flow aggregations for any
range ending on that day.
Rebuilds parseDateRange to explicitly use UTC start-of-day for `gte` and
UTC end-of-day for `lte`, so any transaction on the endpoint day is now
included. Adds a regression test that asserts the Prisma filter range
covers noon UTC on the last day of the range.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chat models (Ollama /api/chat) don't generate a reply when given only a
system message — they need a user turn to respond to. The new conversational
flow sent an empty messages array on "Get Advice", so Ollama received
only [system] and returned empty content, which rendered as a blank pill
in the UI. When the client message list is empty, inject a synthetic
opening-prompt user turn so the model always has something to reply to.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes the "undefined category" bug in generated insights: the PII-strip
filter listed 'name' as sensitive, which wiped category labels like
"Groceries" out of the prompt before it reached the model. Category
names are user-defined labels, not PII, so they're removed from the
filter; a defensive `?? 'Uncategorized'` fallback guards against future
regressions.
The system prompt is rewritten as a friendly first-person persona that
grounds its replies in exact dollar amounts and a month-over-month
comparison (income, expenses, savings rate, top-3 categories for both
months) instead of the previous generic "3-5 numbered tips" format.
Adds a stateless POST /advisor/chat endpoint that accepts the full
client-held message history and replies with the next assistant turn,
rebuilding the financial context on every call. The dashboard card
becomes a mini-chat: user/assistant bubbles, follow-up input, and a
restart button, with auto-scroll to the latest message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new /aggregations/cash-flow endpoint that reports money entering
and leaving liquid accounts (CHECKING/SAVINGS/CASH) over a period.
Defining outflows as "reduces a liquid account" naturally counts
credit-card and loan payments as outflows while excluding the original
credit-card swipes (which don't touch cash until paid), avoiding
double-counting. Dashboard gains a shared range selector (This month,
Last 30d, Last 90d, YTD) controlling the summary cards, spending, and
the new cash-flow card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends AccountType enum and counts all three new types as assets in net
worth. For STOCK/INVESTMENT/RETIREMENT accounts, adds an AccountValuation
model and /accounts/:id/valuations endpoints so users can log periodic
balance snapshots; the balance-history chart uses those snapshots for
market-value accounts instead of reconstructing from transactions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>