From 815fb9d8fa4e50153a05289aff6305f4bd99a444 Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Thu, 14 May 2026 20:33:08 +0200 Subject: [PATCH] fix(goals/funding-widget): switch sparkline to bars + shared scale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared-scale fix alone wasn't enough — a single outlier bucket on one account compressed every other row to invisibility, and the interpolated line between sparse non-zero buckets painted fake "event triangles" between actual data points. Switch from a stroked path to per-bucket bars: - 12 rects per row, x = `i * 8 + 1`, width 6, 2px gap between. - Bar height = `(value / shared_max) * 24`, floored at 1 unit so a non-zero bucket is always visible even when an outlier elsewhere dominates the scale. - Empty buckets render nothing — no fake baseline, no interpolated trough. - Bars grounded at `y = 28` (bottom of viewBox), so "zero" is implicit and the eye reads upward from a stable floor. - Shared `spark_max` across every account's bars (the component method introduced for the line version stays — that part of the diagnosis was right, it just needed a chart type that handled the scale honestly). Net read: the column-chart-on-each-row layout matches "12 weeks of deposits into this account" much more directly than a sparkline ever did, and outlier-vs-modest-but-steady contributions are both legible at a glance. --- .../funding_accounts_breakdown_component.html.erb | 12 +++++++----- .../goals/funding_accounts_breakdown_component.rb | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/components/goals/funding_accounts_breakdown_component.html.erb b/app/components/goals/funding_accounts_breakdown_component.html.erb index c67f924e9..95bff4010 100644 --- a/app/components/goals/funding_accounts_breakdown_component.html.erb +++ b/app/components/goals/funding_accounts_breakdown_component.html.erb @@ -39,7 +39,7 @@ <% account = row[:account] %> <% color = Goals::AvatarComponent.color_for(account.name) %> <% spark = row[:sparkline_points] %> - <% spark_max = [ spark.max, 1.0 ].max %> + <% spark_max = shared_spark_max %>
<%= render Goals::AvatarComponent.new(name: account.name, color: color, size: "sm") %> @@ -57,10 +57,12 @@ <% end %>
diff --git a/app/components/goals/funding_accounts_breakdown_component.rb b/app/components/goals/funding_accounts_breakdown_component.rb index 294ff0825..e5a8b80dd 100644 --- a/app/components/goals/funding_accounts_breakdown_component.rb +++ b/app/components/goals/funding_accounts_breakdown_component.rb @@ -29,6 +29,18 @@ class Goals::FundingAccountsBreakdownComponent < ApplicationComponent ((balance.to_d / total) * 100).round end + # Shared Y-max across every account's sparkline so per-row amplitudes + # are comparable at a glance. Without this each row scaled to its own + # max and a $50 bump on an orange account rendered as tall as a $400 + # weekly peak on a pink one — the eye reads them as equal contribution + # when the weight pills already say they aren't. + def shared_spark_max + @shared_spark_max ||= begin + points = rows.flat_map { |r| r[:sparkline_points] } + [ points.max.to_f, 1.0 ].max + end + end + # Label shown beneath the account name. Prefers the depository subtype # ("Savings", "HSA"…) over the bare accountable_type ("Depository") so the # subline carries useful signal. Falls back to the accountable type's i18n