23 Commits

Author SHA1 Message Date
Gonçalo Rodrigues
541a1c3556 feat: animated homepage + split personal/business navigation (#25)
- 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>
2026-06-14 17:02:37 +01:00
Gonçalo Rodrigues
1fce3b36aa feat: add checkable goals list to org events (#24)
Goals are stored as EventGoal items embedded in the event document.
During active fiscal years, members can check/uncheck goals inline.
Goals can be added and deleted while the event is not yet approved.

Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 16:15:55 +01:00
Gonçalo Rodrigues
26a7236494 style: global dark form inputs + team emoji avatars (#23)
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>
2026-06-14 16:08:29 +01:00
Gonçalo Rodrigues
0f58a51c6d feat(finance): org Phases 2-5 — events, requests, ledger, analysis, report (#20)
Phase 2 — Event planning:
- Create/edit/delete/submit events per fiscal year
- Budget lines (income + expense) with planned amounts
- Admin review flow: approve / reject / comment
- Post-mortem feedback comments on closed years

Phase 3 — Transaction requests:
- 5 types: reimbursement, purchase_order, cash_advance, income, budget_transfer
- Full status machine with append-only StatusLog audit trail
- PO delivery sub-form (actual vendor, amount, invoice note)
- Cash advance settlement sub-form (spent + returned)
- Ledger view per fiscal year with income/expense summary
- Bank CSV import: parse → preview → confirm → ledger entries

Phase 4 — Plan vs actual analysis:
- Variance table by event (planned vs actual income/expense)
- Variance table by team

Phase 5 — Year-end report:
- Executive summary with net variance
- Per-event section: budget lines + team feedback comments
- Feedback form available on closed fiscal years

Supporting changes:
- New store methods: getLedgerEntries, createLedgerEntry, updateLedgerEntry,
  getAttachments, createAttachment
- New template funcs: dateInput, statusColor, varColor
- parseEuroAmount + parseBankCSV helpers in handler_org.go

Co-authored-by: Gonçalo Rodrigues <guga@Goncalos-MacBook-Pro.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 12:58:47 +01:00
Gonçalo Rodrigues
6ed848a001 feat(finance): org management scaffold — Phase 1
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>
2026-06-14 12:43:48 +01:00
Gonçalo Rodrigues
6712c36081 feat: user-editable ISIN→ticker mappings for unrecognised holdings
When a holding has no price (ISIN not in the built-in map and Yahoo
rejects the raw ISIN), the portfolio page now shows an amber banner
listing each missing ISIN with an inline text input and a "Look up"
link to Yahoo Finance symbol search.

Submitting the form POSTs to /portfolio/ticker which upserts the mapping
into a finance_ticker_mappings collection keyed by (user_id, ISIN).
On the next page load custom mappings are resolved first, before the
hardcoded isinToTicker table, so user overrides always win.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:05:36 +01:00
Gonçalo Rodrigues
437fb5a2df refactor: consolidate nav from 15 items to 7
- 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>
2026-06-13 18:33:15 +01:00
Gonçalo Rodrigues
7f7e696b4d refactor: retire Auto Import schedules, add Import Guide + duplicate notice
- 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>
2026-06-13 18:20:48 +01:00
Gonçalo Rodrigues
2170457528 feat: duplicate detection on CSV import
Compute a sha256 fingerprint (date|description|amount|account_id, first
16 hex chars) for every CSV row and store it as bank_ref. At preview
time, existing fingerprints are fetched and matching rows are shown
greyed out with a "duplicate" label. At confirm time, those rows are
silently skipped — only truly new transactions are inserted.

If every row is a duplicate the user is redirected with ?notice=all_duplicates
instead of inserting an empty batch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:15:23 +01:00
Gonçalo Rodrigues
1c2bac1d5f feat: implement Tax Summary, Household Mode, and Auto Import
- 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>
2026-06-13 17:28:22 +01:00
Gonçalo Rodrigues
9dfc95cd32 test: add unit tests — 70.3% coverage
Extracted storeIface from Handler so store can be mocked in tests.
Added handler_test.go with 98 test cases covering:
- All HTTP handlers (Dashboard, Transactions, Accounts, Categories, Goals,
  Portfolio, Reports, Projections, NetWorth, Simulator, Sharing, Import*)
- Auth middleware (authMW, ownerOrViewerMW)
- Pure helpers (monthsBetween, parseFloat, sortStrings, appendIfMissing)
- Error paths (bad JSON, missing fields, store errors, bad CSV)
- Alert logic (budget exceeded, goal miss, spend pace)
- Template funcmap exercised via rich render scenarios

Also added tickerStore.resolve tests to portfolio_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:18:49 +01:00
Gonçalo Rodrigues
995c6d89d6 feat: phase 6 — dashboard alerts and nudges
Adds server-computed alert banners to the dashboard:
- Budget exceeded (red): when a category spend is over 100% of budget
- Budget pace warning (amber): 80%+ of budget used but <80% of month elapsed
- Goal deadline risk (amber): avg savings rate too low to hit a goal on time
- Spend pace warning (amber): disposable spending is 20%+ ahead of month progress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:03:12 +01:00
Gonçalo Rodrigues
39282ff550 feat: phase 5 — what-if simulator + savings rate history
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>
2026-06-13 16:57:14 +01:00
Gonçalo Rodrigues
42f5b0df4d feat: phase 4 — net worth page + dashboard card
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>
2026-06-13 16:52:57 +01:00
Gonçalo Rodrigues
bfd5f62a7a feat: show committed goals in fixed costs panel
Committed goals now appear in the Fixed Costs panel on the dashboard
with a "committed goal" label, and the total line uses TotalCommittedCents
(fixed costs + goal contributions) instead of total monthly expenses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:46:55 +01:00
Gonçalo Rodrigues
3b041267ad feat: phase 3 — goals commit + plan
- Commit/uncommit button on each goal card
- Committed goals are deducted from disposable income on the dashboard
  (Available to spend now reflects reserved goal contributions)
- Conflict detection: warning banner when committed goals exceed
  disposable income, showing the shortfall
- Goals summary bar: disposable before goals, reserved per month,
  free to spend after committed goals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:37:45 +01:00
Gonçalo Rodrigues
35156e001d fix: change GoalPlan int fields to int64 to match sub template func
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:33:15 +01:00
Gonçalo Rodrigues
be0c2bd89e feat: phase 2 — goals explore mode
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>
2026-06-13 16:20:46 +01:00
Gonçalo Rodrigues
5412dda2ac fix: dashboard investment display and budget health
- Exclude FixedCategories (Housing, Utilities, Subscriptions, Investments)
  from budget health panel — they are committed costs, not variable spend
- Portfolio card and stocks panel now degrade gracefully to cost basis
  when Yahoo Finance prices are unavailable, instead of showing €0
- Stocks panel shows "cost basis" label per holding when live prices
  are not available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:14:44 +01:00
Gonçalo Rodrigues
1d3aa764cb feat: phase 1 dashboard redesign — disposable income + smart panels
Replaces the old KPI/chart dashboard with a focused layout that
answers the three key questions immediately:

- Hero block: "Available to spend" = income − fixed costs − spent so far,
  with a progress bar showing % of disposable used vs month elapsed
- Bank math panel: detects recurring fixed expenses (Housing, Utilities,
  Subscriptions, Investments) from last 3 months and shows the minimum
  bank balance needed right now including a 2-week safety buffer
- Savings rate card with month-over-month delta
- Portfolio snapshot card with total value and P&L
- Stocks at a glance panel: per-holding value and P&L inline
- Budget health: thin bars per category, red when over limit
- Recent activity: last 5 transactions with category color dots

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:51:46 +01:00
Gonçalo Rodrigues
c255d7f523 fix(finance): portfolio prices and projections template
Portfolio:
- fetchPrices was passing ISINs directly to Yahoo Finance, which only
  accepts ticker symbols (e.g. VWCE.DE, not IE00B3RBWM25). Added a
  hardcoded isinToTicker map covering common European ETFs (Vanguard,
  iShares, Xtrackers, Amundi). fetchPricesByISIN now resolves each ISIN
  to its ticker before calling Yahoo and keys the result by ISIN so
  computeHoldings can look up prices correctly. Renamed holdingsByISIN
  to uniqueISINs to reflect what it actually returns.

Projections:
- Template had a missing <tr> and a broken pace-bar width expression
  that mixed float64/int64 in the div template function, causing a
  template execution error that rendered the page blank. Moved all
  math server-side: the handler now pre-computes []catProjection with
  MonthlyAvg, AnnualTotal, and PacePct already calculated. Template
  is now simple range + printf. Chart switched to horizontal bar for
  better readability of long category names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:47:03 +01:00
Gonçalo Rodrigues
452f97e6d9 feat(finance): improve UX across dashboard, transactions, reports and categories
- Fix sortStrings no-op bug that caused balance trend chart to display in random date order
- Fix reports table referencing wrong scope for row totals ($.Totals → .Totals per row)
- Add income vs expense split cards on dashboard alongside net figure
- Add budget progress bars on dashboard using existing category budget_cents data
- Color-coded category badges throughout using each category's configured color
- Replace window.prompt() category editor in transactions with inline dropdown
- Replace window.prompt() budget editor in categories with inline input field
- Add manual transaction entry modal (POST /api/transactions) so users don't need CSV for every entry
- Show account names instead of raw MongoDB IDs in the transaction list
- Add clampPct and isOver template helpers for budget bar rendering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 11:33:39 +01:00
Gonçalo Rodrigues
13b7149614 First Commit 2026-06-13 11:25:23 +01:00