fix(goals/pledge-modal): helper text reacts to selected account

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 `<option>` 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.
This commit is contained in:
Guillem Arias
2026-05-14 20:37:16 +02:00
parent 815fb9d8fa
commit 4a8f6557e6
2 changed files with 46 additions and 9 deletions

View File

@@ -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() {

View File

@@ -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| %>
<p class="text-sm text-secondary">
<% if @goal.any_connected_account? %>
<%= t(".helper_transfer") %>
<% else %>
<%= t(".helper_manual") %>
<% end %>
<p class="text-sm text-secondary"
data-goal-pledge-preview-target="helperConnected"
hidden>
<%= t(".helper_transfer") %>
</p>
<p class="text-sm text-secondary"
data-goal-pledge-preview-target="helperManual"
hidden>
<%= t(".helper_manual") %>
</p>
<%= f.money_field :amount,
@@ -39,8 +48,14 @@
data-goal-pledge-preview-target="preview"></p>
<%= 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"
}
} %>
<div class="flex justify-end pt-2">
<%= f.submit t(".submit") %>