Repository navigation
fix(frontend): show RI exchange failure reasons, stop double-escaping - #1348
Conversation
…ing picker labels Two of the three 2026-09-02 audit findings for riexchange.ts: - A12-069: a failed exchange's error (set by FailRIExchange) was never rendered, so the history row showed only a "failed" badge and the operator had to read backend logs to see why. Show the escaped reason under the badge, capped in width so a long AWS error wraps instead of squeezing the other columns. - A12-067: target-picker option labels were passed through escapeHtml and then assigned to textContent, which escapes on its own, so any &, < or quote rendered as an entity. Assign the raw strings. A12-066 (the "type a UUID" copy) is left to the dedicated issue LeanerCloud#740. Refs LeanerCloud#321 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 3 billable files and costs up to $0.75. Or wait 59 minutes for your next included review. View limit details
Comment |
Conflicts: - frontend/src/riexchange.ts (Cost Explorer alternative option label in openExchangeModal): kept main's formatOfferingCost(alt) currency-aware cost (LeanerCloud#1343) and LeanerCloud#321's raw instance_type assigned to textContent (main still wrapped it in escapeHtml, which double-escapes in textContent). - frontend/src/riexchange.ts (renderExchangeHistory row): kept main's em dash for an empty payment_due (LeanerCloud#1343) and LeanerCloud#321's errorLine under the status badge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): APPROVE Blocking:
|
|
Local verification at
|
|
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 |
Conflicts: - frontend/src/plans.ts: imports kept both (status-badge + main's purchase-row-guards). Planned-purchase status cell keeps main's LeanerCloud#181 layout (.status-badges wrapper, Overdue badge, .overdue-hint) with this branch's statusBadgeHtml(status) as the status pill; the Overdue badge is now badgeHtml('danger', 'Overdue', ...) instead of the retired .status-badge class. - frontend/src/riexchange.ts: imports kept both (skeletonText from main + statusBadgeHtml). History status cell uses statusBadgeHtml(rec.status) followed by main's LeanerCloud#1348 error line (.status-detail). - frontend/src/history.ts: imports kept both (status-badge + main's local-date). Semantic fixes in this merge: - __tests__/riexchange-audit-321.test.ts (main): expects the shared .badge.badge-danger with "Failed" instead of .status-badge "failed". - __tests__/css.test.ts: the retired-variant check is now /\.status-badge\b/ so main's LeanerCloud#181 .status-badges wrapper rule does not trip it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed RI exchange showed only a red
failedbadge in Exchange History, so operators had to read backend logs to find out why no money was spent. The target picker's option labels were also escaped twice, so any&,<or quote rendered as an entity. This PR shows the stored AWS error under the badge and assigns the raw labels. The third finding, A12-066 (the "type a UUID" copy with no UUID field), is already fixed on main by #1377.Closes #321
What changed
frontend/src/riexchange.ts:renderExchangeHistoryadds a<div class="status-detail">with the escapedrec.errorunder the status badge whenever the record has an error (A12-069).frontend/src/riexchange.ts: the AWS-offering and Cost Explorer alternative option labels inopenExchangeModalnow assign the raw strings totextContent; bothescapeHtmlcalls are removed (A12-067).frontend/src/styles/components.css:.status-detailshares the muted small subline style of.history-approver(the line Purchase History shows under its status badges) and addsmax-width: 18remandoverflow-wrap: anywhereso long AWS errors wrap.frontend/src/__tests__/riexchange-audit-321.test.ts.Why this approach
titletooltip (the issue's other option): tooltips are hard to discover, unreachable by keyboard and invisible on touch screens. It also matches how Purchase History shows a failed execution's error under its badge.escapeHtmlbecause the history table is built as an HTML string.riexchange.test.ts(about 1400 lines).Screenshots
Screenshots were taken before this branch was merged with main; before = main at a14dc8f. After is this branch. Both are headless Chromium at 1440x900 against the repo's mocked e2e API.
Exchange History with one failed record carrying an AWS error and one completed record. Before: the failed row shows only a
failedbadge, with no reason. After: the AWS error (AcceptReservedInstancesExchangeQuote: InvalidParameterValue: ...) appears under the badge and wraps without squeezing the other columns.Verification
Run on this branch's head
38bd2768:frontend/src/riexchange.tsresolved, see the merge commit.npm run lint: 0 errors. Typecheck and build pass.riexchange-audit-321.test.ts(4 tests): AWS offering label keeps&, quotes and<>with no&; CE alternative label is not pre-escaped; a failed record shows its error as text (markup in it is not rendered) under thefailedbadge; a record without an error gets no detail line. 3 of the 4 fail on main. Before the merge, the file passed with--randomize.e2e-local.sh): 200 passed.scripts/test-git-secrets-allowlist.sh: 137 PASS.Notes for review
&,<or quotes, so the double escaping only shows with made-up data. Jest covers it.Overlaps
statusBadgeHtml(rec.status): expect a one-line conflict; keep its badge and appenderrorLine.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