Repository navigation
fix(frontend/dashboard): chart tooltip and axis-tick money formatting does not pin a locale #1733
Description
frontend/src/dashboard.ts formats dollar values with 5-6 independent .toLocaleString() calls inside Chart.js tooltip and axis-tick callbacks, none of which pin a locale. This is the same defect #1728 fixed in formatCurrency, in a file that fix did not reach.
Exact line numbers are listed in PR #1732's body.
Why it was scoped out of #1732
Deliberately, and correctly. Those call sites render to canvas, not the DOM; no test exercises them; and none of #1728's eight failures originated there. Widening a one-line locale fix into untested chart-rendering code would have been scope growth into the least verifiable part of the frontend.
The question worth answering first
Should these call formatCurrency instead of formatting money ad hoc?
If they can, that is the better fix: the underlying defect is that money formatting exists in more than one place, which is exactly what allowed the locale divergence. Consolidating removes the class rather than pinning six more instances of it. formatCurrency already pins 'en-US' after #1732 and already hardcodes the $ prefix, so a caller gets both behaviours for free.
If they genuinely cannot — different precision for axis ticks, abbreviated forms like $1.2M, or non-currency numbers mixed in — then pin each explicitly and record why consolidation was rejected, so the next person does not re-litigate it.
Resolve that before writing any code. The answer determines whether this is a 1-line change or a 6-line change.
Verification
Same standard as #1732: run under at least two locales (LANG=en_US.UTF-8 and LANG=de_DE.UTF-8). Since no test covers these paths, a locale run alone proves nothing — render the dashboard and look at the chart under a non-US locale before and after. If that is impractical, say so plainly in the PR rather than implying coverage that does not exist.
Do not fabricate tests for chart rendering to make this look verified. An honest "mechanical change, untested path, here is the visual check" is worth more than a test written to satisfy a checklist.
Context
- fix(test/frontend): 8 tests assert the host locale's thousands separator, so they fail locally and pass in CI #1728 / PR fix(frontend): pin formatCurrency to en-US instead of the host locale #1732 — the same defect in
formatCurrency; fixed by pinning'en-US', matching theformatDate/formatDateTimeconvention already established in that file. - The design decision is settled: CUDly is USD-only end to end and
formatCurrencyalready hardcodes$, so following the host locale for number grouping was an oversight rather than an intent.
Found while fixing #1728.
Activity
cristim commentedon Aug 7, 2026
Closing as fixed in PR #1732 (commit
ab2b845), which extended the #1728 fix to these sites.The DRY question this issue asked to resolve first came back clean:
dashboard.tsalready importsformatCurrencyand already uses it at:306,:308,:488and:600. The six inline$${x.toLocaleString()}sites were therefore pure duplication rather than a signature or precision mismatch — all six values are guaranteed-finite whole-dollar numbers with a literal$prefix, which is exactlyformatCurrency's default signature.So the better fix was available: replace all six with
formatCurrency(x)rather than pin'en-US'six more times. That removes the duplication that allowed the divergence in the first place, instead of making the divergence consistent.Sites consolidated:
:965,:976-979(savings-by-service tooltip),:997and:1150(Chart.js axis-tick callbacks),:1134(cumulative-savings tooltip).No tests were added, deliberately and as recorded in the PR: these render to canvas, had no existing coverage, and the change is a mechanical substitution. Verified via
tsc,eslint, and the full suite under bothLANG=en_US.UTF-8andLANG=de_DE.UTF-8(2781 pass / 0 fail under each) rather than by fabricating chart-rendering tests to satisfy a checklist.This issue existed for about twenty minutes and its whole value was forcing the consolidate-or-pin question to be answered before any code was written. Worth noting the pattern: filing the sibling finding separately, with the open question stated, produced a better fix than either silently widening the original PR or silently dropping the finding would have.