Skip to content

Charts Reimagined: KPI + Chart UX refresh (built on UI Tweaks) - #95

Open
terryaney wants to merge 24 commits into
davidfowl:mainfrom
terryaney:feature/charts-reimagined
Open

Charts Reimagined: KPI + Chart UX refresh (built on UI Tweaks)#95
terryaney wants to merge 24 commits into
davidfowl:mainfrom
terryaney:feature/charts-reimagined

Conversation

@terryaney

Copy link
Copy Markdown
Contributor

Summary

This PR reimagines the KPI and chart experience in the HTML spending report while preserving the same core budgeting and analysis intent. The functionality is still there, but presented with a different interaction model and additional chart capabilities that I wanted to introduce.

I intentionally kept this separate from PR #94 because this is a larger product-direction change in presentation and behavior, not just incremental polish which has possibility of never being merged while I feel PR #94 is a safe, incremental improvement that will most likely be merged.

Review Guide

image

Please use charts.html as the primary feature walkthrough instead of reproducing all details in this PR body. That page documents the chart/KPI behavior and intent implemented on this branch.

Dependency and Merge Plan

This PR is based on PR #94 (feature/ui-tweaks) and also intersects with PR #91 lineage.

If PR #91 and/or PR #94 are merged first, this branch picks up conflicts. I will then:

  1. Rebase/merge onto latest main
  2. Resolve conflicts
  3. Re-run validation
  4. Push an updated revision for review

Scope in This Branch

  1. KPI strip and chart interaction refresh
  2. Category chart behavior updates, including empty bucket cleanup
  3. Cash Flow, Recurring vs Variable, Seasonality, Volatility, and Recurring views refinements
  4. Responsive and chart state behavior improvements
  5. Documentation updates, including the new charts documentation page and sitemap image metadata
  6. Test updates for chart/report behavior
  7. KPI compatibility hooks restored for report_html test selectors used by existing tests
  8. Post-hook KPI behavior hardening (anchor month and sparkline/trend correctness)
  9. KPI detail-row math reconciliation so trend baseline and displayed 12-month average align

terryaney added 24 commits July 13, 2026 15:05
1. Removed year from title to support multi-year data/reporting and made page title match content title
2. Data from... subtitle is distinct list of source
3. Include Negatives buttons hides if no negatives or has badge count
	showing how many negative merchants exist
4. Revamped date filter to be more functional than simply just a month list
Both bugs are the same root cause: the JS classified transactions using
merchant.tags — the union of every tag across all of a merchant's
transactions (analyzer.py:122 builds it that way) — instead of the
transaction's own txn.tags, which report.py already emits per transaction.
categorizeAmount() is a strict income -> investment -> transfer -> sign
chain, so one tagged transaction re-bucketed every other transaction at
that merchant. This changed reported dollar amounts; it is not cosmetic.

Bug 1 — charts and the Filtered View tile (filteredViewTotals,
chartAggregations). Every number in all three charts and in the Filtered
View KPI tile was wrong. A merchant whose union is
[monthly-bill, refund, transfer] had all of its ordinary purchases counted
as transfers and vanish from spending. Now classified per transaction, the
JS reconciles with the Python KPIs to the dollar.

Both KPI tiles now agree line-for-line on unfiltered data.

Bug 2 — Section View percentages (grossSpending). grossSpending was
grandTotal + creditsTotal, and both inputs drop a whole merchant when its
tag union is excluded from spending. It feeds the (X%) badge beside each
section header, which renders whenever typeTotals is absent — that is,
Section View always (Category/Subcategory pass server-computed typeTotals
and were never affected). The denominator ran 15% low: $215,322 against a
true $253,781, hiding $6,910 of Amazon spending, $2,448 of Minnesota
Department of Revenue, and more. grossSpending now derives from the same
per-transaction pass and equals Python's spendingTotal exactly. Monthly
Bills reads 23.8% (was 28.0%), Food & Dining 20.0%.

Chart identity
- Renamed: "Monthly Trend" -> "Cash Flow Trend", "By Category" -> "Spending
  by Category", "Category Trends by Month" -> "Spending by Category Trend".
- Cash Flow Trend now plots Spending, Income, Credits, and Investment.
  Credits were previously discarded despite being part of the cash-flow
  definition (income - spending + credits). Transfers stay excluded.
- Removed the synthetic Income/Investment datasets from the category trend
  chart; it is now purely spending by category. Those datasets were built
  from tags, not real categories, so clicking one filed a category filter
  that matched nothing and blanked all three charts.

Consistent interactions
- Legends are visibility toggles on all three charts. The category trend
  legend previously added a category filter on click.
- Removed the data-click filter handlers from both category charts. The
  Cash Flow Trend bar click (month filter) is retained.

Correctness and rendering
- Both category charts share one ranking: top 10 categories plus an "Other"
  rollup. The pie previously listed all 16 categories (legend overflowed and
  clipped) while the trend chart silently dropped everything past the top 8,
  under-reporting spending. "Other" is omitted when 10 or fewer categories
  are present.
- Series with no data are no longer emitted, so an all-zero Investment
  series no longer leaves a phantom legend entry.
- Months with nothing to plot are dropped per chart (a transfer-only month
  drew an empty column).
- Cash Flow Trend bar clicks index into monthlyChartMonths — the months
  actually plotted — instead of availableMonths. Under a date filter the two
  diverged and a bar click filed the wrong month.
- Added min-width: 0 to .chart-container; the grid default of min-width:
  auto locked tracks open and clipped canvases on re-widen.

Documented, not fixed: creditMerchants has the same merchant-union bug. It
is unrendered — the "Credits Applied" section was dropped from the template
in ad9477e while docs/reference.html still documents it — so a comment on
the computed records the bug and the two ways to fix it when the section is
restored.
1. Introduce **containment** — wrap the button row + all category collapsers in one **Transaction Details** card that collapses to **header only** (fixing the "empty padded band when collapsed" that Trends shows today).
2. Left-align the view buttons; add collapse/expand controls on the right.
3. Polish each collapser: anchored header + rows-only skinny scroll, bordered table, glowing color dot, non-blue label, and Count-centered / Total-right / %-right columns.
- Unify Merchant/Subcategory category views into one keyed v-for so Vue
  patches <section> nodes in place instead of remounting on a groupByMode
  toggle (preserves collapse state, no DOM churn).
- Remove the staggered fadeIn entrance animation. The details <section> is the
  only <section> in the report, so those global `section { animation }` +
  nth-child rules only ever animated it, replaying as a cascade every time the
  view toggle remounted the section-view sections.
Wrap the toggle on mobile (<=640px) — view modes on line 1, actions on line 2;
Collapse becomes icon-only to fit. Verified at 360/390px; 8 container tests pass.
UI and interaction polish

- Reworked merchant row action presentation: explicit info and filter links
- Refined expanded transaction row structure to better separate date,
  account badge, description, tags, and amount.
- Fixed miscellaneous CSS issues for hard coded colors, popup layout
  and transaction details buttons.  Removed dead CSS rules
- Hide app components (and added progress bar) while VUE was mounting/initializing

Adaptive column sizing architecture

- Introduced dynamic transaction column sizing via CSS variables for
  date, account, and amount tracks.
- Added startup-time width profiling and debounced resize recalculation
  for desktop-first responsiveness.

Rule Info Fixes

- Added explicit rule provenance into popup data (ruleName, pattern, source)
  so "Why This Matched" reflects the actual classification path.
- Improved fallback behavior: when no categorization rule matches, promote
  the first matching tag rule so popup rule details remain meaningful.
- Switched popup tag display to merchant-level union tags (matching the
  main grid) so tags like transfer are not dropped when first-transaction
  match_info is incomplete.
- Cleaned up tag-source reporting to avoid redundant/misleading "from [rule]"
  lines when the displayed rule already explains the tag.

Outcome

- Delivers a cleaner, more consistent UI with stronger alignment and spacing.
- Makes transaction detail rows feel more intentional and legible across real data variance.
- Transaction Trends collapsed state
- Transaction Details collapsed state
- Details view mode (Merchant, Subcategory, View)
- Include Negatives toggle
- Per-section collapse state
- Per-item expansion state (merchant/subcategory rows)
- Per-section sort settings
getFilterDescriptor() used item.id (category-specific) instead of
item.displayName for merchant-type filters, breaking cross-category
merchant filtering when the same merchant appears in multiple categories.

Also brings the test suite in line with two intentional behavior changes
already on this branch that its own tests hadn't caught (no CI runs on
this branch's pushes):
- Category sections now default to collapsed on a fresh load ("Save
  Layout Settings to Local Storage"), so tests need to expand sections
  before interacting with rows. Added an autouse fixture instead of
  touching every test.
- Transaction dates always show the year now (row UX polish), so the
  multi-year-only assertions were stale.
…ar paging

- add debounced chart rerender on viewport/layout changes via resize + ResizeObserver
- disable animation only for resize-triggered rerenders to avoid redraw jitter
- make x-axis labels adaptive (horizontal/angled/vertical) with autoskip under tight widths
- improve compare-year sublabels (font parity, angle direction, overlap reduction)
- cap compare mode to 3 years and add paging for older/newer year windows
- persist compare-year page state across UI state save/restore
- expand Playwright chart coverage for compare-year windowing/paging and updated chart controls
- make chart grouping pills, compare toggles, and legend chips compact on small viewports
…abels

- align 12 Month Avg detail row with trend prior-12 baseline window
- keep trend on prior-window comparison while preserving anchor-month behavior
- include credits in Income detail primary values; keep gray secondary informational
- use calculateCashFlow parity in KPI flow computations
- update KPI styling and report_html coverage for new semantics
- Build recurring merchant lists from spending-classified transactions only, so transfer/income/investment-only merchants do not appear in fixed calculations.
- Update Recurring vs Variable footnote to Top 10 Fixed: ..., + N more.
- Rename Subscription & Recurring Audit to Fixed Spending Audit in report UI.
- Update charts documentation wording to match fixed terminology.
- make trend report panels full width and reorder chart sections
- remove split-row and single-expanded reactive layout logic
- prevent chart redraws on panel expand/collapse; rerender only when chart-section width changes
- scope cash-flow and fixed-variable control updates to rerender only their own charts
- make chip legends hide zero-data items by default when values are provided
- fix cash-flow tooltip Net math to exclude Investments
- rename chart-specific rerender helpers for clarity (rerenderCashFlow, rerenderFixedVariable)
@terryaney

Copy link
Copy Markdown
Contributor Author

@davidfowl Hmm...want me to update the node versions in the action file(s) as a new PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant