From 40a6613603d76756d12445fed6b24113f59289e0 Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Thu, 14 May 2026 21:56:42 +0200 Subject: [PATCH] fix(goals/chart): full money format on "Short" annotation + 4-digit year on x-axis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two screenshot-driven audit fixes. The chart's "Short $160.6K" annotation used `_fmtMoneyShort`'s K/M shorthand while the page's other monetary readouts ("$26,621 to catch up", "$187,031 saved", "$1,830 last 30d") were full `Intl.NumberFormat` output. Inconsistent units in the same viewport. Switch the annotation to `_fmtMoney` ("$160,634 short") + reword to put the money first ("$X short" reads more naturally than "Short $X"). Y-axis tick labels keep the K/M shorthand — that column is space-constrained and the same convention is already understood as "axis abbreviation." The x-axis terminal tick rendered `"Jan '27"` from the `"%b '%y"` time-format string. A glance read it as January 27th of the year, not January 2027 — and the target-date tick is the single one users navigate the chart for. Switch to `"%b %Y"` → "Jan 2027." Slightly wider per tick; the existing adjacent- duplicate-removal logic keeps the count sane. --- .../controllers/goal_projection_chart_controller.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/javascript/controllers/goal_projection_chart_controller.js b/app/javascript/controllers/goal_projection_chart_controller.js index 6686f5dd6..4dc541d20 100644 --- a/app/javascript/controllers/goal_projection_chart_controller.js +++ b/app/javascript/controllers/goal_projection_chart_controller.js @@ -297,9 +297,12 @@ export default class extends Controller { const collidesWithTargetLabel = targetAmount > 0 && Math.abs(projDotY - y(targetAmount)) < 18; if (innerWidth >= 320 && !(willHit && collidesWithTargetLabel)) { + // Full Intl.NumberFormat (no K/M shorthand) so the chart annotation + // matches the rest of the page's monetary readouts ("$160,634 + // short" reads cleanly next to "$26,621/mo to catch up"). const labelText = willHit - ? this._fmtMoneyShort(projectionEnd, data.currency) - : `Short ${this._fmtMoneyShort(targetAmount - projectionEnd, data.currency)}`; + ? this._fmtMoney(projectionEnd, data.currency) + : `${this._fmtMoney(targetAmount - projectionEnd, data.currency)} short`; svg .append("text") .attr("x", x(target) - 8) @@ -377,7 +380,10 @@ export default class extends Controller { .text("Today"); } - const tickFmt = d3.timeFormat("%b '%y"); + // Full 4-digit year so the terminal "Jan 2027" reads as the year, not + // as "Jan 27" (which scans as January 27th). Slightly wider per tick; + // the de-dupe logic below keeps the count sane. + const tickFmt = d3.timeFormat("%b %Y"); const tickCount = Math.min(5, Math.max(2, Math.round(innerWidth / 80))); const ticks = x.ticks(tickCount); const tickGroup = svg.append("g");