The link of the "categorize by hand" step carries `guided`, which the
Transactions page reads through `readOnboardingNavState` and turns into
start filters: `useTransactions(initialFilters?)` puts them over the
defaults through `initTransactionsState`, at mount only, so the card
consuming the navigation state changes nothing. The guided entry lists
the uncategorized transactions whatever their date (the default filter
starts on January 1st, and in January the sample statements fall in the
previous year). When its first page comes back empty, the reducer drops
"uncategorized only" once: a new filters object, so the existing filter
effect reads again, and the loading placeholder meanwhile. The first
page and any filter the user sets disarm the fallback, so it never loops
and never drops a filter behind the user's back.
`transactionsCardModel(filters, checklist)` says which sentence matches
the list, names the filter by its own label, points at the first row's
category, and says "Done." once the step is checked, which the card
needs to offer the Budget step or the end of the path. The label loses
its capital through an i18next `lowercase` formatter used by the
sentence itself, `{{filter, lowercase}}`.
TransactionTable wraps every row's category in the same element and
puts the ring on the first one only, so the ring coming or going never
remounts the combobox; CategoryCombobox is unchanged. CARD_PAGES gets
/transactions, and the page renders the card first and highlights its
target only while the card shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A profile without any transaction now gets an "import first" card with a
button to the import page on both pages, in place of the filter bar, the
table and the pagination (Transactions) and of the filter panel and the
grid (Budget). Title, page help and template actions stay.
The signal is a profile-wide `totalInDb` count loaded once at mount via
`getTransactionCount()`, not `rows.length`: the default Transactions
filter is "since January 1st", so an empty page cannot tell an empty
profile from a year without transactions. `null` (not loaded yet) keeps
the regular layout, and so does a loading Transactions page.
`transactionsReducer` / `initialTransactionsState` and `budgetReducer` /
`initialBudgetState` are exported with a pure `showsEmptyState` selector
per hook, so the reducer tests pin that the count never touches filters,
sort or page and that 0 then N > 0 brings the table back. The existing
"no transactions found" / "no categories found" rows are unchanged.
Resolves#353
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Allow users to split a transaction across multiple categories directly
from the transactions table. Split children are hidden from the list
and automatically included in dashboard, report, and budget aggregates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add period buttons (This month, 3 months, 6 months, This year, All)
above the transaction filters. "This year" is selected by default so
the page no longer shows all transactions since the beginning of time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tag icon appears next to the category combobox when a category is
assigned. Clicking it expands an inline row pre-filled with the
transaction description, allowing users to add keyword rules without
leaving the Transactions page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace substring matching (.includes) with \b word-boundary regex so
keywords like "Pay" no longer match "Payment". Add an auto-categorize
button on the transactions page that re-runs keyword matching on
uncategorized transactions and displays the result count.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add paginated transaction list with search, category/source/date filters,
sortable columns, inline category dropdown, expandable notes, and summary stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>