fix(ui): harden 4 dead-button / silent-no-op handlers (card-setup lockout, KYC region verify, cancel-deposit, ToS confirm)#2229
fix(ui): harden 4 dead-button / silent-no-op handlers (card-setup lockout, KYC region verify, cancel-deposit, ToS confirm)#2229Hugo0 wants to merge 2 commits into
Conversation
Same class as the withdraw Continue throw + Send card: handlers whose failure
path produces no visible change on a control the user can't escape. From the
dead-button audit, the 4 remaining actionable ones:
- HIGH EnableAutoBalanceBanner ('Finish setting up your card'): the modal is
preventClose + hideModalCloseButton and 'void grant()' discarded the result,
so a cancelled/failed passkey (common on iOS/1Password) trapped the user with
a CTA that did nothing. Now surfaces the error and adds a 'Skip for now'
escape once a grant fails (the grant re-prompts on first card spend anyway).
+ tests.
- HIGH UnlockedRegions ('verify now' → nothing happens; customer-reported by
Federico): setSelectedRegion(null) closed the modal before the await, then any
initiate error landed in an off-screen inline <p>. Now a dismissible error
modal (Try again + Contact support) surfaces flow.error.
- MED CancelDepositActions: cancel failures were only console.error'd, so users
thought a deposit was cancelled when it wasn't. Added an error state + alert.
- MED useMultiPhaseKycFlow ToS confirm: the await had no try/catch, freezing the
'preparing' modal ~30s with no feedback. Wrapped it; on failure surface the
recovery state immediately (mirrors BridgeTosStep).
#5 (payQR non-MANTECA no-op) left as-is: currently unreachable, adding handling
to an impossible path is the opposite anti-pattern. Documented only.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughFour components receive error-handling improvements: ChangesError Handling UX Improvements
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Comment |
Code-analysis diffPainscore total: 5797.23 → 5798.91 (+1.68) 🆕 New findings (16)
✅ Resolved (16)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/TransactionDetails/provider-actions/CancelDepositActions.tsx (1)
126-137:⚠️ Potential issue | 🟠 MajorAvoid redundant bridge cancel calls on retry
The REQUEST flow (lines 126–136) executes
cancelOnramp(bridgeTransferId)thenchargesApi.cancel(transaction.id). If the charge cancel fails,wrapActioncatches the error and shows the user "Please try again." On retry, the entire sequence runs again, re-executingcancelOnrampeven if it already succeeded on the first attempt.While REST DELETE endpoints are conventionally idempotent, this creates unnecessary API overhead and obscures which operation failed on retry. Consider tracking or checkpointing that the bridge cancellation succeeded before attempting the charge cancellation, so retries skip the already-completed bridge step.
🤖 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 `@src/components/TransactionDetails/provider-actions/CancelDepositActions.tsx` around lines 126 - 137, The wrapAction callback in the REQUEST flow is executing cancelOnramp(bridgeTransferId) unconditionally every time the action runs, including on retries. If chargesApi.cancel fails and the user retries, cancelOnramp will be called again even though it likely already succeeded on the first attempt. Refactor the code to track or checkpoint that the bridge cancellation has already completed successfully, then conditionally skip the cancelOnramp call on subsequent retries. This avoids redundant API calls and makes it clearer which operation actually failed during the retry.
🤖 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.
Outside diff comments:
In `@src/components/TransactionDetails/provider-actions/CancelDepositActions.tsx`:
- Around line 126-137: The wrapAction callback in the REQUEST flow is executing
cancelOnramp(bridgeTransferId) unconditionally every time the action runs,
including on retries. If chargesApi.cancel fails and the user retries,
cancelOnramp will be called again even though it likely already succeeded on the
first attempt. Refactor the code to track or checkpoint that the bridge
cancellation has already completed successfully, then conditionally skip the
cancelOnramp call on subsequent retries. This avoids redundant API calls and
makes it clearer which operation actually failed during the retry.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e7494110-3955-4ad8-a270-bc123165c388
📒 Files selected for processing (5)
src/components/Home/EnableAutoBalanceBanner.tsxsrc/components/Home/__tests__/EnableAutoBalanceBanner.test.tsxsrc/components/Profile/views/UnlockedRegions.view.tsxsrc/components/TransactionDetails/provider-actions/CancelDepositActions.tsxsrc/hooks/useMultiPhaseKycFlow.ts
Code-review catch: a ROW (rest-of-world) region has no provider/rail, so an initiate returns a terminal 'not available in your region yet'. The new error modal was showing 'Verification couldn't start' + 'Try again', which re-issues the identical ROW request and loops. Gate retriability on the region's provider: terminal ROW → neutral title + 'Got it'; transient → 'Try again' + support.
Follow-up to the withdraw + Send dead-button fixes. The dead-button audit's 4 remaining actionable findings — handlers whose failure path leaves an unresponsive control with no feedback.
EnableAutoBalanceBannervoid grant()discards error → cancelled/failed passkey traps the userUnlockedRegions<p>→ 'nothing happens' (customer-reported, Federico; blocks EU onboarding)flow.errorCancelDepositActionsconsole.error'd → user thinks deposit cancelled when it isn'tErrorAlertuseMultiPhaseKycFlowBridgeTosStep)Not fixed:
payQRnon-MANTECA no-op — currently unreachable (the pay screen only renders for MANTECA); adding handling to an impossible path is the opposite anti-pattern. Documented only.Risk: low — all changes are additive error-handling on failure paths; happy paths unchanged. Verified: prettier ✓, typecheck ✓, eslint not-worsened (0 new errors), unit tests pass.