Generalize the report category collapse from level-1-only to every hierarchy
level, on the three hierarchical report tables (real-vs-real Compare,
real-vs-budget Compare, Trends by category). Visibility is now decided by an
ancestor walk, not by row adjacency, so it is independent of row order (the
level-ordered budget grid emits a non-DFS order).
- collapsibleRows: rewrite visibleRows as an ancestor walk (a row is hidden iff
any ancestor is collapsed); add parentKeyOf + injective `p:` keys;
collapsibleKeys returns all parents (any depth); extract the pure, tested
isCollapsedFor polarity helper; MAX_TREE_DEPTH cycle guard.
- useCollapsibleGroups: persist in user_preferences (per-profile, destroyed with
the profile) instead of localStorage; storageKey nullable (no persistence);
options.defaultExpanded; async hydration (no flash); collapseAll(rows).
- 3 tables: fix BOTH gates (collapsed flag + button) isTopParent -> isParent, add
parentKeyOf accessors, aria-level on parent rows.
- Delete dead CategoryTable.tsx (0 imports).
- Tests: rewrite collapsibleRows.test.ts (BFS==DFS masking, cycle guard,
cross-section ancestor, "(direct)" leaf, polarity); extend overTimeTableModel
fixture to 3 levels with cascade assertions.
Collapse stays purely visual: subtotals and result lines are computed from raw
rows, never from visible rows.
Resolves#288
Generated autonomously by /autopilot run of 2026-07-15
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renders the trends-by-category table from the #264 id-keyed tree as an indented
parent/child hierarchy (parity with ComparePeriodTable): top-level parents carry
a chevron and fold their subtree down to a subtotal, with an "expand all /
collapse all" button. Collapsed by default; expansion state is persisted under a
trends-specific localStorage key (reports-trends-expanded), distinct from the
comparable tables. The "result before transfers" line is interleaved before the
transfers section instead of sitting at the bottom.
Collapse is purely visual: section subtotals, before/net results and totals come
from computeOverTimeResults over the raw tree, never the visible rows.
New pure module overTimeTableModel.ts (section grouping + collapse accessors)
keeps the component thin and unit-testable without a React render harness;
overTimeResults.ts is left untouched. 8 new tests; build + 728 vitest green.
Resolves#265
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an id-keyed hierarchical `tree: OverTimeRow[]` sidecar to
getCategoryOverTime, built via the generic buildLeafDrivenTree (#263) as
buildOverTimeTree — leaf-driven, income-first, no top-N / no "Other" bucket.
The name-keyed pivot (data/categories/colors/types/categoryIds) is left
BYTE-IDENTICAL, so the Trends chart and the dashboard render unchanged.
computeOverTimeResults now consumes the id-keyed tree LEAVES (never the
`is_parent` subtotals) grouped by each leaf's category_id-resolved type, so
the section subtotals and the Result-before/net lines are exact: two homonym
categories of different types no longer collide, and a non-top-N income or
transfer category is no longer lumped into "Other" as an expense.
CategoryOverTimeTable renders the tree leaves (every category, id-safe)
instead of the top-N pivot; the chart keeps reading the top-N-capped pivot so
its stacked series stay bounded. OverTimeRow mirrors CategoryDelta's snake_case
hierarchy block (parent_id/is_parent/depth/category_type) so it composes with
collapsibleRows / useCollapsibleGroups for the #265 hierarchy work.
Tests: rewrote overTimeResults.test.ts onto the tree (homonym regression +
leaves-only-not-parents); added buildOverTimeTree suite (nesting, income-first,
grand-total invariance, orphan, A->B->A cycle depth guard) and a
getCategoryOverTime tree-wiring test proving the pivot stays top-N+Other while
the tree carries every category. 720 vitest green, build + tsc green.
Resolves#264
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply the compare report's "result analysis" presentation to Trends -> By
category (table view). Categories are grouped into Income -> Expenses ->
Transfers sections with per-month and total subtotals; the old mixed "Total"
row (which summed absolute magnitudes into a meaningless figure) is replaced by
a Net result row per month (income - expenses + transfers) plus a Result before
transfers row shown only when transfers exist, both coloured by sign. Category
cells stay neutral levels.
- reportService.getCategoryOverTime: project COALESCE(c.type,'expense') in both
SELECTs and return an additive `types` map (built from the top-N rows, like
`colors`). Dashboard widget and CategoryOverTimeChart are untouched.
- shared types: add `types` to CategoryOverTimeData.
- new pure module overTimeResults.ts (computeOverTimeResults) with unit tests -
the per-month result reducer, mirroring the compare's pure Totals helper.
- i18n: add reports.compare.resultNet / resultBeforeTransfers (FR+EN); reuse the
existing sections.* and total* keys.
- .gitignore: anchor the autopilot `reports/` rule to `/reports/` so it no
longer swallows new files under src/components/reports/.
Section order is income-first (Revenus -> Depenses -> Transferts), per the
issue; the existing COMPARE_TYPE_ORDER is expense-first, so a local
OVER_TIME_TYPE_ORDER is used rather than reusing it.
Resolves#256
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Section subtotals: text-sm font-semibold with more padding.
Grand totals: text-sm font-bold with border-t-2 and extra padding.
Applied consistently across BudgetTable, BudgetVsActualTable,
MonthlyTrendsTable, CategoryOverTimeTable, CategoryTable,
and DynamicReportTable.
Closes#14
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add table/chart toggle for Trends, By Category, and Over Time reports
- Add "Show amounts" toggle to display values on chart elements
- Add filter panel with category checkboxes and source dropdown
- Add source filter at SQL level for all chart report queries
- Add sticky headers on Dynamic Report and Budget vs Actual tables
- Add interactive hover: dimmed non-hovered bars, filtered tooltip, legend hover
- Fix comment icon color to match split indicator (orange) (#7)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>