mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 23:39:03 +00:00
Surfaces the forecast on the page and makes the levers live. - KPI cards (_kpis): Freedom date, Coast FIRE, Money-lasts-to + terminal value, with a "set your birth year" prompt until a plan is projectable. Wrapped in #retirement_kpis for Turbo Stream replacement; money carries privacy-sensitive. - What-if form: birth_year / retire_age / target_spend / monthly_savings / real_return_pct. On input, retirement_what_if_controller debounces and POSTs the current values to PATCH /retirement/forecast, which recomputes against transient inputs and streams the KPI cards back WITHOUT persisting. "Save plan" submits to #update to persist retirement_params. - RetirementController gains #update (persist) and #forecast (transient recompute → turbo_stream). Both reuse merged_plan_params, which drops blank fields so a partial what-if doesn't clobber stored values. Tests: KPI section renders; update persists params; forecast streams #retirement_kpis without writing the slider value back. Rubocop + erb_lint + biome clean. PR4 replaces this minimal form with the designed slider rail + glide chart; the #forecast endpoint and the engine stay.
41 lines
1.4 KiB
Ruby
41 lines
1.4 KiB
Ruby
class RetirementController < ApplicationController
|
|
include RetirementScoped
|
|
|
|
def show
|
|
@pension_sources = @plan.pension_sources.order(:start_age)
|
|
@adjustments = @plan.adjustments.ordered
|
|
@statements = @plan.statements.chronological.reverse
|
|
@bucket_account_ids = @plan.retirement_bucket_entries.pluck(:account_id).to_set
|
|
@bucket_candidates = Current.family.accounts.visible.alphabetically
|
|
@breadcrumbs = [
|
|
[ t("breadcrumbs.home"), root_path ],
|
|
[ t("breadcrumbs.retirement"), nil ]
|
|
]
|
|
end
|
|
|
|
def update
|
|
if @plan.update(retirement_params: merged_plan_params)
|
|
redirect_to retirement_path, notice: t(".updated")
|
|
else
|
|
redirect_to retirement_path, alert: @plan.errors.full_messages.to_sentence
|
|
end
|
|
end
|
|
|
|
# Live what-if: recompute against transient inputs WITHOUT persisting, and
|
|
# stream the KPI cards back. The plan is only saved via #update.
|
|
def forecast
|
|
@plan.assign_attributes(retirement_params: merged_plan_params)
|
|
render turbo_stream: turbo_stream.replace(
|
|
"retirement_kpis", partial: "retirement/kpis", locals: { plan: @plan }
|
|
)
|
|
end
|
|
|
|
private
|
|
def merged_plan_params
|
|
raw = params.fetch(:retirement, {}).permit(
|
|
:birth_year, :retire_age, :target_spend, :monthly_savings, :real_return_pct
|
|
).to_h
|
|
(@plan.retirement_params || {}).merge(raw.reject { |_, v| v.to_s.strip.empty? })
|
|
end
|
|
end
|