Skip to content

fix(frontend): one money formatter and placeholder, right-align money - #1376

Merged
IAmJSD merged 5 commits into
LeanerCloud:mainfrom
IAmJSD:ui/issue-739
Oct 24, 2026
Merged

IAmJSD merged 5 commits into
LeanerCloud:mainfrom
IAmJSD:ui/issue-739

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Money was printed three ways (with the cost period set to yearly, Opportunities showed "$123456" with no separators), absent values used --, — and - depending on the screen (an empty Home mixed a green -- with a bold —), and every money column was left-aligned so digits didn't line up. This PR routes the Opportunities period figures through formatCurrency, uses one — placeholder for absent values, and right-aligns count and money columns with tabular digits on Opportunities, Purchase History and the approval queue.

Refs #739
Partial: modules/savings-history.ts (its private compact formatter and its --) is left to #749, which owns that code, and right-alignment is not yet applied to the Inventory, Plans and RI Exchange tables.

What changed

  • frontend/src/utils.ts: new exported NO_VALUE (—); formatCurrency returns it for null/NaN/Infinity.
  • frontend/src/recommendations.ts: a small formatScaled helper sends formatCostForPeriod and formatScaledRange through formatCurrency with the existing per-period precision (yearly 0, daily/monthly 2 and 0, hourly 4), so every period groups thousands. Opportunities headers and cells for COLUMN_DEFS kind numeric (count, savings, upfront, monthly, on-demand, effective %) get class num.
  • dashboard.ts, history.ts, plans.ts, recommendations.ts, approval-details.ts: every -- and - placeholder becomes NO_VALUE, and an absent KPI value (Home savings, Purchases summary cards) no longer gets the green savings class.
  • frontend/src/styles/tables.css: th.num, td.num { text-align: right; font-variant-numeric: tabular-nums }; the Purchase History and approval-queue count/money columns use it.
  • frontend/src/index.html: the Purchases savings stat cards start at — instead of "$0.00".
  • Tests: new __tests__/money-format.test.ts and tests-e2e/money-columns.spec.ts, new assertions in history.test.ts and dashboard.test.ts, about 40 expectations updated from --/- to —, and NO_VALUE added to 18 manual utils mocks.

Why this approach

  • formatCurrency stays the single formatter instead of adding the issue's suggested formatMoney: 28 modules already import it, and en-US toLocaleString already gives the same output as Intl.NumberFormat.
  • The em dash was already the majority placeholder (about 35 uses vs about 18) and the null convention in the recommendations table.
  • Alignment is one shared CSS class rather than a change to every th/td, because many text columns must stay left-aligned.

Screenshots

Before is main (a14dc8f2), after is this branch. Both are headless Chromium at 1440x900 against the repo's mocked e2e API.

Before After
opportunities-yearly before opportunities-yearly after
home-empty before home-empty after
purchases before purchases after
  • Opportunities, yearly period: "$123456" and "$49200", all money columns left-aligned. After: "$123,456" and "$49,200", with Count and money columns (and their headers) right-aligned with tabular digits.
  • Empty Home: green --, --, bold —, green -- and -- recommendations. After: one neutral — in every tile, with no savings green on absent values.
  • Purchase History: left-aligned money and - for a missing monthly cost. After: Count/Upfront/Monthly/Savings right-aligned, and the missing monthly cost shows —.

Verification

Run on this branch's head e50b553b on top of main a14dc8f2:

  • Lint: 0 errors (142 pre-existing warnings). Typecheck and build pass.
  • Jest: 110 suites, 3488 passed, 1 skipped. --randomize on money-format/history/utils/plans: 293 passed. A randomized run including dashboard.test.ts and recommendations.test.ts failed 14 tests, and the same 14 fail with the same seed (242556410) on untouched main, so that order dependence predates this branch. New tests: money-format.test.ts (NO_VALUE, separators in every period with the real utils), a history.test.ts assertion for which columns carry num, a dashboard.test.ts assertion that an absent savings KPI is not green, and a history banner assertion with no .value.savings.
  • Playwright: 157 of 158 passed. The one failure (history-action-confirmation.spec.ts:177, a revoke refresh race) passed 14/14 when re-run alone and doesn't touch the changed code. The new money-columns.spec.ts checks computed text-align: right and tabular-nums on money cells and headers, that text columns stay left, and the yearly "$123,456".
  • git-secrets scan clean (planted key flagged); test-git-secrets-allowlist.sh: 137 pass, 0 fail.

Notes for review

Overlaps

Those PRs will be opened alongside this one and merged in sequence, with conflicts resolved on the later branch.

Note for the UI owner

This changes UI code that @cristim owns. IAmJSD reviewed the before/after screenshots and asked for it to be merged; any design point you disagree with can be a follow-up.

🤖 Generated with Claude Code

…ht-aligned money columns

Money was printed three ways: formatCurrency (separators), the cost
period cells' `$${x.toFixed(n)}` (so a yearly figure read "$123456"),
and savings-history's private helper. Absent values used "--" (from
formatCurrency and about a dozen literals), "—" and, in the Purchases
tables, "-"; the empty Home showed "--" in savings green next to a bold
"—". All money columns were left-aligned, so digits did not line up.

- utils.ts: export NO_VALUE ("—"); formatCurrency returns it for
  null/NaN/Infinity.
- recommendations.ts: formatCostForPeriod and formatScaledRange go
  through formatCurrency with the per-period precision, so every period
  groups thousands.
- dashboard.ts, history.ts, plans.ts, recommendations.ts: every "--" /
  "-" placeholder becomes NO_VALUE; an absent figure is no longer
  styled as a saving.
- tables.css: th.num/td.num right-align with tabular-nums; the
  Opportunities numeric columns (by COLUMN_DEFS kind) and the Purchase
  History and approval queue count/money columns use it.
- index.html: the Purchases savings stat cards start at "—", not
  "$0.00".

The savings-history module's own formatter is left to LeanerCloud#171/LeanerCloud#749.

Refs LeanerCloud#739

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD IAmJSD added priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged urgency/this-quarter Within the quarter impact/all-users Affects every user effort/m Days type/bug Defect labels Oct 24, 2026
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 35 billable files and costs up to $8.75.

Or wait 59 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 6122fa2 and 412e1a4.

Files selected for processing: 35
  • frontend/src/__tests__/allowed-accounts.test.ts
  • frontend/src/__tests__/approval-queue-column-filters.test.ts
  • frontend/src/__tests__/dashboard-ownership-950.test.ts
  • frontend/src/__tests__/dashboard.test.ts
  • frontend/src/__tests__/four-eyes-approval.test.ts
  • frontend/src/__tests__/history-approval-queue.test.ts
  • frontend/src/__tests__/history-approve-button.test.ts
  • frontend/src/__tests__/history-cancel-button.test.ts
  • frontend/src/__tests__/history-cancel-permissions.test.ts
  • frontend/src/__tests__/history-column-filters.test.ts
  • frontend/src/__tests__/history-marketplace-sell-button.test.ts
  • frontend/src/__tests__/history-retry-button.test.ts
  • frontend/src/__tests__/history-revoke-button.test.ts
  • frontend/src/__tests__/history-truncation-notice.test.ts
  • frontend/src/__tests__/history.test.ts
  • frontend/src/__tests__/money-format.test.ts
  • frontend/src/__tests__/plans-range-validation.test.ts
  • frontend/src/__tests__/plans.test.ts
  • frontend/src/__tests__/purchases-placeholders-181.test.ts
  • frontend/src/__tests__/recommendations-enabled-providers.test.ts
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/__tests__/trends.test.ts
  • frontend/src/__tests__/utils.test.ts
  • frontend/src/approval-details.ts
  • frontend/src/dashboard.ts
  • frontend/src/history.ts
  • frontend/src/index.html
  • frontend/src/plans.ts
  • frontend/src/recommendations.ts
  • frontend/src/styles/tables.css
  • frontend/src/trends.ts
  • frontend/src/utils.ts
  • frontend/tests-e2e/money-columns.spec.ts
  • frontend/tests-e2e/nullable-purchase-cost.spec.ts
  • frontend/tests-e2e/trends.spec.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

IAmJSD and others added 4 commits October 24, 2026 17:19
Conflicts (all in frontend/src/history.ts):
- Purchase History row: kept main's escaped count and upfrontCell()
  (LeanerCloud#1364 amortized upfront) and this branch's right-aligned `num`
  classes on count, monthly and savings.
- Approval queue placeholders: kept this branch's NO_VALUE for account,
  term and payment; dropped the branch's local `const amortize` because
  main hoisted the same declaration above the row map.
- Approval queue row: same combination as the history row.
- upfrontCell() (main, merged cleanly) now emits class `num` on both its
  plain and "(in monthly)" variants, so the upfront column stays
  right-aligned like the other money columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since this branch was cut, main added placeholders that bypass NO_VALUE:
- plans.ts formatRampProgress (LeanerCloud#1327) returned "--" for a missing step
  count; it now returns NO_VALUE like the other plan-card fields.
- trends.ts (new Trends sub-tab, LeanerCloud#1317) showed "--" on its Latest and
  Average savings stat cards; they now use NO_VALUE, matching the monthly
  table, whose null cells come from formatCurrency and already render it.
- trends.test.ts, trends.spec.ts, plans.test.ts and
  nullable-purchase-cost.spec.ts expected the old "--"/"-" text from
  formatCurrency(null) and the muted monthly cell; they now expect "—".

Refs LeanerCloud#739

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- frontend/src/history.ts (Purchase History and approval-queue rows):
  kept main's no-commitments handling (LeanerCloud#1329: hasNoCommitments, dashOr,
  NO_RECS_HTML, termCell) and this branch's num classes on the
  count/upfront/monthly/savings cells and NO_VALUE for the missing monthly
  cost, created-by, account and payment cells. The upfront placeholder for
  an empty row also gets class num, matching upfrontCell.
- frontend/src/recommendations.ts (sortable header): kept main's LeanerCloud#755
  header structure (plain <th> with aria-sort, sort-btn and filter button
  siblings) and added this branch's num class for numeric columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… main added

Main's LeanerCloud#1329 (no-commitments rows) added a muted "-" placeholder
(MUTED_DASH) for Purchase History and approval-queue cells and a "-" Term
column-filter label for a 0 term. They now use NO_VALUE ("—"), like the
rest of the absent values on these tables, so a no-commitments row no
longer mixes "-" with "—". purchases-placeholders-181.test.ts expects "—".

Refs LeanerCloud#739

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): APPROVE 412e1a42c250f3709b5733140bdec108b0611af5

Blocking:

  • none
    Non-blocking:
  • frontend/src/history.ts:1141: the Plan cell in Purchase History still falls back to '-'. A "No recommendations attached" row now shows — in nine cells and - in Plan, which is exactly the mixed-placeholder look this PR sets out to remove. This line predates the branch and was missed. history.ts:1735-1736 (the Sell-on-Marketplace dialog rows) also still use '-'.
  • frontend/src/lib/trends-data.ts:132 / frontend/src/trends.ts:226: bc7ca46 moved the Trends "Latest savings" and "Average savings" stat cards to —, but the "Coverage" card next to them still uses formatCoverage, which returns 'N/A'. The monthly table has the same mix, and trends.test.ts now locks it in with ['…', '—', 'N/A']. Either migrate it or note it as out of scope.
  • frontend/src/tests/history-load-more.test.ts:17: this test came from main (feat(frontend): add Load more to History and queue from all pages #1358) through the merge. Its jest.mock('../utils') has no NO_VALUE, so in that suite MUTED_DASH and every placeholder renders the string "undefined". It passes today only because nothing there asserts on placeholders. It is the only manual utils mock not updated.
  • frontend/src/styles/components.css:1231-1235: .approval-details-table th.num/td.num is now redundant with the new global th.num, td.num rule (tables.css:38-42) and can be removed.
  • For the record, these placeholders remain outside the stated scope: apikeys.ts:144 '--', users/userList.ts:125 '-', inventory.ts:513/525 and ladder.ts:270-271 'N/A', approval-details.ts:253 and riexchange.ts '—' literals. The $${x.toFixed()} in modules/savings-history.ts is the only other money formatter left; the PR already defers it to fix(frontend): purchases savings chart uses two y-axes and shadows formatCurrency #749.
    Attacked: Read git diff origin/main...HEAD in full: utils, recommendations (formatScaled with PERIOD_DECIMALS), history, dashboard, plans, trends, index.html, tables.css and tests. For both merges (093719a, 202208c), diffed history.ts and recommendations.ts against main's parent. Main's escaping is kept: escapeHtml(String(p.count ?? '')), escapeHtml(formatTerm) in termCell, the providerCell whitelist, upfrontCell (fix(frontend): show the monthly charge and term total before purchase #1364), dashOr/NO_RECS_HTML (fix(frontend): stop rendering placeholder values on pending purchases #1329), and fix(frontend): Enter on a column-filter button sorts instead of opening the filter #755's aria-sort, sort-btn and filter siblings. The only changes on top of main's code are the num class and NO_VALUE. No test was deleted or loosened: the follow-ups bc7ca46 and 412e1a4 only swap --/- for —, and the num-column assertions are new. Main's newest commit (f2ab673) merges cleanly; its bulk confirm uses formatCurrency through commitmentSummary.ts, so there is no new formatter. th.sortable .sort-btn uses text-align: inherit, so headers really do right-align. Grepped the tree for --/-/N/A/toFixed/Intl.NumberFormat/currency toLocaleString. Ran 40 jest suites (money-format, history*, purchases-placeholders-181, recommendations*, dashboard, plans*, trends, utils, approval*, purchase-execution-toast, purchase-modal-fanout-payment, css*) on HEAD+main in a scratch worktree: 1519 passed, 1 skipped.

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at 412e1a42 merged onto current main (29077779; merge-tree 2b94e53e; Linux, Node, headless Chromium):

  • This branch's own checks passed at this head: lint, typecheck, build, the full jest suite, the full Playwright e2e suite, and the git-secrets checks (see Verification in the PR body).
  • The combined tree of every UI PR in this merge wave (tree 04af8e9b, main plus 5 PRs) passes npm ci, lint, typecheck, build, jest (1 skipped, 4122 passed, 4123 total), and the full Playwright suite (238 passed).
  • CI is green on 412e1a42 (CI Success, Run pre-commit hooks).
  • The independent review above approves this exact head.
  • Nothing was applied to any cloud.

@IAmJSD
IAmJSD marked this pull request as ready for review October 24, 2026 20:51
@IAmJSD
IAmJSD merged commit 49de8f8 into LeanerCloud:main Oct 24, 2026
29 checks passed
@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Marked ready and merged by IAmJSD's agent, at IAmJSD's request (IAmJSD reviewed the before/after screenshots; cristim is traveling), after the independent review and local verification above, all at 412e1a42.

IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/modules/chart-utils.ts: import block only; kept this
  branch's chart.js import (for upsertChart) and main's formatCurrency
  import (LeanerCloud#1376).
- frontend/src/dashboard.ts: chart-utils import line only; import both
  main's formatSparseTrendMessage/MIN_TREND_POINTS (LeanerCloud#1334) and this
  branch's upsertChart. The sparse-data early return from main sits
  before the upsertChart call and destroys the chart like the empty state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
…urrency

Main's LeanerCloud#1376 (Refs LeanerCloud#739) changed formatCurrency's absent marker to
NO_VALUE ("—"); formatCompactCurrency still hardcoded "--". Delegate
null/NaN/Infinity to formatCurrency so the two cannot drift again.

Refs LeanerCloud#749

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/dashboard.ts: import block only. Kept this branch's lib/upcoming-scope import and took main's widened chart-utils import (formatSparseTrendMessage, MIN_TREND_POINTS from LeanerCloud#1334). renderUpcomingPurchases merged cleanly with main's LeanerCloud#1376 formatter changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/dashboard.ts (imports): kept this branch's lib/kpi-tile
  import and took main's widened chart-utils import (formatSparseTrendMessage,
  MIN_TREND_POINTS from LeanerCloud#1334).
- frontend/src/dashboard.ts (renderDashboardSummary): this branch replaced
  the hand-built tile loop with buildKpiTile; main (LeanerCloud#1376, LeanerCloud#739) added a
  guard to that loop so an absent NO_VALUE figure is not styled as a
  saving. Kept buildKpiTile and moved main's guard into it
  (lib/kpi-tile.ts: valueSavings && value !== NO_VALUE), so Home keeps
  main's behaviour. Because Opportunities uses the same builder, its
  savings tile showing NO_VALUE is now neutral too, matching LeanerCloud#739's intent.
  Added a kpi-tile.test.ts case for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/history.ts: utils import. Main (LeanerCloud#1376) added NO_VALUE to
  the utils import; this branch added the historyConfirmBody import. Kept
  both.
- frontend/src/__tests__/ladder-timeline.test.ts: both sides appended a
  test at the same spot. Kept this branch's refreshLadderTimeline test
  (LeanerCloud#728) and main's planned-purchases layout and UTC date filter describe
  block (LeanerCloud#1387, issue LeanerCloud#753) one after the other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/dashboard.ts:
  - imports: kept this branch's filterByEnabledProviders import and all of
    main's new imports (upcoming-scope LeanerCloud#1365, sparse-trend helpers LeanerCloud#1334,
    chart-a11y LeanerCloud#1381, first-run empty state LeanerCloud#1389).
  - loadDashboard Promise.allSettled: combined both sides into five
    fetches; this branch's loadEnabledProviders() (LeanerCloud#180) plus main's
    listAccountsMinimal() for the first-run onboarding card (LeanerCloud#1389).
  - call site: kept renderDashboardSummary(summaryData, recs, recsCount)
    and main's renderHomeOnboarding(...). The onboarding check uses the
    Enabled-Providers-filtered recs, matching the Opportunities first-run
    check, which also runs on the preference-filtered list.
  - helpers: kept both loadEnabledProviders() and main's
    renderHomeOnboarding(); renderDashboardSummary keeps this branch's
    recsCount parameter.
  - savings tile detail: this branch's filtered count with main's
    NO_VALUE placeholder (LeanerCloud#1376): recsCount ?? total_recommendations ??
    NO_VALUE; Active Commitments takes main's NO_VALUE.
- frontend/src/recommendations.ts: import block; main's NO_VALUE added to
  the utils import, kept this branch's enabled-providers import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
… savings tile

Main's LeanerCloud#1376 replaced the '--' placeholder with NO_VALUE (an em dash).
This branch's LeanerCloud#180 fallback test still expected '--' for the savings
tile value when the recommendations fetch fails.

Refs LeanerCloud#180

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit that referenced this pull request Oct 24, 2026
#1385)

* feat(frontend): add formatCompactCurrency for chart axes

Chart axes need short labels ("$1.5K", "$12.35K", "$1.23M"). The
savings chart built them by hand with `$${(v / 1000).toFixed(1)}K`.
Add one shared helper on Intl.NumberFormat's compact notation, pinned
to en-US like formatCurrency and with the same absent-value marker, so
the chart can drop its own formatting (issue #749).

Refs #749

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(frontend): plot one savings series on one axis with shared formatters

The Purchases savings chart drew period savings (left axis) and
cumulative savings (right axis) in one plot, so the points where the
lines crossed meant nothing. Its ticks read "$1500.00", and the module
had its own formatCurrency ("$1.50K") with the same name as the shared
helper in utils.ts, so the stat cards formatted money differently from
the rest of the Purchases page.

- Period savings are bars on a single y-axis with compact ticks
  (formatCompactCurrency: "$1.5K"). The running total is a separate view,
  chosen with a new Show: Savings per period / Cumulative savings select
  (#savings-series), on the same single axis.
- Tooltips and the Period / Avg / Peak stat cards use the shared
  formatCurrency with cents ("$12,840.00", "$428.00/mo"); the local
  formatCurrency is removed.
- The x-axis keeps its period-start anchor (QA 2.2) with the bar chart's
  default offset turned off.

Refs #749

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(frontend): share formatCurrency's absent marker in formatCompactCurrency

Main's #1376 (Refs #739) changed formatCurrency's absent marker to
NO_VALUE ("—"); formatCompactCurrency still hardcoded "--". Delegate
null/NaN/Infinity to formatCurrency so the two cannot drift again.

Refs #749

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit that referenced this pull request Oct 24, 2026
…#1328)

* fix(frontend): make Home and Opportunities agree on Potential Monthly Savings

Both cards sum the same per-cell range, but over different rows.
Opportunities drops recommendations for providers switched off in
Settings -> Enabled Providers (#463); Home summed every provider, so
with a provider disabled the two same-named KPIs disagreed. Home's
"N recommendations" line also came from the backend total, which ignores
that setting.

Home now reads the config and applies the same filter through a shared
helper (lib/enabled-providers.ts), and counts the rows it summed. A
config fetch failure falls back to no filter, as on Opportunities.

When column filters narrow the Opportunities list, its savings card now
says "Filtered: X of Y recommendations", so a deliberately narrower
scope is labeled instead of looking like a disagreement.

Refs #180

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(frontend): label the Opportunities savings card when a filter is pushed to the server

The "Filtered: X of Y" line only appeared when the visible count was below
the loaded count. A single-value service or region filter is sent to the
server (#162), so the loaded list is already narrowed, the two counts match,
and the card showed a smaller total than Home with no label: the #180
symptom on every load after the first.

The card now decides "filtered" from the active column filters and from the
filters the last fetch pushed to the server. When the loaded list was
narrowed server-side the unfiltered total isn't known, so the line names the
filter instead of inventing a total, e.g.
"Filtered: 6 recommendations (Service: ec2)". The e2e recs mock now applies
service/region like the backend does.

Refs #180

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(frontend): expect the em dash placeholder on Home's failed-fetch savings tile

Main's #1376 replaced the '--' placeholder with NO_VALUE (an em dash).
This branch's #180 fallback test still expected '--' for the savings
tile value when the recommendations fetch fails.

Refs #180

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/all-users Affects every user priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant