whisper-money/resources/js/pages
Víctor Falcón 7281b610b5
Render the Cashflow Sankey with recharts (responsive + expense drill-down) (#670)
## What & why

The Cashflow "Money Flow" diagram was a bespoke SVG Sankey that laid out
nodes and links by hand. It didn't hold up on small screens (fixed 400px
min-width, cramped flows) and was ~950 lines of custom geometry to
maintain.

This replaces it with recharts' `<Sankey>` (already a dependency) inside
a `ResponsiveContainer`, letting recharts own the layout. Net **−950 /
+515** lines across the component.

## What's kept

- Income → **Net** hub → expense flow.
- Small-category **"Other"** grouping (`groupSmallCategories`).
- Privacy-mode masking on every amount (hub + categories +
subcategories).
- Click-through to a category's transactions.
- Category colors, resolved via `categoryBarColor` (same as the
breakdown cards).

## What's new

- **Drill-down (expense side):** click an expense category with children
to expand its subcategories into their own column; recharts re-lays-out
around it. One open at a time, a chevron marks expandable parents, and
the expanded parent's label moves onto its bar. Subcategories and
childless categories still click through to their transactions. Data
comes from the existing `GET /api/cashflow/sankey?...&parent=<id>`
endpoint, fetched lazily and reset when the period changes.
- **Mobile:** the chart scrolls horizontally with a comfortable
min-width (same pattern as the Trend chart) instead of crushing the
flows; canvas height grows with the busiest column so labels don't
collide.
- `align="left"` so an expanded parent's subcategories line up beside it
instead of collapsing into the last column and crossing every flow.

## What's dropped (intentional)

- The "Other" popover breakdown — the per-category detail already lives
in the breakdown cards below the chart.
- Income-side drill-down — with the central hub, expanding an income
source would split the income column across two depths (visually
broken); income categories rarely have children. Income keeps
click-through.

## Review

Two parallel reviews (technical + bugs/side-effects) ran over the diff.
Applied:
- **Category colors:** resolve the `CategoryColor` keyword via
`categoryBarColor` instead of using it raw as an SVG `fill` — non-CSS
keywords (`slate`, `neutral`, `amber`, `rose`) were rendering **black**.
(Pre-existing bug carried over from the old chart.)
- Skip top-level categories with `amount <= 0` so they don't become
stray unlinked nodes.
- Keep an expanding parent's label beside its bar until subcategories
load (no flicker); collapse again if the fetch fails.
- Cap right-side label width so a parent left of an expanded column
can't stretch its label across it.
- Memoize the cashflow `period` so the drill-down fetch effect doesn't
re-run on unrelated re-renders.

Dropped (with reason): re-adding the "Other" popover and income
drill-down (intentional, above); wrapping aria-labels in `__()` (would
add i18n keys, and matches the prior behavior); the recharts node/link
prop casts (version is pinned; params are hand-typed to document the
fields used).

## Testing

- 6 Vitest unit tests (render, "Other" grouping, empty state, drill-down
with a mocked fetch, expand affordance, privacy masking) — green.
- `tsc --noEmit` clean for the touched files; ESLint + Prettier clean.

> **Note on browser QA:** I did not record an automated demo video. The
local DB holds real user financial data (won't log in / record), and
creating a synthetic test/demo account is currently blocked by pending
Spaces migrations in the local DB (`current_space_id` column missing —
unrelated to this change). The chart was verified interactively during
development (desktop render, mobile layout, drill-down, and the
black-color fix). Happy to record a walkthrough if we seed a safe demo
account.
2026-07-12 16:26:17 +00:00
..
Accounts fix(accounts): remove double-skeleton flash on account show (#634) 2026-07-03 15:56:49 +00:00
auth refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
budgets feat: add catch-all budgets (#527) 2026-06-15 16:07:19 +00:00
cashflow Render the Cashflow Sankey with recharts (responsive + expense drill-down) (#670) 2026-07-12 16:26:17 +00:00
onboarding feat(onboarding): auto-enable AI for connected banks, ask the rest (#618) 2026-07-01 07:26:52 +00:00
open-banking feat(connections): create a new account from the manage-accounts selector (#560) 2026-06-19 12:49:03 +02:00
settings refactor(ai): remove AiConsentSettings feature flag (#619) 2026-07-01 09:47:55 +02:00
subscription feat(subscriptions): reframe pay_now paywall copy around try-and-refund (#605) 2026-06-28 10:40:59 +00:00
transactions fix(security): scope job-status endpoints to owner + feature-area fixes (#627) 2026-07-03 14:49:32 +02:00
waitlist feat: verify waitlist leads (#285) 2026-04-14 11:26:01 +01:00
dashboard.tsx feat(dashboard): add accounts manager dialog with visibility toggle and reorder (#604) 2026-06-27 16:11:25 +00:00
privacy.tsx Remove plaintext-transactions feature flag & E2E references (#116) 2026-02-13 11:10:21 +01:00
terms.tsx Replace E2E encryption mentions with privacy-first messaging (#124) 2026-02-14 16:59:50 +01:00
welcome.tsx feat(welcome): add Francisco Montes testimonial (#636) 2026-07-04 14:08:33 +00:00