Skip to content

feat(frontend/dashboard): Potential Monthly Savings card mirrors Recommendations-page range #293

@cristim

Description

@cristim

Problem

PR #283 closed #279 by making every summary card on the
Recommendations page render a per-cell range (savings, upfront,
payback) and narrow on selection. The Dashboard view's "Potential
Monthly Savings" card (frontend/src/dashboard.ts:128) still renders
the flat summary.potential_monthly_savings from
/api/dashboard/summary — the same overcounting bug class #272 closed
on the Recommendations page (sums every variant of every cell, ~6×
overstates achievable totals).

Splitting this out of #279 because the Dashboard work is structurally
different from the Recommendations-page work that's already shipped:
it's a separate page, has no per-row selection state to narrow on, and
needs either a backend /dashboard/summary extension or a frontend
extra-fetch to source the per-cell range data.

Acceptance criteria

  • Dashboard's "Potential Monthly Savings" card renders the same
    per-cell range that the Recommendations page card does
    (pageLevelRange(groupRecsByCell(recs)).savingsMin /
    .savingsMax).
  • Implementation: frontend-only — dashboard.ts::loadDashboard
    fetches /api/recommendations alongside the existing
    /api/dashboard/summary + /api/upcoming calls, computes the range
    client-side using the existing pageLevelRange /
    groupRecsByCell helpers exported from recommendations.ts. The
    recs endpoint is served from Postgres cache so the extra API call
    is cheap. Backend change deferred — a future PR can move the range
    computation server-side if needed.
  • No selection narrowing on Dashboard — Dashboard is a read-only
    view, no checkboxes. Card always reflects the range across whatever
    recommendations the user's current provider/account filter exposes.
  • Other Dashboard cards (Active Commitments, Current Coverage,
    YTD Savings) keep their existing API-driven values — they're
    aggregates that don't have the variant-fan-out overcounting issue.
  • If /api/recommendations fails, the Dashboard still renders —
    the savings card falls back to formatCurrency(0) and the rest of
    the dashboard is unaffected (independent loaders).
  • Tests:
    • dashboard.test.ts (or wherever dashboard tests live): assert
      the savings card renders the range when recs are mocked.
    • Failure-isolation: when getRecommendations rejects, the other
      cards still render.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions