From 4a8f6557e69a4ba005d984bde36a2b7561223d32 Mon Sep 17 00:00:00 2001
From: Guillem Arias
Date: Thu, 14 May 2026 20:37:16 +0200
Subject: [PATCH] fix(goals/pledge-modal): helper text reacts to selected
account
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The helper paragraph above the amount field was painted off
`@goal.any_connected_account?` — a goal-level decision that fires
once at modal render and never updates. On a mixed-funding goal
(one connected + one manual account linked), the helper read the
"transfer" copy regardless of which account the user picked from
the dropdown, even though the saved pledge's `kind` is decided
per-account by `kind_for_account` in the controller. Stale UI vs.
correct data.
Render both helper paragraphs hidden, then toggle the right one
visible via Stimulus on `change->goal-pledge-preview#accountChanged`.
The select renders its `` tags with `data-manual="true|false"`
from `Account#manual?`; the controller reads that attribute off
the currently-selected option and flips `hidden` on the two helper
targets.
`connect()` also calls `accountChanged()` so the initial paint
matches the preselected account from `?account_id=…` (the catch-up
callout's amount-specific deep link).
Switch from `options_from_collection_for_select` to
`options_for_select` with the `[label, value, { data: { manual: } }]`
3-tuple form — Rails supports per-option data attributes there but
not on the collection helper.
---
.../goal_pledge_preview_controller.js | 24 +++++++++++++-
app/views/goal_pledges/new.html.erb | 31 ++++++++++++++-----
2 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/app/javascript/controllers/goal_pledge_preview_controller.js b/app/javascript/controllers/goal_pledge_preview_controller.js
index 8d7601aa6..535345f48 100644
--- a/app/javascript/controllers/goal_pledge_preview_controller.js
+++ b/app/javascript/controllers/goal_pledge_preview_controller.js
@@ -4,7 +4,13 @@ import { Controller } from "@hotwired/stimulus";
// target amount from values and updates a preview sentence each keystroke.
// Template strings come from ERB so the wording stays localized.
export default class extends Controller {
- static targets = ["amountInput", "preview"];
+ static targets = [
+ "amountInput",
+ "preview",
+ "accountSelect",
+ "helperConnected",
+ "helperManual",
+ ];
static values = {
currentBalance: Number,
targetAmount: Number,
@@ -16,6 +22,22 @@ export default class extends Controller {
connect() {
this.update();
+ this.accountChanged();
+ }
+
+ // Helper text reacts to the currently-selected account, not the goal as a
+ // whole. A mixed-funding goal (one connected account + one manual) used to
+ // paint the "connected" helper even if the user then picked the manual
+ // account from the dropdown — the saved pledge would be `kind: manual_save`
+ // (correct, per `kind_for_account` in the controller) but the helper read
+ // "transfer-style" copy until submission.
+ accountChanged() {
+ if (!this.hasAccountSelectTarget) return;
+ if (!this.hasHelperConnectedTarget || !this.hasHelperManualTarget) return;
+ const opt = this.accountSelectTarget.selectedOptions[0];
+ const isManual = opt?.dataset.manual === "true";
+ this.helperConnectedTarget.hidden = isManual;
+ this.helperManualTarget.hidden = !isManual;
}
update() {
diff --git a/app/views/goal_pledges/new.html.erb b/app/views/goal_pledges/new.html.erb
index ee0ab326a..a8c310efc 100644
--- a/app/views/goal_pledges/new.html.erb
+++ b/app/views/goal_pledges/new.html.erb
@@ -5,6 +5,12 @@
<%= render "shared/form_errors", model: @pledge %>
<% end %>
+ <%
+ account_options = @goal.linked_accounts.map do |a|
+ [ a.name, a.id, { data: { manual: a.manual?.to_s } } ]
+ end
+ %>
+
<%= styled_form_with model: @pledge,
url: goal_pledges_path(@goal),
class: "space-y-3",
@@ -17,12 +23,15 @@
goal_pledge_preview_template_nonzero_value: t(".preview_nonzero"),
goal_pledge_preview_template_reached_value: t(".preview_reached")
} do |f| %>
-
- <% if @goal.any_connected_account? %>
- <%= t(".helper_transfer") %>
- <% else %>
- <%= t(".helper_manual") %>
- <% end %>
+
+ <%= t(".helper_transfer") %>
+
+
+ <%= t(".helper_manual") %>
<%= f.money_field :amount,
@@ -39,8 +48,14 @@
data-goal-pledge-preview-target="preview">
<%= f.select :account_id,
- options_from_collection_for_select(@goal.linked_accounts, :id, :name, @pledge.account_id),
- { label: t(".account_label") } %>
+ options_for_select(account_options, @pledge.account_id),
+ { label: t(".account_label") },
+ {
+ data: {
+ goal_pledge_preview_target: "accountSelect",
+ action: "change->goal-pledge-preview#accountChanged"
+ }
+ } %>
<%= f.submit t(".submit") %>