Skip to content

fix(reports): make the Uncategorized spending drill-down load its transactions - #141

Merged
KenTaniguchi-R merged 1 commit into
mainfrom
fix/uncategorized-drill-down
Sep 2, 2026
Merged

fix(reports): make the Uncategorized spending drill-down load its transactions#141
KenTaniguchi-R merged 1 commit into
mainfrom
fix/uncategorized-drill-down

Conversation

@KenTaniguchi-R

Copy link
Copy Markdown
Owner

Clicking Uncategorized on /reports (Spending tab) opened a drill-down sheet reading "No transactions found." Two bugs stacked on the same value.

1. The fetch never fired

DrillDownSheet fetched from an effect keyed on the filter's individual fields:

}, [filter?.categoryId, filter?.month, filter?.type, effectiveDateFrom, effectiveDateTo]);

While the sheet is closed filter is null, so every one of those reads as undefined. An uncategorized drill-down carried categoryId: item.id ?? undefined, making the open state field-for-field identical to the closed state — nothing in the deps changed, the effect never re-ran, and rows stayed [].

Every real category has a non-undefined id, which is why only Uncategorized broke, and why it appeared to work if you opened a real category first ("abc" → undefined is a change).

Now depends on the filter object itself, which is fresh state on each click.

2. Even once it fired, it queried the wrong thing

The null categoryId was flattened to undefined twice on the way down — in report-spending.tsx and again in actions/reports.ts. getTransactions reads null as IS NULL but undefined as no category filter at all (queries/transactions.ts:106-109), so the sheet would have listed every transaction in the range. The null is now carried through.

Also fixed

  • Footer link. "View all in Transactions" had the same hole: it dropped the category param for null, linking to all transactions. Extracted as drillDownTransactionsUrl, which emits the category=uncategorized sentinel parse-transaction-filters.ts already understands. Unit tests cover the three id states plus month narrowing.
  • The chart's "Other" slice. It also carries id: null, so with the null now meaningful it would have drilled into Uncategorized. It spans several categories, which drill-down can't express, so it's flagged synthetic and inert on click — that flag also replaces the fragile id === null && name === "Other" check used to pick its neutral color.

Cash Flow and Income vs Expense drill-downs are unaffected: both queries exclude null categories (isNotNull(transactions.categoryId)), so neither can produce an uncategorized row.

Verification

  • pnpm typecheck, pnpm lint clean
  • 4 new unit tests + 45 existing report/spending/transaction integration tests pass
  • Against the local dev DB, the default 3-month range holds 87 uncategorized expense transactions ($15,589.86) that the sheet was hiding

🤖 Generated with Claude Code

…nsactions

Clicking Uncategorized on the Spending tab opened an empty sheet reading
"No transactions found." Two bugs stacked on the same value:

1. DrillDownSheet fetched from an effect keyed on the filter's individual
   fields. With the sheet closed the filter is null, so every field reads
   undefined — and an uncategorized drill-down carried categoryId undefined
   too, making the open state field-for-field identical to the closed one.
   The effect never re-ran and rows stayed empty. Every real category has a
   non-undefined id, which is why only Uncategorized broke. Depend on the
   filter object itself instead; it is fresh state on each click.

2. The null categoryId was flattened to undefined twice on the way down
   (report-spending and the server action). getTransactions reads null as
   IS NULL but undefined as "no category filter", so once the fetch did fire
   it returned every transaction in the range rather than the uncategorized
   ones. Carry the null through.

The footer link had the same hole — it dropped the category param for null,
linking to all transactions. Extracted as drillDownTransactionsUrl, which
emits the category=uncategorized sentinel the transactions page already
parses, with unit tests over the three id states.

The chart's rolled-up "Other" slice also carries id null, so with the null
now meaningful it would have drilled into Uncategorized. It spans several
categories, which drill-down cannot express, so it is flagged synthetic and
inert on click; that flag also replaces the fragile id-and-name check used
to pick its neutral color.
@KenTaniguchi-R
KenTaniguchi-R merged commit 541966f into main Sep 2, 2026
4 of 5 checks passed
@KenTaniguchi-R
KenTaniguchi-R deleted the fix/uncategorized-drill-down branch September 2, 2026 02:40
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