Skip to content

fix(opportunities): cost-column sort toggle + expand/collapse persistence on filter change - #1259

Merged
cristim merged 3 commits into
mainfrom
fix/qa230-opps-table-sort-expand
Jul 9, 2026
Merged

cristim merged 3 commits into
mainfrom
fix/qa230-opps-table-sort-expand

Conversation

@cristim

@cristim cristim commented Jun 19, 2026 •

Copy link
Copy Markdown
Member

Closes #1251

Stacked on #1254 (pre-commit repair); retarget to main when #1254 merges.

Bug 1: Cost-column sort no-ops after the first click (QA 4.13)

Symptom: Clicking the Cost column header sorts on the first click but subsequent clicks (which should toggle asc/desc) do nothing.

Root cause: The per-cell monthly_cost sort score used Math.min over all scaled finite variant costs, including 0. Azure all-upfront recommendations have monthly_cost = 0 (a real value, not null), so any cell with at least one all-upfront variant scored 0. Most/all cells tied at 0, leaving the direction multiplier nothing to act on (direction * (0 - 0) = 0); the comparator then fell through to a direction-invariant cellKey localeCompare tiebreaker, so flipping direction never reordered. The first click only appeared to work because it switched away from the default savings-desc ordering.

Fix: Compute the minimum NON-ZERO recurring cost so mixed (all-upfront + no-upfront) cells get a meaningful, direction-responsive score. Fall back to 0 only when every finite value is 0 (pure all-upfront cell), and to POSITIVE_INFINITY only when all variants are null (preserving the existing null-sink behavior and the #494 best-case framing intent).

Bug 2: Expand/collapse desyncs on provider-filter change (QA 4.13)

Symptom: Select one provider, click "Expand all", then change the provider filter to another provider (or All Providers). Expected: the table updates and stays expanded. Actual: groups for the newly-shown provider render collapsed, "Collapse all" resets to "Expand all", and "All Providers" yields a mixed expanded/collapsed state.

Root cause: The module-level expandedCells and expandedSpGroups Sets are never cleared when the provider/account Global filter changes. loadRecommendations re-fetches on provider change but did not clear them, so stale keys from the prior provider desynced the Expand-All button state and left a mixed view.

Fix: Call resetExpandedCells() at the start of loadRecommendations() so a provider/account filter change clears expand state before the new data renders. Column-filter and sort re-renders go through rerenderRecommendations() (not loadRecommendations()), so the intended "expand survives column-filter/sort" behavior is preserved.

Testing

frontend/src/__tests__/recommendations.test.ts:

  • Sort (mixed cell): a cell with both an all-upfront (monthly=0) and a no-upfront (monthly>0) variant sorts by the non-zero value, and toggling asc<->desc produces opposite orderings (the toggle is live).
  • Sort (pure all-upfront): a cell whose variants are all monthly=0 scores 0 (not POSITIVE_INFINITY) and sorts before all-null cells.
  • Expand reset: simulate expand-all under one provider filter, then a provider-filter change (loadRecommendations), and assert the button label reverts to "Expand all" and newly-shown groups render collapsed.

All three confirmed to FAIL on pre-fix code and PASS post-fix.

  • npx jest src/__tests__/recommendations.test.ts -> 396 pass, 0 fail
  • npx tsc --noEmit -> no errors

Summary by CodeRabbit

Bug Fixes

  • Expanded recommendation cells now properly reset when applying global filter changes
  • Monthly cost sorting now correctly handles recommendations with mixed zero-cost and null-cost variants, ensuring accurate sort order when toggling direction

@cristim cristim added triaged Item has been triaged type/bug Defect priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/this-quarter Within the quarter impact/many Affects most users effort/s Hours labels Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 4 minutes and 52 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc507c38-7ffe-4d6b-93e4-164182e89cf9

📥 Commits

Reviewing files that changed from the base of the PR and between 451a70f and 5725200.

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

Walkthrough

Two bugs from QA 4.13 are fixed. loadRecommendations() now calls resetExpandedCells() on each reload so expand/collapse state does not persist across provider/account filter changes. cellScoreFor() for monthly_cost is updated to prefer the smallest non-zero finite scaled cost, falling back to 0 for pure all-upfront cells and +Infinity for all-null cells. Three regression tests are added covering both fixes.

Changes

QA 4.13: Expand-state reset and monthly_cost sort scoring fixes

Layer / File(s) Summary
Expand/collapse state reset on loadRecommendations
frontend/src/recommendations.ts, frontend/src/__tests__/recommendations.test.ts
loadRecommendations() calls resetExpandedCells() after reading sort state from URL, ensuring stale expansion state does not survive provider/account Global filter changes. A regression test (QA 4.13) verifies the expand-all button label reverts and variant rows are hidden after reload.
cellScoreFor monthly_cost: non-zero finite cost preference
frontend/src/recommendations.ts, frontend/src/__tests__/recommendations.test.ts
cellScoreFor() for monthly_cost now returns the minimum non-zero finite scaled cost, falls back to 0 when all finite values are zero (pure all-upfront), and returns POSITIVE_INFINITY only when no finite values exist. Two regression tests confirm mixed cells sort by non-zero cost with working asc/desc toggling, and pure all-upfront cells score 0 and rank before all-null cells.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • LeanerCloud/CUDly#242: Introduced the Monthly Cost column and its initial monthly_cost sort wiring in the same cellScoreFor function this PR patches.
  • LeanerCloud/CUDly#496: Also modifies cellScoreFor() around zero-vs-null monthly_cost per-cell scoring and adds deterministic ordering tests in recommendations.test.ts.
  • LeanerCloud/CUDly#807: Touches loadRecommendations() and resetExpandedCells() in the same file, directly overlapping with the expand-state reset change in this PR.

Suggested labels

priority/p1, severity/high, urgency/this-sprint

🐇 Hop, hop, the cells collapse right,
No ghost expansions haunt the night!
Zero costs now know their place,
Non-zero sorts win every race.
The filter flips, the state resets—
A tidy table, no regrets! 🥕

🚥 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 PR title directly and clearly summarizes the main changes: fixing cost-column sort toggle and expand/collapse persistence on filter change, matching the primary objectives.
Linked Issues check ✅ Passed All coding objectives from issue #1251 are met: sort toggle fixed via non-zero cost logic [#1251], expand state reset on filter change via resetExpandedCells() [#1251], and regression tests added [#1251].
Out of Scope Changes check ✅ Passed All changes directly address the two bugs in issue #1251; no unrelated modifications to other components or functionality were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/qa230-opps-table-sort-expand

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

@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 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 changed the base branch from fix/1222-precommit-repair to main June 19, 2026 14:51
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

cristim added 2 commits June 19, 2026 23:56
Monthly Cost sort direction changes had no effect after the first click
because Math.min over all finite scaled costs scored every cell with at
least one all-upfront variant at 0, making them all tie and leaving the
direction multiplier nothing to act on.

Fix: compute the minimum NON-ZERO recurring cost so mixed
(all-upfront + no-upfront) cells get a meaningful score that responds to
direction toggles. Fall back to 0 only when every finite value is 0 (pure
all-upfront cell), and to POSITIVE_INFINITY when all values are null
(preserving the existing null-sink behavior).

Closes #1251
Selecting one provider, clicking "Expand all", then switching the Global
provider/account filter left groups for the newly shown provider collapsed,
reset the "Collapse all" button back to "Expand all", and produced a mixed
expanded/collapsed view under "All Providers". The module-level expandedCells
and expandedSpGroups Sets retained stale keys from the prior provider because
loadRecommendations re-fetched on filter change without clearing them.

Fix: call resetExpandedCells() at the start of loadRecommendations so a
provider/account filter change clears expand state before the new data
renders. Column-filter and sort re-renders go through rerenderRecommendations
(not loadRecommendations), so the intended "expand survives column-filter/sort"
behavior is preserved.

Closes #1251
@cristim
cristim force-pushed the fix/qa230-opps-table-sort-expand branch from a5d6e64 to 8ced5a7 Compare June 19, 2026 22:02
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@cristim have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 3 seconds before sending another message.

@cristim

cristim commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Adversarial review of #1259 noted the headers above `expandedCells` /
`expandedSpGroups` still read "Cleared on page load / full refresh"
after this PR moved the actual clear into `loadRecommendations()`. The
behavior is now broader than that wording suggests: the reset fires on
every loadRecommendations entry — page load, switchTab('opportunities'),
provider/account Global filter change, manual refresh, lookback change,
and stale auto-refresh. The "survives per-column filter/sort"
guarantee still holds because column-filter and sort/period re-renders
all go through rerenderRecommendations() instead.

Update both headers to match the implementation so the next reader
doesn't conclude that a tab switch or refresh preserves expand state
(it doesn't). Comment-only change; no behavior delta.
@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim
cristim merged commit 1964440 into main Jul 9, 2026
13 of 17 checks passed
@cristim
cristim deleted the fix/qa230-opps-table-sort-expand branch July 9, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/many Affects most users priority/p3 Polish / idea / may never ship severity/low Minor 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.

fix(opportunities): Cost-column sort no-ops after first click + expand/collapse desyncs on provider-filter change (QA 4.13)

1 participant