* infra(terraform): manage finance session secret via random_password
Replace the hand-rolled variable (with insecure hardcoded default) with a
random_password resource so Terraform auto-generates a 48-char secret and
owns the finance-api-secrets k8s Secret lifecycle.
To rotate: terraform taint random_password.finance_session_secret && terraform apply
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(finance): active sessions panel + account deletion with full data purge
Sessions panel (/account):
- AuthSession now stores IPAddress and Device (browser + OS hint)
populated from X-Forwarded-For / User-Agent on every login
- Lists all active sessions with device icon, IP, sign-in time
- Current session badge ("This device") — cannot be self-revoked
- DELETE /sessions/:id revokes any other session (user-scoped)
Account deletion (POST /account/delete):
- Password accounts require password confirmation
- OAuth accounts require typing email address to confirm
- deleteAllUserData purges all 12 finance collections + user record
in a single call: accounts, categories, transactions, trades,
ticker_mappings, goals, import_schedules, properties, loans,
permissions, households, sessions → then the user itself
- Clears session cookie and redirects to login with success message
Infrastructure:
- findAuthUserByID added to store + storeIface
- getSessionsByUserID, deleteSessionForUser added to store + storeIface
- contains() added to template FuncMap
- accountTmpl registered; GET /account, POST /account/delete,
DELETE /sessions/:id routes wired
- 🔐 nav icon links to /account page
- Full EN + PT i18n coverage for all new strings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(finance): expand unit test coverage from ~55% to 64.7%
- Add handler_coverage_test.go (~3300 lines) covering auth flows,
org request lifecycle, CSV bank import, property/loan views,
fiscal year operations, session management, and cross-handler
consistency (values shown on one page match actions on others)
- Add handler_org_test.go (~1800 lines) covering the full org
handler surface: teams, members, invites, events, budget lines,
tx requests (all status transitions), ledger, analysis, and reports
- Extend handler_test.go mockStore with: properties/loans slice fields,
authUsers map with session-aware lookup, household field, org maps,
and updateFiscalYearStatusErr for error-path testing
- Fix nav bar: Business and Account links now show active state and
use i18n keys (removes hardcoded emoji); add account key to en/pt locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Same TOML duplicate-key pattern as nav.analysis: the scalar
settings = "..." in [nav] blocked parsing of the [nav.settings]
sub-table. Removed the scalar; nav dropdown labels now reference
the existing nav.drawer.*_label keys which hold the same strings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove conflicting `analysis = "..."` scalar from [nav] in both
en.toml and pt.toml; it shadowed the [nav.analysis] sub-table,
causing the TOML parser to reject the entire file at startup
- Update nav analysis dropdown label to reuse nav.drawer.analysis_label
- Add lang field to Translator and expose T.Lang() method so base.html
can highlight the active language in the switcher without requiring
a Lang field on every page data struct
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a full translation layer (English + European Portuguese) using
BurntSushi/toml with go:embed. Locale detection reads the lang cookie,
falls back to Accept-Language, then defaults to "en". A language switcher
in the nav writes the cookie and redirects back. All 20 personal finance
templates now use {{.T.Get "key"}} for every UI string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(property): Layer 3 — Dream House Simulator
Add /dream page with a four-phase simulation engine:
Phase 1 — Save the down payment (uses current property equity)
Phase 2 — Construction period (both loans running simultaneously)
Phase 3 — Sell current house, apply proceeds to construction loan
Phase 4 — Final state: just the construction loan remaining
Inputs: dream cost, down payment %, construction loan rate/term,
build duration, monthly savings, expected sale price. All pre-filled
from existing property/loan data when available.
Output: per-phase timeline cards, monthly cost bar chart, total
interest, final payoff date, and a key levers section.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(plan): rename Dream House to Goal Planner at /plan
- Route /dream → /plan
- Nav label "Dream House" → "Goal Planner"
- Template dream.html → plan.html
- All user-facing labels generalised (construction loan → new loan,
build duration → acquisition/build period, current property →
current asset, dream house cost → new goal cost, etc.)
- Empty state updated with generic copy and 🎯 icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(goals): merge Goal Planner into /goals as a second tab
- /goals now has two tabs: "Committed goals" and "Goal Planner"
- Goal creation only happens from the Planner tab (simulate first,
then "Save as goal" → creates an uncommitted goal)
- Commitment, deadline adjustment, and deletion stay on the Goals tab
- Off-track goals show an "Adjust deadline →" button that pushes the
deadline to the realistic date based on current savings rate
- /plan and /dream both redirect to /goals?tab=planner (301)
- "Goal Planner" nav link removed; plan.html kept for redirect compat
- GoalsData gains Tab, PlanProperties, PlanLoans, HasPlanResult,
PlanResult, PlanForm fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(goals): type-driven planner — Save for a purchase vs Sell & upgrade
Goal Planner tab now opens with two goal type cards:
🛒 Save for a purchase — name, target, monthly savings, optional
deadline. Shows time-to-reach at current rate, monthly needed
to hit the deadline, and a feasibility banner.
🔄 Sell & upgrade — the full four-phase transition simulator
(existing asset + loan → acquire new → sell old → payoff).
Each type has its own focused form and result section. Selecting a
type highlights the card and loads the matching form. Results include
a "Save as goal" action that drops an uncommitted goal into the
Goals tab.
Also adds runPurchaseSim() and PurchaseSimResult model.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces properties and loans as first-class financial entities:
- models_property.go: Property, Loan, LoanView, PropertyView, PropertyData
- store_property.go: full CRUD for finance_properties + finance_loans collections
- handler_property.go: GET/POST /property with add/edit/delete for both entities;
amortization helpers (EMI, remaining months, total interest)
- templates/property.html: summary equity cards, property cards with equity bar
and linked loan details, standalone loan cards with payoff progress
- base.html: "Property" nav link added to desktop and mobile drawer
- storeIface + mockStore updated with 10 new property/loan methods
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- New animated homepage at / with 3D card tilt, particle canvas,
floating ring decorations, and gradient title shimmer
- Personal finance pages move to /dashboard (base.html shows only
personal nav + a subtle Business link)
- Business/org inner pages use base_org.html with a purple theme,
org breadcrumb, Year/Team dropdowns, and a ← Hub back link
- org home/teams/members/invite/events/requests/ledger/analysis/report
all switched to renderOrg() + base_org.html
- Route strings updated to org-home, org-teams, org-events, etc.
so active nav highlighting works correctly in the business shell
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Form inputs:
- Add a catch-all CSS rule targeting all text-type inputs, selects, and
textareas not already covered by .form-group or .form-input — sets
background:--bg2, color:--text, focus ring. Fixes white-box appearance
in transactions, goals, settings, portfolio, import, people, and tax
templates without touching any HTML.
Team avatars:
- OrgTeam.Avatar string (emoji) — persisted in MongoDB, defaults to 👥
- OrgTeamCreate handler reads "avatar" form field
- org_teams.html: emoji picker (30 options) in new-team modal; preview
updates live; selected emoji highlighted with accent border
- avatarEmojis() and teamAvatar() template functions registered in FuncMap
- Team badges in org_members, org_events, org_event_detail, org_report
all show the emoji inline with the team name
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Org templates use class="form-input" and class="form-label" which had no
CSS definition — browsers fell back to white-box defaults, clashing with
the dark teal theme.
- .form-input: dark bg (--bg2), teal border, accent focus ring + glow,
placeholder dimming, file input button styling
- .form-label: uppercase micro-label style (11px, --text3) matching the
existing card section headers throughout the app
- textarea.form-input: resize:vertical, min-height, line-height
- select option / .form-input option: dark background on dropdown items
- Keep .form-group label unchanged so existing non-org templates are unaffected
Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds multi-tenant organisation support inside the existing finance namespace.
Users can create organisations, invite others via a copy-paste token link,
and manage teams/members with RBAC (admin, finance, member, viewer).
Fiscal year lifecycle is gated: activation requires all planned events to
be approved first. All org data lives in `org_`-prefixed MongoDB collections.
New files:
- models_org.go — domain types (Org, OrgTeam, OrgMember, OrgInvite,
FiscalYear, OrgEvent, BudgetLine, EventComment,
TxRequest with full StatusLog audit trail, etc.)
- store_org.go — MongoDB store methods for all org collections
- handler_org.go — HTTP handlers + RegisterOrgRoutes(); join invite
route lives at /join/{token} to avoid ServeMux
conflict with /orgs/{slug}/... wildcard routes
- templates/org_*.html — list, create, home, teams, members, invite, join
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove erroneous /100 in currentValue calculation (portfolio values
were 100x too small, causing net worth card to show ~€0)
- Add User-Agent header to Yahoo Finance requests (avoids 429s)
- Wrap ES module chart body in if(total>0){} block (return at top
level of a module is a SyntaxError — chart was silently broken)
- Add mobile hamburger menu: full-screen drawer at ≤720px with
animated open/close, all nav links, scroll lock while open
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace indigo accent palette with deep black backgrounds and
cyan-teal accents (#00c9b8 dark, #00897b light). Borders, glows,
text muted tones and background gradients all updated to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Merge Sharing + Household → /people (tab switcher: sharing | household)
- Merge Accounts + Categories → /settings (tab switcher: accounts | categories)
- Add Analysis dropdown in nav: Reports, Projections, Tax, Net Worth, What If
- Add Settings dropdown in nav: Accounts & Categories, Import CSV, Import Guide
- Legacy GET /sharing, /household, /accounts, /categories redirect 301 to new URLs
- Remove Import and Import Guide as standalone nav links
- New People handler consolidates all people-related mutations (_action field)
- New Settings handler renders both account and category lists in one page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto Import page replaced with a static Import Guide: step-by-step
instructions for CGD/Trade Republic/generic CSV export, duplicate
detection explanation, and a curl example for headless automation
- Removed POST /auto-import and DELETE /auto-import/{id} routes and
their handler logic; schedule CRUD was misleading since no bank
exposes an unauthenticated CSV endpoint
- Nav label changed from "Auto Import" to "Import Guide"
- Transactions page now renders an amber banner when redirected with
?notice=all_duplicates (every row in the uploaded file was skipped)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tax Summary (/tax): annual gross income from Income transactions,
FIFO capital gains/losses from trades, expenses by category, CSV export
- Household Mode (/household): link a partner account, combined
income/expense/disposable view for current month, shared goals list
- Auto Import (/auto-import): schedule recurring CSV imports with
account, format and optional source URL; delete schedules; webhook docs
- New store methods for households and import_schedules collections
- storeIface extended; mockStore updated; all tests still pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds /simulator page with live sliders for income change, one-off
expenses, and fixed cost shifts. Goal timelines update in real time
showing months gained/lost. Includes a savings rate bar chart from
historical monthly data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds /networth page with hero total, cash/portfolio breakdown cards,
and a month-by-month historical chart. Also shows net worth as a
value card on the dashboard with a link to the full breakdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Goals page where users can plan financial goals before committing
to them. Each goal shows:
- Required monthly contribution to hit the deadline
- Months remaining vs months at current savings rate
- Disposable income impact (what's left after the contribution)
- Feasibility banner (green if on track, red with month delta if not)
- Progress bar once savings are tracked
Goal types: one-off purchase, deposit/down-payment, emergency fund,
recurring investment — each with a description hint in the creation modal.
Data: Goal model + store CRUD in finance_goals collection.
Nav: Goals tab added between Portfolio and Sharing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UI — full dark/light theme system:
- CSS custom-property token system (--bg, --surface, --accent, --green, --red, etc.)
with complete light-mode overrides via [data-theme="light"]
- Sticky frosted-glass nav with animated brand icon, theme toggle persisted to localStorage,
respects prefers-color-scheme on first visit
- Cards with layered shadows, glass backdrop-filter, shimmer accent stripe on value cards
- Glowing category color dots, colored P&L badges, budget bars with glow effect
- All Chart.js instances use CSS-variable-aware grid/text colours
- Scroll-reveal animations and animated money counters on every KPI card
- 3-D donut portfolio chart recoloured to match palette; hover lifts the hovered slice
- Accounts page shows type emoji icons; delete removes row in-place
- Sharing page search dropdown themed with var() colours
- Import preview: colour-coded left border on category select driven by category colour
- Projections: second KPI card (monthly avg) + pace bars per category
Seed data (seed.go):
- SeedAdmin() runs in a goroutine at startup; idempotent (skips if transactions exist)
- Resolves admin user ID via internal users service GET /admin/users?search=<email>
(SEED_USER_EMAIL env var, defaults to admin@homelab.local)
- Seeds 4 accounts (CGD Checking, CGD Savings, Visa Credit, Trade Republic)
- Seeds 14 categories with colours and monthly budgets
- Seeds ~65 realistic Portuguese household transactions spread across 6 months
- Seeds 7 ETF buy trades across VWCE, SXR8 (S&P 500), EUNL (MSCI World)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>