Skip to content

feat(home/chart): stack Current/Committed band on per-service potential-range bars - #968

Merged
cristim merged 3 commits into
feat/multicloud-web-frontendfrom
feat/stack-current-on-potential-range
Jun 5, 2026
Merged

feat(home/chart): stack Current/Committed band on per-service potential-range bars#968
cristim merged 3 commits into
feat/multicloud-web-frontendfrom
feat/stack-current-on-potential-range

Conversation

@cristim

@cristim cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Merges the two-grouped-bar layout (potential stack + separate current stack) into a single stacked bar per service with three layers: Current / Committed (bottom, darkest), Lowest option (middle, solid base hue), and Upside (top, 35% opacity).
  • Term replacements: "Floor" / "Min potential" -> Lowest option; "Range" -> Upside (consistent across legend, tooltip, and JSDoc).
  • Sort order updated to (current + max_potential) DESC so the bar with the most total visible value is leftmost.
  • Services present in byService but absent from recs now render a Current-only bar (commitment exists, no new recommendation surfaced).

Design

Layer order (bottom to top, all in stack: 'savings'):

Layer Value Colour
Current / Committed byService[svc].current_savings darkenHexColor(base)
Lowest option max(0, min(rec) - current) base (solid)
Upside max(rec) - min(rec) rgba(base, 0.35)

Data sources: recs[] (from getRecommendations) for the rec-derived layers; byService map (from loadDashboard's getDashboardSummary().by_service) for the Current layer. Union of both sources drives the service set.

Tooltip: shows Service, Total, Current / Committed, Lowest option, Upside, Min/Max option labels.

Test plan

  • npx tsc --noEmit clean (zero errors)
  • Updated existing renderSavingsByService DOM tests: renamed dataset labels, adjusted stack assertions, updated sort-order test to include current in sort key
  • New test: service present in byService only (no recs) renders Current band at the correct value with Lowest option = 0 and Upside = 0
  • New test: Lowest option clamps to 0 when committed savings exceed min rec
  • Existing computeServiceStats / computeServiceStatsFromRecs unit tests unchanged (no logic changes to those helpers)

Summary by CodeRabbit

  • Bug Fixes
    • Improved per-service savings chart visualization to display stacked layers for Current/Committed, Lowest option, and Upside savings.
    • Enhanced service ordering based on total visible savings.
    • Updated tooltips to show detailed breakdown of savings components and better handle services with incomplete data.

@cristim cristim added priority/p2 Backlog-worthy severity/low Minor harm urgency/this-sprint Within the current sprint impact/all-users Affects every user enhancement New feature or request triaged Item has been triaged labels Jun 5, 2026
@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9b95fbe-d1e9-4b7d-9be9-c3c63ac882ea

📥 Commits

Reviewing files that changed from the base of the PR and between f86ff5b and 87bfee6.

📒 Files selected for processing (2)
  • frontend/src/__tests__/dashboard.test.ts
  • frontend/src/dashboard.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/dashboard.ts
  • frontend/src/tests/dashboard.test.ts

📝 Walkthrough

Walkthrough

The renderSavingsByService per-service savings chart implementation and tests are updated to render three stacked layers per service—Current/Committed (from commitment data), Lowest option (clamped gap to minimum recommendation), and Upside (clamped recommendation variance)—with union-based service selection and reformatted tooltip aggregation.

Changes

Per-Service Savings Chart Redesign

Layer / File(s) Summary
Chart Model and Service Filtering
frontend/src/dashboard.ts
Documentation block and service filtering logic updated to reflect the new three-layer stacked bar model, union-based service inclusion from both recommendations and commitment data, inclusion based on positive visible total across all layers, and descending sort by visible total.
Dataset Construction and Stacking
frontend/src/dashboard.ts
Bar chart dataset setup reworked to generate three parallel arrays (current, lowestOption, upside) from per-service values, apply clamping to ensure non-negative layers, compute chart-wide totals for percentage calculations, and assign layer-specific colors (darkened current, solid lowest-option base, rgba upside).
Tooltip and Aggregation Logic
frontend/src/dashboard.ts
Tooltip label callback updated to compute per-service total and percent-of-all using the new visible-total math and to render current/lowest/upside monetary breakdown with optional min/max recommendation labels.
Test Validation of Stacked Chart Behavior
frontend/src/__tests__/dashboard.test.ts
Test assertions expanded to validate three-dataset structure and stacking, verify layer values including clamping behavior, confirm Current/Committed color equals darkened Lowest option color per RGB-channel validation, test edge cases (services absent from commitment data, services with no recommendations), and confirm filter-change re-render includes newly-recommended services in chart labels and ordering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • LeanerCloud/CUDly#782: Modifies the same renderSavingsByService chart data model and dataset construction as the main PR.
  • LeanerCloud/CUDly#766: Evolves the per-service savings bar chart from an earlier floor/range implementation to the new three-layer merged model.

Suggested labels

severity/medium, effort/m, type/feat

Poem

🐰 Three layers now stack so neat,
Current, Lowest, Upside complete!
Services sorted by their worth,
Union-bound from every berth,
Clamped and stacked, the chart takes flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: stacking the Current/Committed band on per-service potential-range bars, which is the primary refactoring from a two-grouped-bar layout to a single stacked bar per service.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stack-current-on-potential-range

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

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/src/__tests__/dashboard.test.ts (1)

1394-1418: ⚡ Quick win

Add one tooltip-total assertion in this stacked-layer test block.

You validate layer values, but not that tooltip Total matches the displayed stack breakdown. A callback assertion here would guard the exact regression in total arithmetic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/__tests__/dashboard.test.ts` around lines 1394 - 1418, In the
"three stacked datasets" test that calls renderSavingsByService and inspects the
Chart mock (Chart as jest.Mock, lastCall = chartCtor.mock.calls[...], datasets,
currentDs/lowestDs/upsideDs), add one assertion that invokes the chart tooltip
callbacks to verify the displayed "Total" equals the sum of the stack layers (0
+ 100 + 300) — e.g., grab the tooltip callbacks from
lastCall[1].options.plugins.tooltip.callbacks and call the appropriate callback
(label/footer) with a synthetic tooltip context for the first datapoint, then
expect the returned "Total" string/value to match the computed total; this
ensures tooltip arithmetic matches the dataset values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/dashboard.ts`:
- Around line 774-777: The total/sort logic currently uses (e.current +
e.maxRec) which diverges from the bar rendering (which sums current + lowest
option + upside); change entries.filter and positive.sort to compute totals
using the exact same expression or helper used by the bar rendering (e.g.,
extract or call the renderTotal helper that sums the same fields the renderer
uses — current + lowestOption + upside) so filtering, tooltips, and sorting all
use that single canonical total; also update the other occurrences in the
818-883 range to use the same helper/expression.

---

Nitpick comments:
In `@frontend/src/__tests__/dashboard.test.ts`:
- Around line 1394-1418: In the "three stacked datasets" test that calls
renderSavingsByService and inspects the Chart mock (Chart as jest.Mock, lastCall
= chartCtor.mock.calls[...], datasets, currentDs/lowestDs/upsideDs), add one
assertion that invokes the chart tooltip callbacks to verify the displayed
"Total" equals the sum of the stack layers (0 + 100 + 300) — e.g., grab the
tooltip callbacks from lastCall[1].options.plugins.tooltip.callbacks and call
the appropriate callback (label/footer) with a synthetic tooltip context for the
first datapoint, then expect the returned "Total" string/value to match the
computed total; this ensures tooltip arithmetic matches the dataset values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c01cddb-b467-46e3-b273-a9e62ca9b024

📥 Commits

Reviewing files that changed from the base of the PR and between dd16b94 and f86ff5b.

📒 Files selected for processing (2)
  • frontend/src/__tests__/dashboard.test.ts
  • frontend/src/dashboard.ts

Comment thread frontend/src/dashboard.ts Outdated
…+ rename layers

Merge the two-grouped-bar layout (potential stack + separate current stack)
into a single stacked bar per service with three layers, bottom to top:

1. Current / Committed (darkest shade): realized savings from active commitments
2. Lowest option (solid base hue): max(0, min(rec) - current), the gap between
   the cheapest available option and what is already committed
3. Upside (35% opacity): max - min spread across recommendation options

Renames: "Min potential" -> "Lowest option"; "Range" -> "Upside" (consistent
across legend, tooltip, aria/aria-label, and JSDoc).

Sort order changes from max-potential-desc to (current + max-potential) desc
so the bar with the most total visible value is leftmost.

Services present in byService but absent from recs now render a Current-only
bar (commitment exists, no further upside surfaced). Tooltip shows all three
layers with a total line and % of all services.

Updates renderSavingsByService tests; adds two new focused tests:
- service with commitments only (byService-only) renders Current band
- Lowest option clamps to 0 when committed savings exceed min rec
@cristim
cristim force-pushed the feat/stack-current-on-potential-range branch from f86ff5b to 9b05a29 Compare June 5, 2026 10:26
@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added 2 commits June 5, 2026 12:28
…ar layers

Total/sort/filter used `current + maxRec` while the bar renders
`current + lowestOption + upside`. For a service with current=250,
min=100, max=400 the old code reported Total=650 in the tooltip but
the bar only showed 550. Fix: compute lowestOption and upside once on
SvcEntry, derive visibleTotal from those, and use visibleTotal in the
filter, sort key, totalSavings accumulator, and tooltip total. Update
the sort-order test comment to reflect the corrected totals.
Add one assertion in the "three stacked datasets" test that invokes the
tooltip label callback and verifies the returned Total line matches the
sum of all three visible bar layers (current + lowestOption + upside).

Guards the regression that Total/sort math now uses the same components
as the rendered bar (current + lowestOption + upside) instead of the
prior current + maxRec divergence.
@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

Addressed CR pass-1 findings:

  • dashboard.test.ts:1394-1418 tooltip Total assertion -> fixed in 87bfee6: added assertion that invokes the tooltip label callback and verifies the Total line matches $400 (sum of current=0 + lowestOption=100 + upside=300)

@coderabbitai review

@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

CR pass-1 findings disposition:

  • Total and ranking math diverges from stacked values (dashboard.ts:774-883, Major) -- stale: addressed in aef40fb ("fix(home/chart): align sort, filter, and tooltip total with visible bar layers") committed after the review.
  • Nitpick: add tooltip-total assertion in stacked-layer test (dashboard.test.ts:1394-1418) -- stale: assertion added in 87bfee6 ("test(home/chart): assert tooltip Total equals sum of stacked bar layers") committed after the review.

Both findings already resolved. PR is merge-ready (mergeStateStatus=CLEAN, CI green). CR re-ping deferred: org-level usage credits blocked. Re-invoke /pr-iterate 968 after CR billing is restored to get the next review pass.

To clear after billing is fixed: rm ~/.claude/agent-comms/cr-rate-limit-deadline.txt

@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim merged commit 5f037eb into feat/multicloud-web-frontend Jun 5, 2026
6 checks passed
@cristim
cristim deleted the feat/stack-current-on-potential-range branch July 27, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request impact/all-users Affects every user priority/p2 Backlog-worthy severity/low Minor harm triaged Item has been triaged urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant