mirror of
https://github.com/we-promise/sure.git
synced 2026-05-30 15:59:02 +00:00
feat(retirement): PR3b what-if KPIs + live forecast Turbo Stream
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.
This commit is contained in:
33
app/javascript/controllers/retirement_what_if_controller.js
Normal file
33
app/javascript/controllers/retirement_what_if_controller.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
// Live "what-if": debounce input changes and POST the current plan inputs
|
||||
// to the forecast endpoint, which streams back the recomputed KPI cards
|
||||
// without persisting. Saving is a separate form submit (#update).
|
||||
export default class extends Controller {
|
||||
static targets = ["form"]
|
||||
static values = { url: String, debounce: { type: Number, default: 300 } }
|
||||
|
||||
preview() {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = setTimeout(() => this.fetchPreview(), this.debounceValue)
|
||||
}
|
||||
|
||||
async fetchPreview() {
|
||||
const response = await fetch(this.urlValue, {
|
||||
method: "PATCH",
|
||||
body: new FormData(this.formTarget),
|
||||
headers: {
|
||||
Accept: "text/vnd.turbo-stream.html",
|
||||
"X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content
|
||||
}
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
window.Turbo.renderStreamMessage(await response.text())
|
||||
}
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user