fix(reports): stop the Spending tab crowning its own data-quality gaps - #154
Merged
Conversation
KenTaniguchi-R
force-pushed
the
fix/147-charts
branch
from
September 2, 2026 13:04
f119d06 to
f307a20
Compare
The information-design third of #147, built to the Spending tab mock. The summary bar read Total Spent · Categories · Top: X. "Categories: 18" is a number no decision turns on, and the crown went to `data[0]` blindly — so whenever Uncategorized was the largest line, which is the usual case, the page put a trophy on the spending it had failed to identify. Three tiles replace it. The headline names the range it measured ("Total spent · Jun 2 – Sep 2") and splits the figure into categorized and uncategorized, so the share that is unaccounted for is stated rather than decorated. The second says what the comparison is against and that it is the same length. The third gives spending as a share of income received, which is a number a decision does turn on. Uncategorized also drew `CHART_COLORS[0]`, the most saturated blue, making "we do not know" the visual hero of the chart. It takes `--chart-neutral` now, the treatment the synthetic "Other" slice already had, in both the chart and the table's category tile. Uncategorized is not a category — it is the absence of one — and should not look like the loudest. Bar becomes the default view. Nine categories spanning three orders of magnitude is a size comparison, which bars read directly. A blank cell in the Change column meant two different things. `prevTotal` defaulted to `0` for a category with no baseline row, and `ComparisonBadge` rendered nothing for zero — so "this category is new" and "this category did not move" were the same empty cell. `prevTotal` is `number | null` now, null meaning no baseline row (and null throughout when no comparison period was requested at all), and the badge renders "New". The MCP tool reports null rather than 0 for the same reason: a consumer should not be able to compute a change against a period the category was not in. The date chip said "Last 3 months" and the resolved dates appeared nowhere on the page. It prints them. Also added: a % of total column, so a row's share is readable without dividing by the headline. Verified against demo data with a temporary uncategorized charge and a category with no baseline: Uncategorized reads $3,200.00 · 18.8% · New with a neutral bar, Property Tax reads $450.00 · 2.6% · New, and the headline splits $17,055.33 into $13,855.33 categorized and $3,200.00 uncategorized at 73.8% of $23,100.00 received. Rows removed afterwards. Refs #147
KenTaniguchi-R
force-pushed
the
fix/147-information-design
branch
from
September 2, 2026 13:06
09a4c25 to
61c7fa0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The information design third of #147, built to your Spending tab mock. Completes the issue.
Stacked on #153 → #152 → #151 → #150.
The summary bar celebrated the wrong things
Total Spent · Categories · Top: X. "Categories: 18" is a number no decision turns on, andtopCategory = data[0]took the largest row blindly — so whenever Uncategorized was largest, which is the usual case, the page put a crown on the spending it had failed to identify.Three tiles replace it, per the mock:
● Categorized/● UncategorizedMar 2 – Jun 2· the preceding period, same length73.8%of $23,100.00 receivedThe split states the unaccounted-for share instead of decorating it.
Uncategorized was the loudest colour on the page
It drew
CHART_COLORS[0], the most saturated blue. It takes--chart-neutralnow — the treatment the synthetic "Other" slice already had — in the chart and in the table's category tile. Uncategorized is not a category, it's the absence of one, and shouldn't look like the hero.Bar is the default view. Nine categories spanning three orders of magnitude is a size comparison; bars read it directly.
A blank Change cell meant two different things
prevTotaldefaulted to0for a category with no baseline row, andComparisonBadgerendered nothing for zero — so "this category is new" and "this category didn't move" were the same empty cell.prevTotalisnumber | nullnow (null when there's no baseline row, and null throughout when no comparison period was requested), and the badge renders New. The MCP tool reports null rather than 0 for the same reason: a consumer shouldn't be able to compute a change against a period the category wasn't in.The resolved range appeared nowhere
The chip said "Last 3 months" and the actual dates were on no part of the page. It prints them:
Date: Last 3 months Jun 2 – Sep 2.Also added a % of total column, so a row's share is readable without dividing by the headline.
Tests
src/lib/comparison-state.test.ts— 7 tests: a null baseline is new, a zero baseline is new, up / down / flat, the half-percent boundary, and a category that spent nothing against a real baseline (−100%, not "new").tests/integration/report-drill-down.test.ts— 2 added: a category absent from the baseline reportsnullwhile one present reports its figure; with no comparison period nothing claims a previous figure.Full suite: 1095 passed, 1 failed — the pre-existing
investment-queriesdate rot.Verification
Inserted a temporary uncategorized charge and a charge in a category with no baseline, then read the live page:
$3,200.00 · 18.8% · New, bar fillvar(--chart-neutral)$450.00 · 2.6% · New$17,055.33, split$13,855.33categorized /$3,200.00uncategorized,73.8%of$23,100.00Rows removed afterwards.
Not built
The mock also proposes a "Data issues in this range" panel (uncategorized count with a link to categorize, categories that net positive and so vanish from spending, transfers miscategorized as spending). That's a new feature rather than information design and sits outside what #147 lists — worth its own issue if you want it.
🤖 Generated with Claude Code