Uses next-themes with system default. Toggle button in sidebar
switches between light/dark and persists to localStorage.
CSS variables for dark mode were already defined in globals.css.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Client side catches it immediately (case-insensitive match against
existing rules) and shows an inline error. API also rejects with 409
as the authoritative guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The search debounce useEffect had `push` in its deps. Since useRouter()
returns a new instance each render, `push` was recreated on every
navigation, re-triggering the effect which called push({ search })
after 400ms — always deleting the page param and snapping back to 1.
Replace the useEffect debounce with a ref-based timer in the handler
so the debounce only fires when the user actually types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
push() depended on searchParams, causing the search debounce effect to
re-fire on every page change and delete the page param. Store searchParams
in a ref so push() is stable and only the search value triggers it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass budget name as explicit children to SelectValue so the selected
label renders correctly. Also handle TRANSFER type display in the header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add TRANSFER to TransactionType enum; excluded from cash flow queries
- Add TransferRule model: description patterns that auto-mark transactions
as transfers on upload (takes priority over budget rules)
- Bulk action "Mark as transfer" in transaction table
- Transfer Rules button/dialog on transactions page for managing patterns
- Transfers shown with ⇄ prefix and muted color in transaction list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Category:
- Add categoryColumn to NormalizerConfig and NormalizedRow
- Map 'Category' column for Discover CC profile
- Write category to Transaction on upload
Budget rules:
- Add BudgetRule model (userId, budgetId, pattern)
- API: GET/POST /api/budget-rules, DELETE /api/budget-rules/:id
- Apply rules during upload (first case-insensitive match wins)
- Budgets page fetches and passes rules per budget
- BudgetCard 'Rules' menu item opens BudgetRulesDialog for add/delete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Base UI Select.Value renders the raw value prop (cuid) rather than
the item label. Pass the looked-up account name as children to
override it. Falls back to placeholder when no account matches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>