Skip to content

fix(frontend): keep the RI exchange reshape empty-state count current - #1395

Merged
IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-1135
Oct 24, 2026
Merged

IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-1135

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

The RI Exchange page loads convertible RIs, reshape recommendations and exchange history in parallel. When the reshape list came back empty before the RI list, its empty state quoted a stale RI count (the previous scope's, or "none are registered yet" on a first load) and was never re-rendered, so the page could list three convertible RIs and say right below that none are registered. This PR re-renders the empty state once both loads finish and shows neutral copy until the RI count is known.

Closes #1135

What changed

All in frontend/src/riexchange.ts:

  • A new module flag currentRIsLoaded is set only after the current scope's RI list loads successfully, and cleared wherever currentRIs is reset (AWS reload start, the Azure/GCP paths, the account-resolve error).
  • renderRecommendations' empty branch quotes the RI count only when the flag is set; otherwise it shows "No reshape recommendations right now."
  • loadReshapeRecommendations now resolves true when it rendered a fresh response and false when superseded or failed. loadRIExchange reads that from the AWS Promise.all and, for the same generation, re-renders the reshape empty state when the load succeeded with no rows. A failed reshape load keeps its "Failed to load recommendations" error.
  • 3 new tests in frontend/src/__tests__/riexchange.test.ts.

Why this approach

This is the fix the issue proposes (re-render after the Promise.all for the same generation, keep errors), plus the loaded flag. Without the flag, the stale or "none registered" copy would still flash until the RI list arrived, and a failed RI load would leave the previous scope's count up permanently. Re-rendering from loadConvertibleRIs would also work, but it would need to know whether the reshape load finished and succeeded; doing it after Promise.all keeps that decision in one place and follows the generation-guard pattern already in the file. Returning Promise<boolean> from loadReshapeRecommendations is backward compatible, since existing callers ignore the result.

Screenshots

Before is main (a14dc8f2), after is this branch. Both are headless Chromium at 1440x900 against the repo's mocked e2e API. On a first load, /api/ri-exchange/instances is delayed 1.5s and returns 3 RIs, while the reshape recommendations return an empty list immediately.

Before After
reshape-count before reshape-count after
  • On main, the table lists 3 active convertible RIs but the Reshape Recommendations empty state says "none are registered yet". On this branch, once both loads finish it reads "All 3 convertible RIs meet your utilization threshold. No reshape needed."

Verification

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

  • npm run lint: 0 errors (142 pre-existing warnings). Typecheck and build pass.
  • Jest: 109 suites, 3489 passed, 1 skipped. riexchange.test.ts passes with --randomize (76/76).
  • New tests: reshape resolves before a deferred RI list (interim copy is neutral, never the old "All 1"; final copy is "All 3 convertible RIs"); a failed RI load shows neutral copy, not a count; a failed reshape load keeps its error after the RIs arrive. The first two fail on main.
  • Playwright via e2e-local.sh: 157 passed.
  • Secrets scan clean (planted key flagged); scripts/test-git-secrets-allowlist.sh: 137 PASS.

Notes for review

  • New copy: "No reshape recommendations right now." appears briefly while the RI list is loading, and stays if the RI list fails to load.
  • No Playwright spec was added: this is a data race, not a layout or interaction fix, and the jest tests drive the exact ordering.
  • riexchange.ts is about 2480 lines, already far over the 500-line rule; this adds about 30.

Overlaps

Several branches edit frontend/src/riexchange.ts in other functions: #18 (large popover refactor that removes big blocks, so merge with care), #284, #321, #709, #727, #729, #737, #738 and #740. None touch loadRIExchange's Promise.all, loadReshapeRecommendations or the renderRecommendations empty branch, so conflicts should be limited to line drift.

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

loadRIExchange loads the convertible RIs, reshape recommendations and
history in parallel. When the reshape list came back empty before the RI
list, its empty state quoted currentRIs from before the load (the
previous scope's count, or "none are registered yet" on first load) and
nothing rendered it again once the RIs arrived.

Track whether currentRIs belongs to the scope being shown. Until it does,
or when the RI list failed to load, the empty state says only "No reshape
recommendations right now." After the AWS Promise.all, re-render the
empty state for the same generation when the reshape load succeeded with
no rows; a failed reshape load keeps its error message.

Refs LeanerCloud#1135

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD IAmJSD added priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/this-quarter Within the quarter impact/few Limited audience effort/xs Trivial / one-liner 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 2 billable files and costs up to $0.50.

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 61be5b6 and c22674e.

Files selected for processing: 2
  • frontend/src/__tests__/riexchange.test.ts
  • frontend/src/riexchange.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@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 c22674edbd3779ca11e2c49e5ecb4b0cb64801bb

Blocking:

  • none
    Non-blocking:
  • frontend/src/riexchange.ts:160: the corrected count only appears after all three loads settle, including exchange history. A slow history call keeps the neutral "No reshape recommendations right now." up longer than needed. Re-rendering at the end of loadConvertibleRIs when the reshape list is loaded and empty would show it sooner.
    Attacked: Traced every write to currentRIs and currentRIsLoaded (AWS reload, Azure, GCP, the resolve-scope error, loadConvertibleRIs). The flag is only set on a successful load for the current generation. A superseded generation returns before re-rendering. A failed reshape load returns false, so its error isn't overwritten. Promise.all can't reject because each loader catches its own errors. The new tests cover the interleaving: recs before RIs, a failed RI load, and a failed recs load.

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at c22674ed merged onto current main (4950e456; merge-tree 89dd34d8; 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 89dd34d8, main plus 46 PRs) passes npm ci, lint, typecheck, build, jest (1 skipped, 3914 passed, 3915 total), and the full Playwright suite (208 passed).
  • CI is green on c22674ed (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 17:44
@IAmJSD
IAmJSD merged commit 51aa5b1 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 c22674ed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner impact/few Limited audience priority/p3 Polish / idea / may never ship severity/low Minor harm type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(frontend): RI exchange reshape empty state can show a stale RI count

1 participant