fire-planner/fire_planner/api
Viktor Barzin 70101c836c
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fire-planner: What-If gains the chart-first scenario editor
The Plan-tab editors (interactive Gantt for life events, flex spending
rules) are now available in What-If too — with local state instead of
DB persistence so users can tweak before committing to a scenario.

Architecture refactor:

- EventGantt is now a controlled component. The `scenarioId` prop +
  internal useMutation/useQueryClient hooks went away; the component
  takes a `persister: { create, patch, delete }` prop and delegates
  every mutation through it. Plan tab wires it to lifeEventsApi +
  cache invalidation; What-If wires it to React local state with
  negative ids for new events.
- FlexRulesEditor is similarly controlled. Takes `rules + onChange`
  instead of a `scenario` object. Plan tab wraps it with the PATCH
  /scenarios/:id mutation; What-If wraps it with setFlexRules.

Backend:

- New stateless POST /scenarios/spending-profile-preview endpoint
  takes base_spending_gbp + horizon + life_events + flex_rules in the
  body and returns the same SpendingProfileResponse shape as the
  read-only /scenarios/{id}/spending-profile endpoint. Used by
  What-If to render the stacked-area chart against unsaved events.
- SpendingProfileResponse.scenario_id loosened to int | None to
  support the preview variant.

Frontend:

- What-If page gains `events` + `flexRules` local state, an
  EventGantt + FlexRulesEditor wired through them, and a Visx
  spending-profile chart fed by /spending-profile-preview.
- Sim auto-refresh: a 600ms debounced effect re-fires /simulate
  whenever the form / events / flex rules change. Manual "Run
  simulation" button stays as an immediate trigger.
- "Save as scenario" still works — preserves the scenario params but
  not yet the life events / flex rules (a Wave-3 follow-up could
  POST them after the scenario is created).

247 pytest pass; mypy + ruff + frontend typecheck/test/build all clean.
2026-05-12 19:35:28 +00:00
..
__init__.py api: expand FastAPI surface for scenarios, networth, life-events, goals, simulate 2026-05-09 21:48:36 +00:00
auth.py api: expand FastAPI surface for scenarios, networth, life-events, goals, simulate 2026-05-09 21:48:36 +00:00
cashflow.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00
dependencies.py api: expand FastAPI surface for scenarios, networth, life-events, goals, simulate 2026-05-09 21:48:36 +00:00
goals.py api: drop bearer-token gate from /api/* CRUD + simulate 2026-05-09 23:56:37 +00:00
income_streams.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00
life_events.py api: drop bearer-token gate from /api/* CRUD + simulate 2026-05-09 23:56:37 +00:00
networth.py api: expand FastAPI surface for scenarios, networth, life-events, goals, simulate 2026-05-09 21:48:36 +00:00
progress.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00
scenarios.py fire-planner: UX review pass 1 — fix sidebar/route/PATCH/badges issues 2026-05-10 17:17:55 +00:00
schemas.py fire-planner: What-If gains the chart-first scenario editor 2026-05-12 19:35:28 +00:00
simulate.py fire-planner: life-event spending bumps now reflected in fan + auto- 2026-05-10 19:17:57 +00:00
spending.py whatif: live data refresh, inflation-adjusted spending, legend fix 2026-05-10 11:27:22 +00:00
spending_profile.py fire-planner: What-If gains the chart-first scenario editor 2026-05-12 19:35:28 +00:00
year_stats.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00