Private
Public Access
1
0
Commit Graph

113 Commits

Author SHA1 Message Date
Catherine Renelle 4808e5d67d Add provider sub-tabs and collapsible panels to Bank Connections settings
Split Bank Connections into SimpleFIN/Plaid sub-tabs with institution
cards as collapsible expansion panels. Persist active tab, provider
sub-tab, and panel expand/collapse state to localStorage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:05:24 -05:00
Catherine Renelle db878c109c Update existing SimpleFIN connections on re-connect instead of creating duplicates
When a user re-connects SimpleFIN (e.g. after a key change), match existing
connections by institution name and update the access token in-place rather
than always creating new SyncConnection records. New linked accounts are
added if discovered, existing mappings are preserved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 15:59:23 -05:00
Catherine Renelle 8b57aee672 Set ASPNETCORE_ENVIRONMENT to Development in docker-compose and pass through encryption key
The Docker container defaults to Production when no environment is set,
which triggers the new startup secret validation. Default to Development
for docker-compose (local dev) and expose BANKSYNC_ENCRYPTION_KEY env var
so production deployments can override it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:36:40 -05:00
Catherine Renelle 2f389ab8e9 Security hardening: fix IDOR vulnerabilities, add rate limiting, SSRF protection, and encryption upgrades
- Fix ReconciliationController and BankSync LinkAccount/UpdateLinkedAccount IDOR (verify account ownership)
- Add ASP.NET Core rate limiting: strict on auth endpoints, global on all API routes
- Harden SSRF validation on Ollama URL (IPv6-mapped bypass, link-local, 0.0.0.0 blocking)
- Upgrade encryption from AES-CBC to AES-GCM with HKDF key derivation (backward-compatible decrypt)
- Add startup validation that rejects default JWT/encryption keys in Production
- Add security headers (X-Frame-Options, CSP, HSTS, Cache-Control, nosniff) to API and nginx
- Mask account numbers in API responses (show last 4 digits only)
- Add password strength validation (min 8 chars) and BCrypt work factor 12
- Hash refresh tokens (SHA-256) before database storage
- Set JWT ClockSkew to zero for immediate expiry enforcement
- Add file upload size limit (10 MB) and filename sanitization
- Cap transaction search PageSize to 200
- Fix FileWatcherService cross-user account matching in multi-user deployments
- Sanitize console.error calls to prevent token leakage in browser logs
- Parameterize raw SQL in SimpleFinConnectionMigration
- Make AuthService.GenerateAuthResponse fully async

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 12:53:20 -05:00
Catherine Renelle fd14473125 Hide Classify Uncategorized button when AI categorization is disabled
Also checks that Ollama URL and model are configured before showing
the button, consistent with the settings page behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:04:42 -05:00
Catherine Renelle ac08e21e55 Default Settings page to Profile tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:56:48 -05:00
Catherine Renelle beb0ef521b Restructure AI settings into 3 cards and add AI Chat enable/disable toggle
Splits the AI tab into Configuration, Categorization, and Chat sections
with independent feature toggles. Both toggles are disabled when Ollama
URL or model is not configured. ChatPanel FAB is gated on isChatEnabled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:54:51 -05:00
Catherine Renelle 7f15cd11e3 Add investment holdings CRUD (create, edit, delete)
Add POST/PUT/DELETE endpoints to InvestmentsController with find-or-create
Security by normalized symbol, price upsert helper, and ownership checks.
Frontend gets add/edit dialog with per-share cost basis input, delete
confirmation, actions column on holdings table, and success/error snackbar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:31:44 -05:00
Catherine Renelle 24839a3a21 Add 7-day sync overlap buffer and improve empty investments state
SimpleFIN sync now looks back 7 days from last sync to catch
retroactively posted transactions. Investments page shows account
balance instead of $0 summary cards when no holdings exist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:16:28 -05:00
Catherine Renelle e9ad7c0248 Group accounts by Assets and Liabilities with section totals
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:52:53 -05:00
Catherine Renelle 5537945276 Organize nav menu into grouped sections with dividers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:50:39 -05:00
Catherine Renelle 81d7337ce8 Make account cards equal height with bottom-aligned actions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:47:32 -05:00
Catherine Renelle ab335dac55 Add warm neutral background to light theme
Set light theme background to warm cream (#F5F3EF) with white cards,
softer text (#2C2C2C), and a surface variant (#EDE9E3) to eliminate
the flat all-white appearance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:16:07 -05:00
Catherine Renelle e83cb582a7 Fix pie chart highlight by directly dimming non-hovered slices
Replace toggleDataPointSelection with direct SVG opacity manipulation
for reliable visual feedback when hovering legend or table rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:50:43 -05:00
Catherine Renelle 7a333a1705 Replace built-in chart legend with custom legend for reliable hover sync
ApexCharts legendMouseOver event is unreliable for donut charts. Use custom
HTML legend items with direct mouseenter/mouseleave handlers that sync
highlighting between legend, chart slices, and the category table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:30:22 -05:00
Catherine Renelle 1df5283188 Fix legend hover not highlighting table row
legendMouseOver receives (chartContext, config), not three parameters.
The extra parameter was shifting config to undefined.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:23:30 -05:00
Catherine Renelle fcffdce6d8 Fix payee action buttons layout and chart tooltip hover highlight
Place edit and delete buttons side by side in payees table using flex.
Debounce chart highlight clear to persist during tooltip hover transitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:10:17 -05:00
Catherine Renelle 94049c08a8 Highlight table row when hovering spending chart legend items
Add legendMouseOver/legendMouseOut events to sync legend hover
with the category table highlighting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 19:57:54 -05:00
Catherine Renelle f3211b7ee7 Add global multi-account filter to all reports with chart hover interaction
Move account selector to date range bar and apply filtering to all four
report types. Add bidirectional hover highlighting between spending donut
chart and category table. Fix donut data label readability with white text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 18:15:46 -05:00
Catherine Renelle 63a58b10c0 Default reports to This Month, add Last Month preset, fix chart theme colors
Change default date range from Last 3 Months to This Month. Add Last Month
option. Apply theme-aware colors to chart axis labels, legends, and data
labels so they're readable in both light and dark modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:26:36 -05:00
Catherine Renelle dc4fd692ba Limit dashboard spending to top 10 categories and fix nav double-highlight
Show top 10 categories in spending breakdown with label indicating the limit.
Fix account-scoped transactions highlighting both Accounts and Transactions
nav items by excluding sub-route match when account-transactions is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:59:27 -05:00
Catherine Renelle d5c90a9197 Include uncategorized spending and show all categories on dashboard
Remove CategoryId.HasValue filter so uncategorized transactions appear
in the spending breakdown. Remove Take(N) limit to show all categories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:42:04 -05:00
Catherine Renelle e2b69b41f4 Show current month's payment in amortization schedule
Filter from start of current month instead of today so the current
month's entry is always visible even mid-month.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:35:53 -05:00
Catherine Renelle 0392fc4690 Use daily per-diem for payoff calculation and rename dashboard bills section
Payoff quotes always use daily per-diem interest (rate/365 × days) regardless
of loan type. Rename "Upcoming Bills" to "Upcoming Transactions" on dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:32:01 -05:00
Catherine Renelle f148b35ab2 Add payoff amount, fix dialog overlay and nav highlight for sub-routes
- Show estimated payoff amount (balance + accrued interest) in loan summary
- Display loan balances as absolute values in red
- Fix dialog overlay blocking UI after edit by closing in finally with nextTick
- Highlight parent nav item when viewing sub-routes (e.g. /loans/:id)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:12:50 -05:00
Catherine Renelle d28faa6838 Hide past payments in amortization schedule by default
Add toggle button to show/hide past payments. Future payments are shown
by default since past entries are rarely useful for day-to-day viewing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:00:15 -05:00
Catherine Renelle 1b06894383 Use daily interest accrual for lines of credit and auto loans, fix hasLoanDetail on account list
Lines of credit and auto loans use daily accrual (rate/365 × days) matching
actual bank calculations. Mortgages and personal loans keep monthly compounding.
Include LoanDetail in GetAllAsync so the loan cards correctly show status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:47:14 -05:00
Catherine Renelle 47010e5f55 Use standard monthly compounding for all loan types
Daily accrual made HELOC estimates worse due to front-loaded 31-day
months. Revert to rate/12 for all types - it's the standard amortization
method and works well for mortgages and personal loans. For HELOCs and
auto loans, differences from bank statements are typically due to
variable rates, extra payments, or daily billing conventions that a
fixed amortization schedule cannot capture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:10:18 -05:00
Catherine Renelle dd2cdbf815 Use daily interest accrual for HELOC and auto loan amortization
Mortgages and personal loans use monthly compounding (rate/12), but
HELOCs and auto loans accrue interest daily (rate/365 * actual days).
Select the calculation method based on account type and update the
disclaimer to reflect which method is being used.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 18:41:32 -05:00
Catherine Renelle 98104c0c0e Add estimation disclaimer to loan amortization view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:36:46 -05:00
Catherine Renelle 428e18bb41 Fix date display timezone shift and revert to standard monthly interest
formatDate was parsing UTC timestamps in local time, shifting dates
back a day in western timezones. Strip the time portion before parsing
so dates display correctly.

Revert interest calculation from daily accrual back to standard monthly
compounding (rate/12) with per-payment rounding. This matches how
mortgage servicers calculate interest and produces correct amortization
schedules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:26:55 -05:00
Catherine Renelle 0f3ad89c59 Fix loan edit concurrency error with ExecuteDeleteAsync
The change tracker-based approaches (RemoveRange, Clear) both caused
DbUpdateConcurrencyException when deleting amortization entries.
Use ExecuteDeleteAsync to delete directly in the database, bypassing
the change tracker entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:12:20 -05:00
Catherine Renelle 6ddecd7a99 Use first payment date and maturity date instead of origination date and term
Replace the term months field with a maturity date picker - users know
when their loan ends, not the month count. Compute termMonths from the
two dates. Rename "Origination Date" to "First Payment Date" and adjust
the amortization schedule so payment #1 falls on the entered date.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:06:21 -05:00
Catherine Renelle 7efeb4957b Fix concurrency exception when editing loan details
RemoveRange + collection replacement caused EF Core to attempt a
double-delete on amortization entries. Use Clear() on the tracked
collection instead, letting EF handle orphan deletion in a single pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:00:47 -05:00
Catherine Renelle b7ef9e36ce Fix loan interest calculation to use daily accrual instead of rate/12
The amortization schedule and payoff calculator were using rate/12 for
monthly interest, which assumes 30.42 days per period and overestimates
interest. Switch to daily accrual (rate/365 * actual days) to match how
lenders actually compute interest between payment dates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:52:50 -05:00
Catherine Renelle 08a0bcda3d Don't pre-populate original balance from current balance in loan defaults
The current account balance is not the origination balance, so defaulting
it into the Original Balance field is misleading. Leave it at 0 so users
enter the actual loan origination amount.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:39:23 -05:00
Catherine Renelle 2ae12770cf Fix date timezone shift in loan detail maturity date computation
Parse date string components directly and use Date.UTC() to avoid
local timezone interpretation that shifts dates by a day in western
timezones when using new Date() with date-only strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:33:46 -05:00
Catherine Renelle 3bb9c688ed Auto-populate loan details from Plaid liabilities and pre-fill setup form
Fetch loan data (mortgage/student) from Plaid's Liabilities API during sync
and auto-create LoanDetail records. For accounts without liabilities data,
pre-populate the setup form with balance, interest rate, and smart term
defaults. Also fixes maturity date computation in the frontend form.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:23:49 -05:00
Catherine Renelle a689681872 Normalize PayeeName on import and propagate payee changes to transactions
Use clean payee name instead of raw bank string when a payee match is found
during import. When payees are created/renamed or aliases added, automatically
update or link matching unlinked transactions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:14:14 -05:00
Catherine Renelle c91d6125a4 Preserve transaction status when editing instead of resetting to Uncleared
Adds a Status select to the add/edit transaction dialog that defaults to
the current value when editing, so cleared transactions stay cleared.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:22:49 -05:00
Catherine Renelle 8f4d4eaabb Exclude current category from parent options when editing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:46:32 -05:00
Catherine Renelle 0b4fa68236 Add edit, delete, and deactivate actions to categories view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:20:42 -05:00
Catherine Renelle b38865050c Include memo field in AI chat transaction summaries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:24:37 -05:00
Catherine Renelle 688dd333dc Add adjustable context size, custom chatbot name, and cat icon for AI chat
Settings > AI tab now includes context size slider (2048–131072) with VRAM
estimate and a chatbot name field. ChatService reads both dynamically instead
of hardcoding num_ctx=16384 and "Purrse AI". Chat panel FAB and empty state
use mdi-cat icon to match the rest of the app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:38:07 -05:00
Catherine Renelle 0ad8e472f7 Auto-reload transactions after AI makes changes
When the AI updates a category (or any tool result), the transactions
page now automatically reloads. ChatPanel triggers a refresh counter
in the usePageContext composable when the response includes a tool
result, and TransactionsView watches that counter to reload data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:07:09 -05:00
Catherine Renelle 401147009a Increase Ollama context window to 16k tokens
Ollama defaults to 2048 tokens which is far too small for page context
data + 15 tool definitions + conversation history. Added num_ctx=16384
via the options field on all Ollama requests. With Qwen at ~4GB and
7GB VRAM free this fits comfortably.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:25:24 -05:00
Catherine Renelle f5d6e9f98e Distinguish read vs write operations in AI system prompt
The AI was claiming to update categories without actually calling the
update tool — it answered from page context as if the change was made.
Restructured the system prompt to clearly separate read-only questions
(can answer from page data) from mutations (MUST call tools). The AI
is now told to never claim a change was made without calling the
appropriate tool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:53:07 -05:00
Catherine Renelle 8aeee44368 Add logging and retry-without-tools for empty AI responses
When the model returns empty content with page context present, the
15 tool definitions likely overwhelm the model's attention. Now on
empty response, the request is retried with tools stripped so the
model focuses on answering from the page data. Also added diagnostic
logging for page context receipt and empty response details.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 21:44:39 -05:00
Catherine Renelle 8a295c48dd Handle empty string AI responses as fallback
The null-coalescing operator only caught null, not empty/whitespace
responses. When Ollama returns empty content (common when the model
hesitates between tool calling and text), the empty string was saved
as the assistant message. Now uses IsNullOrWhiteSpace to catch both.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 21:26:32 -05:00
Catherine Renelle 8e342045eb Skip tool-use nudge when page context provides data
The nudge forced the AI to call a tool even when the answer was
already in the page context (e.g. Sarku transactions visible on the
Transactions page). This caused the model to pick an unrelated tool.
Now the nudge also checks for page context — if the current page has
data attached, the model is allowed to answer from it directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:23:10 -05:00