The Saved control now shows the saved filter the current selection
matches (or is based on), with an unsaved-changes indicator. When the
filters are edited, the menu offers "Update" to save over the existing
filter instead of only creating a new one. Adds the PATCH endpoint and
an order-insensitive fingerprint to detect matches.
Persist named filter sets per user (model, migration, API CRUD) and a
Saved dropdown to load/delete them; available on both the transactions
and analysis screens via the shared filter bar.
Also refine the filter bar: icon-only Saved button, wider category/label
dropdowns, Filters/Saved pushed to the right, and clearer trigger text
("N categories/labels selected").
New top-level Analysis screen to drill into income and expenses across
category, month, account, and label dimensions. Reuses transaction
filters (category + labels surfaced inline, search hidden) and converts
all amounts to the user currency. Click a breakdown row to drill in.
Also make category and label filters combine with OR in the shared
transaction filter scope (affects the transactions screen too).
## Summary
Adds daily granularity support to the dashboard Net Worth Evolution
chart, mirroring the daily balance feature added for individual account
pages in #135.
## Why
The net worth chart only supported monthly granularity, making it
impossible to see short-term net worth fluctuations. Users who track
daily balances need a way to visualize day-over-day net worth changes
across all accounts.
## Screenshots
<img width="1263" height="714" alt="image"
src="https://github.com/user-attachments/assets/646e7e21-5c1b-42dc-9a20-5e9b6d5d72b4"
/>
<img width="1261" height="715" alt="image"
src="https://github.com/user-attachments/assets/b9fe5080-55f3-485a-a566-1d25d83518c7"
/>
## Summary
- Adds a **Monthly / Daily granularity toggle** to the account balance
evolution chart on account pages
- Daily view renders as an **area chart** (line with gradient fill)
showing the last 30 days, while monthly keeps the existing bar chart
- All 3 chart view modes (stacked, period-over-period,
period-over-period %) work in both granularities
- Chart view toggle **tooltips update dynamically** based on granularity
(e.g. "Month over month change" → "Day over day change")
- Backend fills gaps in sparse `account_balances` data by carrying
forward the last known balance day-by-day
- Includes 3 new Pest tests for the daily endpoint (data points,
gap-filling, forbidden access)
## Screenshot
<img width="1271" height="743" alt="image"
src="https://github.com/user-attachments/assets/cca7a7ad-bdcc-45d2-a5e1-47b1cb855d49"
/>
<img width="1274" height="755" alt="image"
src="https://github.com/user-attachments/assets/14e1f2d8-41cd-49cc-b0f4-307edb847af8"
/>
## Summary
- Add `GET /api/transactions?encrypted=true` endpoint for paginated
listing of encrypted transactions
- Add `PATCH /api/transactions/bulk` endpoint for batch-updating
decrypted transaction data (max 50 per request, bypasses model events)
- Add `useDecryptTransactions` hook that mirrors the existing account
name decryption flow: fetches encrypted transactions page-by-page,
decrypts `description`/`notes` client-side via Web Crypto API, sends
plaintext back and clears IVs
- Wire the hook into `AppSidebarLayout` so decryption runs automatically
when the user unlocks their encryption key
- Once all transactions (and accounts) are decrypted, the encryption key
button in the header disappears automatically
## Test plan
- [x] 11 Pest tests covering encrypted/plaintext filtering, pagination,
user scoping, bulk update, authorization, validation, nullable notes,
guest access, and no-model-events behavior
- [x] Manual: create encrypted transactions, unlock encryption key,
verify transactions get decrypted and the key button disappears
## Summary
- Store account names in plaintext instead of encrypting them
client-side. Encryption remains only for transaction descriptions and
notes.
- Existing encrypted account names are silently migrated when the user
unlocks their encryption key, via a new `useDecryptAccountNames` hook
that calls API endpoints to update each account.
- New `AccountName` component handles rendering both encrypted (legacy)
and plaintext accounts across the app.
## Test plan
- [x] Create a new account — name should be stored in plaintext
(`encrypted = false`, `name_iv = null`)
- [x] Unlock encryption key with existing encrypted accounts — they
should auto-migrate silently
- [x] After migration, verify accounts show `encrypted = false` in DB
and names are readable plaintext
- [x] Edit a migrated account — should work without encryption
- [x] Verify all account name displays work across dashboard, account
details, transactions, settings
This PR simplifies the IndexedDB synchronization mechanism by removing
individual sync controllers and services, and instead using Inertia.js
shared props to provide data globally across the application.
## Benefits
1. **Simplified Architecture**: Removed complex sync logic for
static/semi-static data (accounts, categories, banks, labels, automation
rules)
2. **Better Performance**: Data is now shared via Inertia props,
eliminating unnecessary API calls and IndexedDB operations
3. **Reduced Complexity**: Significantly reduced codebase size (~2000
lines removed)
4. **Better UX**: Data is immediately available on page load without
waiting for sync operations
5. **Maintainability**: Fewer moving parts means easier to maintain and
debug
## Migration Notes
- Transaction syncing still uses IndexedDB for offline support
- All other data (accounts, categories, banks, labels, automation rules)
is now fetched via Inertia shared props
- Components automatically receive updated data on navigation without
manual sync operations
Summary
- Add new Cashflow page with income/expense analytics, Sankey chart
visualization, and trend analysis
- Add Cashflow summary widget to the dashboard
- Implement feature flag system using Laravel Pennant to control feature
visibility per user
- Add artisan commands to enable/disable features: `php artisan
feature:enable/disable [feature] [email|all]`
## New Features
- **Cashflow Page**: Period-based income/expense breakdown with category
analysis
- **Sankey Chart**: Visual flow diagram showing money movement between
categories
- **Trend Chart**: 12-month historical cashflow visualization
- **Dashboard Widget**: Quick cashflow summary card
- **Feature Flags**: Cashflow feature hidden behind feature flag,
controllable per-user
## How to enable it
This feature it's under a feature flag until we are sure it's working
fine. If you want to test it, [send us a message on the general channel
in our discord](https://discord.gg/9UQWZECDDv). If you're self hosting
just run `php artisan feature:enable CashflowFeature {your_email}`.
## New Cashflow Page
<img width="1278" height="1185" alt="image"
src="https://github.com/user-attachments/assets/83469f24-9f96-4e5f-bef9-86c076d31243"
/>
<img width="1274" height="1035" alt="image"
src="https://github.com/user-attachments/assets/8949ff9d-c370-4fc4-81c6-268307c587a5"
/>
## New Dashboard Widget
<img width="1180" height="289" alt="image"
src="https://github.com/user-attachments/assets/ad984a66-9ffd-4079-85ce-0b2d28278d76"
/>
<img width="1179" height="286" alt="image"
src="https://github.com/user-attachments/assets/bff60286-8ea2-4e9c-8fa6-22755566648f"
/>
## Summary
Add a labeling system for transactions that differs from categories in
that labels are ephemeral and created on-the-fly when assigned (no
pre-creation required). A transaction can have multiple labels
(many-to-many relationship), and labels can be used for filtering in the
transaction table and as actions in automation rules.
<img width="1372" height="484" alt="image"
src="https://github.com/user-attachments/assets/fd342b11-dafb-44ed-b818-578e1ea856e6"
/>
<img width="1373" height="613" alt="image"
src="https://github.com/user-attachments/assets/aa5cfb8b-50b7-4101-a872-54904924f234"
/>
<img width="1035" height="594" alt="image"
src="https://github.com/user-attachments/assets/ffa946b2-b01f-496b-8cb8-ab55ab5b79ed"
/>
## Changes
### Backend (Laravel)
- Add `labels` table with user_id, name, color, and soft deletes
- Add `label_transaction` pivot table for transaction-label many-to-many
- Add `automation_rule_labels` pivot table for automation rule-label
many-to-many
- Create Label model with relationships to User, Transaction, and
AutomationRule
- Add LabelController for CRUD operations (returns JSON for on-the-fly
creation)
- Add LabelSyncController for frontend sync
- Add LabelPolicy for authorization
- Update TransactionController to handle bulk label updates
- Update AutomationRuleController to handle label actions
- Add validation for label_ids in transactions and automation rules
### Frontend (React/TypeScript)
- Add Label type and color utilities
- Add labels table to IndexedDB (Dexie version 6)
- Create label-sync service with findOrCreate functionality
- Create LabelCombobox component with multi-select and create-on-type
- Add labels column to transactions table
- Add labels filter to transaction filters
- Add bulk label assignment in bulk actions bar
- Add labels action in create automation rule dialog
- Update rule engine to include labels in evaluation results
## Testing
- 11 feature tests for label CRUD
- 3 tests for label sync
- All 241 feature tests pass
## Screenshots
N/A - UI changes can be reviewed in browser
## Summary
- Add `WithoutSsr` middleware to disable SSR per route
- Extract API endpoints to `routes/api.php` for better organization
- Apply `WithoutSsr` to dashboard, accounts, and transactions routes
## Changes
### New Files
- `app/Http/Middleware/WithoutSsr.php` - Middleware that disables SSR
for specific routes
- `routes/api.php` - Dedicated file for API endpoints
### Modified Files
- `bootstrap/app.php` - Register API routes
- `routes/web.php` - Remove API routes, apply WithoutSsr middleware
## Route Structure
- **routes/web.php** - Page routes only (Inertia pages)
- **routes/api.php** - All `/api/*` endpoints
- **routes/settings.php** - Settings routes (existing)