From 01118b858fc07cf042fc9bc385e08deeb7e43e37 Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Fri, 29 May 2026 12:38:24 +0200 Subject: [PATCH] feat(retirement): PR4d what-if slider rail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each lever (retire age / target spend / save per mo / real return) now pairs a numeric input with a range slider; retirement_what_if_controller mirrors the value across the pair (data-lever) and debounces the live forecast preview. Birth year stays a plain numeric input. (Skinned delete confirmations already render via Sure's global Turbo.config.forms.confirm → DS::Dialog override, so the PR2 turbo_confirm buttons are already styled — no change needed.) --- .../retirement_what_if_controller.js | 12 ++++++ app/views/retirement/show.html.erb | 40 ++++++++++++------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/app/javascript/controllers/retirement_what_if_controller.js b/app/javascript/controllers/retirement_what_if_controller.js index 116385bab..0f3e565f8 100644 --- a/app/javascript/controllers/retirement_what_if_controller.js +++ b/app/javascript/controllers/retirement_what_if_controller.js @@ -7,6 +7,18 @@ export default class extends Controller { static targets = ["form"] static values = { url: String, debounce: { type: Number, default: 300 } } + // Mirror a lever's value across its paired number + range inputs (matched + // by data-lever), then debounce a preview. + sync(event) { + const lever = event.target.dataset.lever + if (lever) { + this.element.querySelectorAll(`[data-lever="${lever}"]`).forEach((el) => { + if (el !== event.target) el.value = event.target.value + }) + } + this.preview() + } + preview() { clearTimeout(this.timer) this.timer = setTimeout(() => this.fetchPreview(), this.debounceValue) diff --git a/app/views/retirement/show.html.erb b/app/views/retirement/show.html.erb index 0e881ff6a..8de6fcdf6 100644 --- a/app/views/retirement/show.html.erb +++ b/app/views/retirement/show.html.erb @@ -48,21 +48,31 @@ <%= form_with url: retirement_path, method: :patch, data: { retirement_what_if_target: "form" }, class: "space-y-3" do |form| %> -
- <% [ - [ "birth_year", @plan.birth_year ], - [ "retire_age", @plan.retire_age ], - [ "target_spend", @plan.target_spend ], - [ "monthly_savings", @plan.monthly_savings ], - [ "real_return_pct", @plan.real_return_pct ] - ].each do |field, value| %> - + <% levers = { + "retire_age" => { min: 40, max: 75, step: 1 }, + "target_spend" => { min: 0, max: 20_000, step: 50 }, + "monthly_savings" => { min: 0, max: 20_000, step: 50 }, + "real_return_pct" => { min: 0, max: 10, step: 0.1 } + } %> +
+ + + <% levers.each do |field, cfg| %> + <% value = @plan.public_send(field) %> +
+ + <%= number_field_tag "retirement[#{field}]", value, step: cfg[:step], id: "rwi_#{field}", + autocomplete: "off", class: "form-field__input w-full", + data: { lever: field, action: "input->retirement-what-if#sync" } %> + <%= range_field_tag "rwi_#{field}_slider", value, min: cfg[:min], max: cfg[:max], step: cfg[:step], + class: "w-full accent-green-600 cursor-pointer", + data: { lever: field, action: "input->retirement-what-if#sync" } %> +
<% end %>
<%= form.submit t("retirement.what_if.save"), class: "text-sm font-medium text-primary underline cursor-pointer" %>