Skip to content

[TASK-18803] fix: redirect to onboarding when manteca reports missing fund origin#1762

Merged
jjramirezn merged 3 commits intodevfrom
fix/user-facing-timeout-errors
Mar 18, 2026
Merged

[TASK-18803] fix: redirect to onboarding when manteca reports missing fund origin#1762
jjramirezn merged 3 commits intodevfrom
fix/user-facing-timeout-errors

Conversation

@jjramirezn
Copy link
Contributor

  • Detect onboarding-incomplete errors (fund origin, profile incomplete)
  • Auto-generate onboarding URL and redirect user to complete their profile
  • Return URL set to current page so user lands back in withdraw flow
  • Fallback message if onboarding URL generation fails

- Detect onboarding-incomplete errors (fund origin, profile incomplete)
- Auto-generate onboarding URL and redirect user to complete their profile
- Return URL set to current page so user lands back in withdraw flow
- Fallback message if onboarding URL generation fails
@notion-workspace
Copy link

@vercel
Copy link

vercel bot commented Mar 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Mar 18, 2026 6:56pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Walkthrough

Adds onboarding error handling to the Manteca withdraw flow: a new handleOnboardingError detects onboarding-related errors, calls mantecaApi.initiateOnboarding, redirects on success, or sets a user-visible error on failure. Introduces isRedirectingToOnboarding state and short-circuits existing error paths while updating loading/UI labels.

Changes

Cohort / File(s) Summary
Manteca withdraw UI
src/app/(mobile-ui)/withdraw/manteca/page.tsx
Added handleOnboardingError to detect incomplete onboarding, call mantecaApi.initiateOnboarding, redirect on success, and present fallback error messaging on failure. Introduced isRedirectingToOnboarding state; short-circuited existing error handling in bank-details submission and withdraw-with-signed-tx paths. Updated UI/loading labels to reflect redirection state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding onboarding redirection when Manteca reports missing fund origin during the withdraw flow.
Description check ✅ Passed The description is directly related to the changeset, explaining the onboarding detection, URL generation, return URL handling, and fallback messaging.

✏️ 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 fix/user-facing-timeout-errors
📝 Coding Plan
  • Generate coding plan for human review comments

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

@jjramirezn
Copy link
Contributor Author

@coderabbitai review in depth.

Evaluate:

  • risk of introducing new bugs and breaking existing functionality
  • performance impact and latency
  • code quality and DRY

Also, write a very concise testing guide for QA

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (2)

193-196: Capture onboarding-initiation failures before fallback messaging.

This catch path swallows useful diagnostics. Add exception capture so onboarding URL failures are traceable.

Suggested improvement
-        } catch {
+        } catch (error) {
+            captureException(error)
             setErrorMessage('Please complete your account setup. Go to Settings to update your profile.')
             setIsRedirectingToOnboarding(false)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx around lines 193 - 196, The
catch block currently swallows errors when initiating onboarding; change the
catch to capture the exception (e.g., catch (err) or catch (error)) and surface
it by logging it (console.error or the app logger) and/or append a short error
detail to setErrorMessage so failures to build the onboarding URL are traceable;
keep the existing setIsRedirectingToOnboarding(false) behavior. Target the catch
in the onboarding initiation flow that calls setErrorMessage and
setIsRedirectingToOnboarding.

97-97: isRedirectingToOnboarding is currently write-only.

The state is set/reset but never read, so it does not prevent duplicate actions or provide loading feedback during redirect.

Suggested usage pattern
-    const [isRedirectingToOnboarding, setIsRedirectingToOnboarding] = useState(false)
+    const [isRedirectingToOnboarding, setIsRedirectingToOnboarding] = useState(false)

...
-                        disabled={
+                        disabled={
                             !isCompleteBankDetails ||
                             isDestinationAddressChanging ||
                             !isDestinationAddressValid ||
-                            isLockingPrice
+                            isLockingPrice ||
+                            isRedirectingToOnboarding
                         }
-                        loading={isDestinationAddressChanging || isLockingPrice}
+                        loading={isDestinationAddressChanging || isLockingPrice || isRedirectingToOnboarding}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx at line 97,
isRedirectingToOnboarding is write-only and should be read to prevent duplicate
redirects and show loading UI: update the redirect handler (where
setIsRedirectingToOnboarding(true) is called) to early-return if
isRedirectingToOnboarding is already true, set it back to false on
error/cleanup, and use isRedirectingToOnboarding in the component JSX (e.g.,
disable the submit/button that triggers the redirect and render a spinner or
"Redirecting…" text) so the state both guards the action and provides user
feedback; reference the isRedirectingToOnboarding state and
setIsRedirectingToOnboarding setter and modify the redirect-triggering function
and related button/element rendering accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx:
- Line 239: Onboarding redirect detection is only checking result.error while
user messaging sometimes uses result.message; update the call sites that invoke
handleOnboardingError (e.g., the statements calling
handleOnboardingError(result.error)) to use the same unified source as the
displayed message — for example pass result.error ?? result.message or pass the
whole result and let handleOnboardingError read from result.message as fallback
— so detection and user-facing messaging are consistent (apply the same change
to both call sites where handleOnboardingError is invoked).
- Around line 178-185: The onboarding error detector in handleOnboardingError is
too broad and brittle; update onboardingErrors to match specific phrases and
common backend variants (e.g., 'onboarding_incomplete', 'onboarding incomplete',
'fund origin missing', 'profile incomplete') and implement a normalized-match
before testing (lowercase, replace underscores/dashes with spaces, collapse
extra whitespace), or use word-boundary regex checks (e.g.,
/\bonboarding(?:[_\s-]incomplete)?\b/, /\bfund\s+origin\b/,
/\bprofile\s+incomplete\b/) so only exact/intentional onboarding-related
messages trigger the redirect; keep the function name handleOnboardingError and
the onboardingErrors concept so the change is localized.

---

Nitpick comments:
In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx:
- Around line 193-196: The catch block currently swallows errors when initiating
onboarding; change the catch to capture the exception (e.g., catch (err) or
catch (error)) and surface it by logging it (console.error or the app logger)
and/or append a short error detail to setErrorMessage so failures to build the
onboarding URL are traceable; keep the existing
setIsRedirectingToOnboarding(false) behavior. Target the catch in the onboarding
initiation flow that calls setErrorMessage and setIsRedirectingToOnboarding.
- Line 97: isRedirectingToOnboarding is write-only and should be read to prevent
duplicate redirects and show loading UI: update the redirect handler (where
setIsRedirectingToOnboarding(true) is called) to early-return if
isRedirectingToOnboarding is already true, set it back to false on
error/cleanup, and use isRedirectingToOnboarding in the component JSX (e.g.,
disable the submit/button that triggers the redirect and render a spinner or
"Redirecting…" text) so the state both guards the action and provides user
feedback; reference the isRedirectingToOnboarding state and
setIsRedirectingToOnboarding setter and modify the redirect-triggering function
and related button/element rendering accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 949d3fc3-cdbe-4340-98e9-bc435fdbf41b

📥 Commits

Reviewing files that changed from the base of the PR and between f566e72 and 46935f0.

📒 Files selected for processing (1)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and changed files
gh pr view 1762 --repo peanutprotocol/peanut-ui --json files,additions,deletions,title,body

echo "---DIFF---"
gh pr diff 1762 --repo peanutprotocol/peanut-ui

Length of output: 3404


🏁 Script executed:

#!/bin/bash
# Read the full file
cat src/app/\(mobile-ui\)/withdraw/manteca/page.tsx

echo "---MANTECA API---"
# Find mantecaApi definition and initiateOnboarding
rg -n "initiateOnboarding" --type ts --type tsx -A5 -B5

echo "---MANTECA API FILE---"
fd "manteca" --type f -e ts -e tsx | head -20

Length of output: 36410


🏁 Script executed:

#!/bin/bash
cat src/services/manteca.ts

Length of output: 14495


@jjramirezn, here's the in-depth review across all three dimensions plus a QA guide:


🐛 Risk of Introducing Bugs

1. Dead state — isRedirectingToOnboarding never used in JSX (High)

setIsRedirectingToOnboarding(true) is set before the async redirect but the state is never read in the render tree. This means:

  • No button is disabled, no spinner is shown.
  • A user in a slow network could click "Withdraw" or "Review" again while the redirect is in flight, triggering a second handleOnboardingError call and a second initiateOnboarding API request.
  • Either wire the state into UI (disable the submit button / show a full-screen loading overlay) or remove it.
// Example fix: disable the submit button while redirecting
<Button
    onClick={handleBankDetailsSubmit}
    disabled={!isCompleteBankDetails || isLockingPrice || isRedirectingToOnboarding}
    loading={isLockingPrice || isRedirectingToOnboarding}
>
    {isRedirectingToOnboarding ? 'Redirecting...' : isLockingPrice ? 'Locking rate...' : 'Review'}
</Button>

2. Missing dependency in handleBankDetailsSubmit useCallback (Medium)

handleOnboardingError is called inside handleBankDetailsSubmit but is absent from its dependency array. While it's stable at runtime (its own deps are []), this is an exhaustive-deps lint violation that can mask future breakage if deps of handleOnboardingError ever change.

 }, [
     selectedBank, accountType, destinationAddress,
     countryConfig?.needsBankCode, countryConfig?.needsAccountType,
     usdAmount, currencyCode, currencyAmount,
-    isUserMantecaKycApproved, isLockingPrice,
+    isUserMantecaKycApproved, isLockingPrice, handleOnboardingError,
 ])

3. Overly broad keyword matching (Medium)

'onboarding' as a substring match is very permissive. Any future error message containing that word (e.g. "Error during onboarding service timeout") would silently trigger a redirect instead of showing the user a proper error.

// Current — too broad
const onboardingErrors = ['fund origin', 'onboarding', 'profile incomplete']

// Safer — use more specific phrases matching actual Manteca API responses
const ONBOARDING_ERROR_KEYWORDS = ['fund origin missing', 'profile incomplete', 'onboarding required'] as const

Move this to a module-level constant to avoid re-creating the array on every call.

4. No timeout on initiateOnboarding (Low)

If the API call hangs indefinitely, isRedirectingToOnboarding stays true forever with no user feedback (and right now, no UI change at all — see point #1). Consider adding an AbortController timeout or at least a fallback timeout.


⚡ Performance Impact & Latency

The changes are on the error path only — no impact on the happy path. The extra initiateOnboarding API call is made only after Manteca returns an error, which is acceptable. No concerns here beyond the missing timeout noted above.


🧹 Code Quality & DRY

5. Missing analytics event (Medium)

Both WITHDRAW_FAILED PostHog events are captured before calling handleOnboardingError, but there's no separate event for the redirect itself. This limits production observability of how often this code path is hit. Consider adding:

posthog.capture(ANALYTICS_EVENTS.WITHDRAW_ONBOARDING_REDIRECT, {
    method_type: 'manteca',
    error_trigger: error,
})

6. onboardingErrors array recreated on every invocation (Low)

The array is defined inside the useCallback body. Even though the callback itself is stable, each call allocates a new array. Extract it to module scope:

// At module level, outside the component
const MANTECA_ONBOARDING_ERROR_KEYWORDS = ['fund origin', 'onboarding', 'profile incomplete'] as const

✅ What's Working Well

  • Control flow is correct: return true in both branches (success redirect and catch fallback) causes callers to return early, preventing double setErrorMessage calls.
  • finally blocks in both handleBankDetailsSubmit and handleWithdraw still execute after return, so setIsLockingPrice(false) and setLoadingState('Idle') are not skipped.
  • window.location.href is the right primitive for a hard redirect — React state updates after it are irrelevant.
  • initiateOnboarding API correctly passes returnUrl: window.location.href so the user lands back in the withdraw flow.

🧪 QA Testing Guide

Prerequisites: An account with incomplete Manteca onboarding (missing fund origin / incomplete profile).

# Scenario Steps Expected Result
1 Redirect on initiateWithdraw error Fill amount + bank details → tap "Review" with an account that triggers a fund origin error App calls initiateOnboarding, then redirects to the onboarding URL; after completing onboarding, browser returns to the withdraw page
2 Redirect on withdrawWithSignedTx error Complete all steps through "Review" → tap "Withdraw" with an account triggering a profile incomplete error Same redirect behaviour as above; loading state clears (setLoadingState('Idle') via finally)
3 Fallback message on initiateOnboarding failure Mock / block the initiateOnboarding endpoint → trigger an onboarding error Error message "Please complete your account setup. Go to Settings to update your profile." appears; user stays on page; no spinner stuck
4 Non-onboarding error unaffected Trigger a TAX_ID_MISMATCH or CUIT_MISMATCH error Normal user-friendly error message shown; no redirect
5 Happy path regression Complete a full withdraw with a fully onboarded account No change in behaviour; success screen shown normally
6 Return URL After completing onboarding redirect (scenario 1/2), check the URL the onboarding flow returns to Should be the exact withdraw page URL (with method/country params)

🧠 Learnings used
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 852
File: src/hooks/useTransactionHistory.ts:177-239
Timestamp: 2025-05-14T21:16:06.369Z
Learning: When reviewing code, pay attention to and resolve CodeRabbit comments as they can contain useful refactoring suggestions and insights.

Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 594
File: src/components/Request/Pay/Views/Initial.view.tsx:188-213
Timestamp: 2024-12-31T15:08:15.640Z
Learning: Always resolve coderabbit comments when reviewing code, since they can contain useful suggestions.

- Tighten error matching (remove broad 'onboarding', use 'onboarding required')
- Add handleOnboardingError to useCallback deps
- Wire isRedirectingToOnboarding into button (disabled + loading + 'Redirecting...')
- Check result.message in withdraw path for onboarding errors
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (1)

758-766: ⚠️ Potential issue | 🟡 Minor

Review step button missing redirect state handling.

Unlike the bank details step button (lines 696-707), this button doesn't account for isRedirectingToOnboarding. If an onboarding error occurs during handleWithdraw, the finally block sets loadingState to 'Idle' before the page navigates away, briefly leaving the button enabled and clickable.

For consistency, consider adding isRedirectingToOnboarding to the disabled and loading checks:

Suggested fix
                     <Button
                         icon="arrow-up"
                         onClick={handleWithdraw}
-                        loading={isLoading}
-                        disabled={!!errorMessage || isLoading}
+                        loading={isLoading || isRedirectingToOnboarding}
+                        disabled={!!errorMessage || isLoading || isRedirectingToOnboarding}
                         shadowSize="4"
                     >
-                        {isLoading ? loadingState : 'Withdraw'}
+                        {isRedirectingToOnboarding ? 'Redirecting...' : isLoading ? loadingState : 'Withdraw'}
                     </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx around lines 758 - 766, The
Withdraw step button currently doesn't consider isRedirectingToOnboarding, so
when handleWithdraw triggers an onboarding redirect the finally block resets
loadingState and briefly makes the button clickable; update the Button rendering
(the component using props icon, onClick={handleWithdraw}, loading={isLoading},
disabled={!!errorMessage || isLoading}, and children {isLoading ? loadingState :
'Withdraw'}) to include isRedirectingToOnboarding in both loading and disabled
checks (e.g., loading={isLoading || isRedirectingToOnboarding} and
disabled={!!errorMessage || isLoading || isRedirectingToOnboarding}) and ensure
the displayed label also reflects the combined state (use the combined loading
flag when selecting loadingState vs 'Withdraw') so the button stays
disabled/shows loading while redirecting.
🧹 Nitpick comments (1)
src/app/(mobile-ui)/withdraw/manteca/page.tsx (1)

196-199: Consider capturing the error for observability.

The catch block discards error details. While the user sees a fallback message, logging or capturing the exception would help diagnose why initiateOnboarding failed.

Suggested improvement
-        } catch {
+        } catch (err) {
+            captureException(err)
             setErrorMessage('Please complete your account setup. Go to Settings to update your profile.')
             setIsRedirectingToOnboarding(false)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx around lines 196 - 199, The
catch block that handles failures from initiateOnboarding currently swallows the
exception; update it to capture and record the error (e.g., catch (err) { ... })
and forward the error to your observability tooling (console.error or
app-specific logger / Sentry via captureException) while preserving the existing
UI behavior that calls setErrorMessage and setIsRedirectingToOnboarding; locate
the try/catch around initiateOnboarding in page.tsx and add the error reporting
there.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx:
- Around line 758-766: The Withdraw step button currently doesn't consider
isRedirectingToOnboarding, so when handleWithdraw triggers an onboarding
redirect the finally block resets loadingState and briefly makes the button
clickable; update the Button rendering (the component using props icon,
onClick={handleWithdraw}, loading={isLoading}, disabled={!!errorMessage ||
isLoading}, and children {isLoading ? loadingState : 'Withdraw'}) to include
isRedirectingToOnboarding in both loading and disabled checks (e.g.,
loading={isLoading || isRedirectingToOnboarding} and disabled={!!errorMessage ||
isLoading || isRedirectingToOnboarding}) and ensure the displayed label also
reflects the combined state (use the combined loading flag when selecting
loadingState vs 'Withdraw') so the button stays disabled/shows loading while
redirecting.

---

Nitpick comments:
In `@src/app/`(mobile-ui)/withdraw/manteca/page.tsx:
- Around line 196-199: The catch block that handles failures from
initiateOnboarding currently swallows the exception; update it to capture and
record the error (e.g., catch (err) { ... }) and forward the error to your
observability tooling (console.error or app-specific logger / Sentry via
captureException) while preserving the existing UI behavior that calls
setErrorMessage and setIsRedirectingToOnboarding; locate the try/catch around
initiateOnboarding in page.tsx and add the error reporting there.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b505293-8b15-4e45-8feb-c196088ae864

📥 Commits

Reviewing files that changed from the base of the PR and between 5b07078 and 5997aca.

📒 Files selected for processing (1)
  • src/app/(mobile-ui)/withdraw/manteca/page.tsx

@jjramirezn jjramirezn merged commit 29e61ac into dev Mar 18, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant