You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(frontend/recs): add 'On-Demand Monthly Cost' column to recommendations table #317
Add a new On-Demand Monthly Cost column to the recommendations table on the Recommendations page so users can see the unsubsidised monthly cost alongside the discounted Monthly Cost and Effective % columns.
Why
Today the table shows:
Monthly Cost — recurring discounted cost
Savings — monthly delta vs on-demand
Effective % — savings as a fraction of the equivalent on-demand monthly
…but never surfaces the on-demand baseline directly. Users routinely want to answer "what would I pay without this commitment?" and currently have to do the arithmetic in their head:
This identity is already implemented in frontend/src/recommendations.ts (see the doc comment for effectiveSavingsPct at L388–415) — the value exists, it's just not displayed.
Scope
New column key: on_demand_monthly (display label "On-Demand Monthly").
Renders formatCurrency(...) for non-null monthly_cost; renders — when monthly_cost is null (provider didn't return a recurring breakdown — same convention as the existing Monthly Cost column at L1510).
Sortable + filterable like the other numeric columns:
Add to numericColumns near L189–198 (return Number.POSITIVE_INFINITY when null so null rows sort to the end consistently with monthly_cost).
Add to the column-header label map at L485-ish.
Add to the cell-value resolver around L614–619 (return Number.NaN for null so numeric filter predicates don't match = 0 spuriously).
Add to the column orderings at L646 and L1481.
Add a unit test covering: null monthly_cost → —, non-null with savings + amortized upfront → matches monthly_cost + savings + (upfront_cost/(term*12)).
Out of scope
The cost-period selector (hourly / daily / monthly / yearly) is filed as a separate issue and will fold this column into its scaling once both ship.
No backend change required — pure frontend derivation.
Acceptance criteria
Column renders correctly for AWS, Azure, GCP rows where monthly_cost is non-null.
Renders — (em-dash) when monthly_cost is null.
Sort by on_demand_monthly (asc + desc) works; null rows sort to the end.
Numeric filter predicates (>, <, =) on the column match the same semantics as monthly_cost.
Unit test added in frontend/src/__tests__/recommendations.test.ts.
No regressions in existing column ordering / sort / filter tests.
COLUMN_DEFS / column ID: on_demand_monthly added to the RecommendationsColumnId union in state.ts; SORT_HEADER_LABELS entry 'On-Demand Monthly' added in recommendations.ts.
Exported helper onDemandMonthly(): implements monthly_cost + savings + (upfront_cost / (term × 12)); returns null for null monthly_cost or term = 0.
Render logic: buildVariantRowMarkup renders formatCurrency(odm) for non-null result, '—' (em-dash U+2014) for null — matching the convention of the existing Monthly Cost column. TABLE_COL_COUNT bumped 13 → 14.
Sort support: added to SORTABLE_NUMERIC_COLUMNS with ?? Number.POSITIVE_INFINITY sentinel; extra null-last guard in groupsInSortOrder ensures null rows sort to the end in both asc and desc. Added to both column-ordering arrays (NUMERIC_COLUMNS + COLUMN_ORDER).
Filter support: added to FILTERABLE_COLUMNS; numericCellValue returns Number.NaN for null result so numeric predicates (>, <, =) do not spuriously match = 0.
Tests (frontend/src/__tests__/recommendations.test.ts, +156 lines): 7 onDemandMonthly() unit tests (null input, term=0, no-upfront, partial-upfront, 3yr, all-upfront, identity formula); rendering tests for non-null ($150) and null (em-dash); filter test confirming null row excluded from = 0 predicate; sort-header-click wiring test; column-count and filter-trigger assertions updated to include on_demand_monthly.
Activity
feat(frontend/recs): add 'On-Demand Monthly' column to recommendation…
feat(frontend/recs): add 'On-Demand Monthly' column to recommendation…
cristim commentedon May 13, 2026
Verified resolved by PR #322 (merged 2026-05-06).
Evidence:
COLUMN_DEFS/ column ID:on_demand_monthlyadded to theRecommendationsColumnIdunion instate.ts;SORT_HEADER_LABELSentry'On-Demand Monthly'added inrecommendations.ts.onDemandMonthly(): implementsmonthly_cost + savings + (upfront_cost / (term × 12)); returnsnullfor nullmonthly_costorterm = 0.buildVariantRowMarkuprendersformatCurrency(odm)for non-null result,'—'(em-dash U+2014) for null — matching the convention of the existing Monthly Cost column.TABLE_COL_COUNTbumped 13 → 14.SORTABLE_NUMERIC_COLUMNSwith?? Number.POSITIVE_INFINITYsentinel; extra null-last guard ingroupsInSortOrderensures null rows sort to the end in both asc and desc. Added to both column-ordering arrays (NUMERIC_COLUMNS + COLUMN_ORDER).FILTERABLE_COLUMNS;numericCellValuereturnsNumber.NaNfor null result so numeric predicates (>,<,=) do not spuriously match= 0.frontend/src/__tests__/recommendations.test.ts, +156 lines): 7onDemandMonthly()unit tests (null input, term=0, no-upfront, partial-upfront, 3yr, all-upfront, identity formula); rendering tests for non-null ($150) and null (em-dash); filter test confirming null row excluded from= 0predicate; sort-header-click wiring test; column-count and filter-trigger assertions updated to includeon_demand_monthly.Recommending closure.