Skip to content

[TASK-14554]feat: multi-step bank account form#1286

Closed
Zishan-7 wants to merge 3 commits intopeanut-wallet-devfrom
feat/multi-step-bank-acc-form
Closed

[TASK-14554]feat: multi-step bank account form#1286
Zishan-7 wants to merge 3 commits intopeanut-wallet-devfrom
feat/multi-step-bank-acc-form

Conversation

@Zishan-7
Copy link
Contributor

@Zishan-7 Zishan-7 commented Oct 6, 2025

image image

@notion-workspace
Copy link

@vercel
Copy link

vercel bot commented Oct 6, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Canceled Canceled Oct 7, 2025 1:35pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

Replaces DynamicBankAccountForm with a new MultiStepBankAccountForm in consumers and adds a MultiStepBankAccountForm module (components, steps, types, stepConfig, and barrel) implementing a three-step, account-type-aware bank details flow with validation and debounced IBAN/BIC handling.

Changes

Cohort / File(s) Summary
Consumer swap: use MultiStepBankAccountForm
src/components/AddWithdraw/AddWithdrawCountriesList.tsx, src/components/Claim/Link/views/BankFlowManager.view.tsx
Replace DynamicBankAccountForm import/usage with MultiStepBankAccountForm; update IBankAccountDetails import source; remove obsolete imports; preserve refs, onSuccess, initialData, and error wiring.
Multi-step form core & public API
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx, src/components/MultiStepBankAccountForm/index.ts, src/components/MultiStepBankAccountForm/types.ts, src/components/MultiStepBankAccountForm/stepConfig.ts
Add MultiStepBankAccountForm (forwardRef with handleSubmit), re-export types (IBankAccountDetails, AccountType), implement account-type detection and step configuration, and add a barrel export.
Form primitives / UI
src/components/MultiStepBankAccountForm/FormInput.tsx, src/components/MultiStepBankAccountForm/StepIndicator.tsx
Add controlled FormInput (trims on blur, integrates with react-hook-form, shows field errors) and StepIndicator (visual step dots).
Step implementations
src/components/MultiStepBankAccountForm/steps/PersonalInfoStep.tsx, src/components/MultiStepBankAccountForm/steps/AccountDetailsStep.tsx, src/components/MultiStepBankAccountForm/steps/AddressStep.tsx, src/components/MultiStepBankAccountForm/steps/index.ts
Add step components: PersonalInfo, AccountDetails (IBAN/BIC with debounced validation and auto-BIC fetch, US routing, MX CLABE), Address; export via steps barrel.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • kushagrasarathe
  • jjramirezn

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title “[TASK-14554]feat: multi-step bank account form” succinctly captures the primary change of introducing the multi-step bank account form, aligns with conventional commit style, and directly reflects the main feature added in the PR.
Description Check ✅ Passed The description consists of two attached screenshots showing the new multi-step bank account form UI, which directly relates to the changeset introducing that form and replacing the previous implementation.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/multi-step-bank-acc-form

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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: 8

🧹 Nitpick comments (4)
src/components/MultiStepBankAccountForm/steps/AddressStep.tsx (2)

32-37: Consider country-specific validation for state field.

Not all countries have states or administrative divisions. For some countries, this field might not be applicable. Consider making this field conditionally required based on the country, or providing guidance on what users should enter if their country doesn't have states.


40-46: Consider postal code format validation.

Postal codes have different formats across countries (e.g., US: 5 digits, UK: alphanumeric, Canada: alphanumeric with space). The current required-only validation doesn't enforce format rules, which could lead to invalid data being submitted.

Consider adding country-specific validation patterns:

 <FormInput
     name="postalCode"
     placeholder="Your Postal Code"
-    rules={{ required: 'Postal code is required' }}
+    rules={{
+        required: 'Postal code is required',
+        pattern: {
+            value: getPostalCodePattern(country),
+            message: 'Invalid postal code format'
+        }
+    }}
     control={control}
     errors={errors}
     touchedFields={touchedFields}
 />
src/components/AddWithdraw/AddWithdrawCountriesList.tsx (1)

241-241: Consider using undefined instead of null for error prop.

TypeScript and React typically prefer undefined over null for optional values. The prop type should be checked to confirm what's expected.

-                    error={null}
+                    error={undefined}
src/components/MultiStepBankAccountForm/StepIndicator.tsx (1)

10-26: Add accessibility features for screen readers.

The step indicators lack semantic meaning and ARIA labels, making them inaccessible to screen reader users. Consider adding role="progressbar" or aria-label attributes.

Apply this diff to improve accessibility:

 const StepIndicator: React.FC<StepIndicatorProps> = ({ currentStep, stepConfig }) => {
     return (
-        <div className="flex items-center justify-center space-x-2">
+        <div 
+            className="flex items-center justify-center space-x-2"
+            role="progressbar"
+            aria-label={`Step ${currentStep} of ${stepConfig.totalSteps}`}
+            aria-valuenow={currentStep}
+            aria-valuemin={1}
+            aria-valuemax={stepConfig.totalSteps}
+        >
             {Array.from({ length: stepConfig.totalSteps }, (_, index) => {
                 const stepNumber = index + 1
                 const isCurrent = currentStep === stepNumber

                 return (
                     <div
                         key={stepNumber}
                         className={twMerge('size-2 rounded-full bg-grey-2', isCurrent && 'bg-primary-1')}
+                        aria-label={`Step ${stepNumber}${isCurrent ? ' (current)' : ''}`}
                     ></div>
                 )
             })}
         </div>
     )
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8da32 and e28c9b2.

📒 Files selected for processing (12)
  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx (2 hunks)
  • src/components/Claim/Link/views/BankFlowManager.view.tsx (2 hunks)
  • src/components/MultiStepBankAccountForm/FormInput.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/StepIndicator.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/index.ts (1 hunks)
  • src/components/MultiStepBankAccountForm/stepConfig.ts (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/AccountDetailsStep.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/AddressStep.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/PersonalInfoStep.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/index.ts (1 hunks)
  • src/components/MultiStepBankAccountForm/types.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".

Applied to files:

  • src/components/AddWithdraw/AddWithdrawCountriesList.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/components/MultiStepBankAccountForm/stepConfig.ts
🧬 Code graph analysis (10)
src/components/MultiStepBankAccountForm/steps/AccountDetailsStep.tsx (4)
src/components/MultiStepBankAccountForm/types.ts (2)
  • IBankAccountDetails (1-16)
  • AccountType (18-18)
src/utils/withdraw.utils.ts (3)
  • validateMXCLabeAccount (82-134)
  • getCountryFromIban (8-27)
  • validateUSBankAccount (34-74)
src/utils/bridge-accounts.utils.ts (3)
  • validateIban (36-38)
  • validateBic (73-89)
  • isValidRoutingNumber (91-118)
src/app/actions/ibanToBic.ts (1)
  • getBicFromIban (10-17)
src/components/MultiStepBankAccountForm/steps/PersonalInfoStep.tsx (2)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (1)
  • IBankAccountDetails (312-312)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (1-16)
src/components/MultiStepBankAccountForm/steps/AddressStep.tsx (1)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (1-16)
src/components/AddWithdraw/AddWithdrawCountriesList.tsx (1)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (1)
  • MultiStepBankAccountForm (36-307)
src/components/MultiStepBankAccountForm/StepIndicator.tsx (1)
src/components/MultiStepBankAccountForm/types.ts (1)
  • StepConfig (20-24)
src/components/MultiStepBankAccountForm/FormInput.tsx (1)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (1-16)
src/components/MultiStepBankAccountForm/types.ts (1)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (2)
  • IBankAccountDetails (312-312)
  • AccountType (312-312)
src/components/MultiStepBankAccountForm/stepConfig.ts (2)
src/components/AddMoney/consts/index.ts (1)
  • BRIDGE_ALPHA3_TO_ALPHA2 (2487-2529)
src/components/MultiStepBankAccountForm/types.ts (3)
  • AccountType (18-18)
  • StepConfig (20-24)
  • IBankAccountDetails (1-16)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (12)
src/app/actions/types/users.types.ts (1)
  • AddBankAccountPayload (36-56)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (1-16)
src/components/Global/PeanutActionDetailsCard/index.tsx (2)
  • PeanutActionDetailsCardProps (29-52)
  • PeanutActionDetailsCard (54-253)
src/components/MultiStepBankAccountForm/stepConfig.ts (2)
  • getAccountType (8-13)
  • getStepConfig (15-105)
src/context/authContext.tsx (1)
  • useAuth (183-189)
src/redux/hooks.ts (2)
  • useAppDispatch (5-5)
  • useAppSelector (6-6)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/hooks/useSavedAccounts.tsx (1)
  • useSavedAccounts (12-25)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/redux/slices/bank-form-slice.ts (1)
  • bankFormActions (36-36)
src/components/AddMoney/consts/index.ts (1)
  • ALL_COUNTRIES_ALPHA3_TO_ALPHA2 (2537-2540)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN_SYMBOL (21-21)
src/components/Claim/Link/views/BankFlowManager.view.tsx (1)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (1)
  • MultiStepBankAccountForm (36-307)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (4)
src/components/MultiStepBankAccountForm/FormInput.tsx (1)

41-50: LGTM! Good async handling and trimming logic.

The blur handler correctly:

  • Trims string values with a type guard
  • Calls the field's onBlur
  • Awaits the custom onBlur callback if provided

This allows for debounced async validation patterns (like BIC validation) while maintaining proper form state.

src/components/MultiStepBankAccountForm/index.ts (1)

1-6: LGTM! Clean barrel export pattern.

The barrel export properly consolidates the public API surface, making imports simpler for consumers.

src/components/MultiStepBankAccountForm/steps/index.ts (1)

1-3: LGTM! Clean step exports.

Properly consolidates step components for easier importing.

src/components/AddWithdraw/AddWithdrawCountriesList.tsx (1)

236-242: initialData and error props are correctly used
Passing initialData={{}} prevents a runtime error when spreading and error={null} matches the string | null prop type. No changes required.

Copy link
Contributor

@kushagrasarathe kushagrasarathe left a comment

Choose a reason for hiding this comment

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

@Zishan-7 left comments, requesting changes, mostly nits, some points:

  • lots of new code, but good abstraction
  • can use comments in components and logic for quick understanding
  • lets not use any
  • BIG concern on testing effort given time constraint
  • resolve cr/bugbot comments

const titles = [
'Who will receive the funds?',
'Where should we send the money?',
'Where this bank is located?',
Copy link
Contributor

Choose a reason for hiding this comment

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

blocking: its not the bank address, its the users address that we need to take, copy needs to be updated everywhere/anywhere relevant

Copy link
Contributor

Choose a reason for hiding this comment

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

good comment

onBlur?: (field: any) => Promise<void> | void
}

const FormInput: React.FC<FormInputProps> = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: i like this component

nit: lets add comments and not use any

touchedFields: Partial<Record<keyof IBankAccountDetails, boolean>>
}

const AddressStep: React.FC<AddressStepProps> = ({ control, errors, touchedFields }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

@vercel vercel bot temporarily deployed to Preview – peanut-wallet October 7, 2025 13:35 Inactive
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: 0

♻️ Duplicate comments (7)
src/components/MultiStepBankAccountForm/stepConfig.ts (7)

17-22: [Duplicate] Fix inconsistent country code format and unsafe fallback.

This issue was flagged in previous reviews: the function mixes alpha-2 ('MX') and alpha-3 ('USA') codes inconsistently, and the fallback to 'IBAN' on line 21 is unsafe as it will incorrectly classify unknown countries.

As mentioned by kushagrasarathe, this is a valid concern that needs addressing. Consider normalizing input to accept both alpha-2 and alpha-3 forms, and either use explicit IBAN country validation (via a Set of known IBAN countries) or throw an error for unsupported countries instead of defaulting to 'IBAN'.


77-77: Fix misleading copy: this is the user's address, not the bank's address.

Same issue as line 32 - this should ask for the user's address, not the bank's location.

Apply this diff:

-                    'Where this bank is located?',
+                    'What is your address?',

32-32: Fix misleading copy: this is the user's address, not the bank's address.

As kushagrasarathe noted, the copy needs updating. This step asks for the user's residential address, not the bank's location.

Apply this diff:

-                    'Where this bank is located?',
+                    'What is your address?',

The same issue exists on line 77 for the MX flow.


17-22: Address the unsafe IBAN fallback flagged in previous review.

The unsafe fallback to 'IBAN' for unknown countries remains unresolved from the previous review, where it was flagged as a critical issue. This will incorrectly classify unsupported countries as IBAN-compatible.

As recommended in the previous review, either:

  1. Validate against an explicit IBAN country set, or
  2. Throw an error for unsupported countries

This ensures callers handle unsupported countries explicitly rather than silently misclassifying them.


17-22: Fix unsafe default to IBAN for unknown countries.

The function defaults all unknown countries to 'IBAN', which is incorrect. Countries like Japan, Brazil, India, and others don't use IBAN. This will cause downstream issues when building the payload.

As previously suggested, use an explicit IBAN country set:

+const IBAN_COUNTRIES = new Set([
+    'AUT', 'BEL', 'BGR', 'HRV', 'CYP', 'CZE', 'DNK', 'EST', 'FIN', 'FRA',
+    'DEU', 'GRC', 'HUN', 'ISL', 'IRL', 'ITA', 'LVA', 'LIE', 'LTU', 'LUX',
+    'MLT', 'NLD', 'NOR', 'POL', 'PRT', 'ROU', 'SVK', 'SVN', 'ESP', 'SWE',
+    'CHE', 'GBR'
+])
+
 export const getAccountType = (country: string): AccountType => {
     const upperCountry = country.toUpperCase()
     if (upperCountry === 'USA') return 'US'
     if (upperCountry === 'MX' || upperCountry === 'MEX') return 'MX'
-    return 'IBAN'
+    if (IBAN_COUNTRIES.has(upperCountry)) return 'IBAN'
+    throw new Error(`Unsupported country code: ${country}`)
 }

28-34: Update misleading step title.

The title "Where this bank is located?" is incorrect—this step collects the user's address, not the bank's address.

Apply this diff:

                 const titles = [
                     'Who will receive the funds?',
                     'Where should we send the money?',
-                    'Where this bank is located?',
+                    'What is your address?',
                 ]

73-79: Update misleading step title (MX flow).

Same issue as the US flow—this collects the user's address, not the bank's location.

Apply this diff:

                 const titles = [
                     'Who will receive the funds?',
                     'Where should we send the money?',
-                    'Where this bank is located?',
+                    'What is your address?',
                 ]
🧹 Nitpick comments (12)
src/components/MultiStepBankAccountForm/stepConfig.ts (2)

4-96: Add documentation comments for better maintainability.

As requested in previous review, the file needs comments to understand the use and flow. Consider adding JSDoc comments for:

  • The purpose of each validator function (lines 5-15)
  • The getAccountType function's behavior and expected inputs
  • The getStepConfig function and the StepConfig pattern
  • Each account type configuration explaining when it's used

Example:

/**
 * Validates that required personal information fields are present and error-free.
 * @param data - The form data to validate
 * @param errors - React Hook Form errors object
 * @returns true if firstName and lastName are valid
 */
const validatePersonalInfo = (data: IBankAccountDetails, errors: FieldErrors<IBankAccountDetails>): boolean => {

24-96: Add documentation comments for clarity.

Previous feedback requested comments to understand the use and flow of this configuration system. Consider adding JSDoc comments explaining:

  • The purpose of getStepConfig
  • How the step numbering works (1-indexed)
  • What each account type's flow entails

Additionally, the step titles for US and MX are identical (lines 29-33, 74-78). Consider extracting to a shared constant:

const THREE_STEP_TITLES = [
    'Who will receive the funds?',
    'Where should we send the money?',
    'Where this bank is located?',
] as const

Then use it in both configs:

 US: {
     totalSteps: 3,
     getStepTitle: (step: number) => {
-        const titles = [
-            'Who will receive the funds?',
-            'Where should we send the money?',
-            'Where this bank is located?',
-        ]
-        return titles[step - 1] || ''
+        return THREE_STEP_TITLES[step - 1] || ''
     },
src/components/MultiStepBankAccountForm/FormInput.tsx (3)

7-16: Excellent type safety improvements!

The any types have been properly replaced with react-hook-form types (RegisterOptions and ControllerRenderProps). This provides full type safety and autocomplete support.

As kushagrasarathe noted, this component looks good. Consider adding JSDoc comments to explain:

  • The auto-trim behavior on blur
  • When to use the optional onBlur callback
  • The purpose of touchedFields in error display logic

7-16: Good fix of type safety issues.

The interface now uses proper react-hook-form types (RegisterOptions, ControllerRenderProps) instead of any, successfully addressing the previous review concern.

Based on learnings: The codebase follows a strict policy of avoiding any types.

Optionally, consider adding JSDoc comments to document the props, especially onBlur which has a specific use case (debounced validation for IBAN/BIC fields).


18-60: Consider adding component documentation.

Previous feedback requested comments for this component. Consider adding a JSDoc comment explaining:

  • The component's purpose (controlled input with react-hook-form)
  • The trim-on-blur behavior (line 43-45)
  • The optional async onBlur callback use case (debounced BIC/IBAN validation)

Example:

/**
 * A controlled form input component using react-hook-form's Controller.
 * Automatically trims whitespace on blur and supports async validation callbacks.
 * Displays validation errors only after the field has been touched.
 */
const FormInput: React.FC<FormInputProps> = ({ ... }) => {
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (5)

207-208: Type error as unknown instead of inferring from catch.

Caught errors should be typed as unknown and narrowed with type guards rather than assuming Error.

Apply this diff:

-            } catch (error) {
+            } catch (error: unknown) {
                 setSubmissionError(error instanceof Error ? error.message : 'An unknown error occurred')

236-270: Consider extracting step rendering logic.

As kushagrasarathe suggested in previous review, a helper function using a switch statement would improve readability and maintainability.

However, the current inline approach is clear enough. If you add more account types in the future (e.g., PIX, Mercado Pago), extracting to a dedicated renderStep function would make the component more maintainable:

const renderStep = (
    step: number,
    accountType: AccountType,
    props: StepRenderProps
): JSX.Element | null => {
    switch (step) {
        case 1:
            return <PersonalInfoStep {...props.personalInfo} />
        case 2:
            return <AccountDetailsStep {...props.accountDetails} />
        case 3:
            return <AddressStep {...props.address} />
        default:
            return null
    }
}

236-270: Consider consolidating step rendering with step configuration.

Previous feedback suggested improving the step rendering approach. While the current switch statement is clear, consider adding step component mappings to the StepConfig in stepConfig.ts for better scalability:

// In stepConfig.ts
export interface StepConfig {
    totalSteps: number
    getStepTitle: (step: number) => string
    getStepComponent: (step: number) => React.ComponentType<StepProps>
    isStepValid: (step: number, data: IBankAccountDetails, errors: FieldErrors<IBankAccountDetails>) => boolean
}

This would make adding new account types (e.g., Mercado Pago, PIX) more straightforward, as mentioned in the previous feedback.


66-68: Name splitting has limitations for multi-word first names.

The current implementation treats the first word as firstName and everything else as lastName. This won't handle cases like "Mary Anne Smith" correctly (would split to firstName: "Mary", lastName: "Anne Smith").

Consider documenting this limitation or using a more sophisticated name parser if handling international names with multiple given names is important. For most cases, this simple approach is acceptable.


159-181: Consider using sanitizeBankAccount for consistency.

Line 161 manually removes spaces with .replace(/\s/g, ''), but sanitizeBankAccount is already available and used elsewhere (line 136). Using it consistently would reduce duplication and ensure the same normalization is applied everywhere.

                 const payload: Partial<AddBankAccountPayload> = {
                     accountType: bridgeAccountType,
-                    accountNumber: accountNumber.replace(/\s/g, ''),
+                    accountNumber: sanitizeBankAccount(accountNumber),
                     countryCode: isUs ? 'USA' : country.toUpperCase(),
src/components/MultiStepBankAccountForm/types.ts (2)

3-18: Consider documenting field usage by account type.

The IBankAccountDetails type includes all possible fields for US, IBAN, and MX account types, but only subsets are required for each type. Consider adding JSDoc comments to clarify which fields are used by which account type.

Example:

export type IBankAccountDetails = {
    name?: string
    firstName: string
    lastName: string
    email: string
    /** Required for all account types (IBAN uses this for IBAN number, US/MX for account number) */
    accountNumber: string
    /** Required for IBAN accounts only */
    bic: string
    /** Required for US accounts only */
    routingNumber: string
    /** Required for MX accounts only */
    clabe: string
    // ... rest of fields
}

This would improve maintainability and reduce confusion about which fields are actually validated for each flow.


3-18: Consider making account-type-specific fields optional.

Currently, all fields (except name) are required strings. However, bic, routingNumber, clabe, and iban are only used for specific account types. Making them optional would better represent the domain model.

 export type IBankAccountDetails = {
     name?: string
     firstName: string
     lastName: string
     email: string
     accountNumber: string
-    bic: string
-    routingNumber: string
-    clabe: string
+    bic?: string
+    routingNumber?: string
+    clabe?: string
     street: string
     city: string
     state: string
     postalCode: string
-    iban: string
+    iban?: string
     country: string
 }

This would require updating validation checks to handle undefined, but would more accurately reflect which fields are required for each account type.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e28c9b2 and b2ee2e2.

📒 Files selected for processing (6)
  • src/components/MultiStepBankAccountForm/FormInput.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/stepConfig.ts (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/AccountDetailsStep.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/steps/PersonalInfoStep.tsx (1 hunks)
  • src/components/MultiStepBankAccountForm/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/MultiStepBankAccountForm/steps/PersonalInfoStep.tsx
  • src/components/MultiStepBankAccountForm/steps/AccountDetailsStep.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.

Applied to files:

  • src/components/MultiStepBankAccountForm/stepConfig.ts
📚 Learning: 2025-10-07T10:19:20.059Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#1286
File: src/components/MultiStepBankAccountForm/FormInput.tsx:10-10
Timestamp: 2025-10-07T10:19:20.059Z
Learning: The codebase follows a strict policy of avoiding `any` types. Always use proper TypeScript types, particularly from libraries like react-hook-form (RegisterOptions, FieldErrors, UseFormSetValue, etc.) rather than using `any`.

Applied to files:

  • src/components/MultiStepBankAccountForm/FormInput.tsx
🧬 Code graph analysis (3)
src/components/MultiStepBankAccountForm/stepConfig.ts (1)
src/components/MultiStepBankAccountForm/types.ts (3)
  • IBankAccountDetails (3-18)
  • AccountType (20-20)
  • StepConfig (22-26)
src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (11)
src/app/actions/types/users.types.ts (1)
  • AddBankAccountPayload (36-56)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (3-18)
src/components/Global/PeanutActionDetailsCard/index.tsx (2)
  • PeanutActionDetailsCardProps (29-52)
  • PeanutActionDetailsCard (54-253)
src/components/MultiStepBankAccountForm/stepConfig.ts (2)
  • getAccountType (17-22)
  • getStepConfig (24-96)
src/context/authContext.tsx (1)
  • useAuth (183-189)
src/redux/hooks.ts (2)
  • useAppDispatch (5-5)
  • useAppSelector (6-6)
src/context/WithdrawFlowContext.tsx (1)
  • useWithdrawFlow (157-163)
src/hooks/useSavedAccounts.tsx (1)
  • useSavedAccounts (12-25)
src/hooks/useDebounce.ts (1)
  • useDebounce (9-23)
src/utils/format.utils.ts (1)
  • sanitizeBankAccount (1-4)
src/redux/slices/bank-form-slice.ts (1)
  • bankFormActions (36-36)
src/components/MultiStepBankAccountForm/FormInput.tsx (1)
src/components/MultiStepBankAccountForm/types.ts (1)
  • IBankAccountDetails (3-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (19)
src/components/MultiStepBankAccountForm/FormInput.tsx (3)

41-49: LGTM: Thoughtful whitespace trimming.

The automatic trimming of string values on blur (lines 43-44) prevents common user input errors while preserving the user's ability to type freely.


7-16: Excellent type safety improvements!

The interface now uses proper react-hook-form types throughout:

  • rules is correctly typed as RegisterOptions<IBankAccountDetails, keyof IBankAccountDetails>
  • onBlur uses ControllerRenderProps<IBankAccountDetails, keyof IBankAccountDetails>

This addresses the previous review comments and aligns with the codebase policy of avoiding any types.

Based on learnings.


18-60: LGTM! Clean and well-structured component.

The implementation is solid:

  • Proper use of react-hook-form's Controller
  • Automatic whitespace trimming on blur for data sanitization
  • Conditional error display based on both error presence and field touch state
  • Clean integration with BaseInput and ErrorAlert
src/components/MultiStepBankAccountForm/types.ts (2)

3-18: Verify that all fields should be required.

All fields in IBankAccountDetails are marked as required (non-optional), but the form uses them conditionally based on account type:

  • US accounts need accountNumber and routingNumber
  • IBAN accounts need accountNumber and bic
  • MX accounts need clabe

This means most fields will have empty strings rather than being truly optional. While this works, it's less type-safe than using optional fields with proper guards.

Consider whether fields that are conditionally required should be marked optional:

export type IBankAccountDetails = {
    name?: string
    firstName: string
    lastName: string
    email: string
    accountNumber?: string  // Only required for US/IBAN
    bic?: string            // Only required for IBAN
    routingNumber?: string  // Only required for US
    clabe?: string          // Only required for MX
    street?: string         // Only required for US/MX
    city?: string           // Only required for US/MX
    state?: string          // Only required for US/MX
    postalCode?: string     // Only required for US/MX
    iban?: string
    country: string
}

This would require adding runtime type guards but would make the type system more accurate.


20-26: LGTM! Clean type definitions.

The types are well-structured:

  • AccountType clearly defines the three supported account types
  • StepConfig interface properly types the validation callback with FieldErrors<IBankAccountDetails> instead of any

Based on learnings.

src/components/MultiStepBankAccountForm/MultiStepBankAccountForm.tsx (11)

136-139: Good fix: Identifiers now normalized before comparison.

The use of sanitizeBankAccount on both the input identifier (line 136) and saved account identifier (line 138) properly addresses the previous issue where IBANs with spaces/case differences weren't matching.


183-192: Verify iban field assignment logic.

The logic for assigning the iban field in the result object (line 185) is complex:

iban: isIban ? data.accountNumber || iban || '' : '',

This suggests that for IBAN accounts:

  1. The form field accountNumber might contain the IBAN
  2. Or there's a separate iban value from getValues('iban')
  3. Otherwise it defaults to empty string

Please confirm:

  1. Should data.accountNumber contain the IBAN for IBAN account types?
  2. When would iban from getValues('iban') be used vs data.accountNumber?
  3. Is there an iban input field in the form, or is this dead code?

Looking at line 157: const iban = data.iban || getValues('iban') but iban is never used except in line 185. This might be unnecessary complexity.


162-162: Confirm Bridge API country code format
Couldn't locate any existing Bridge API calls to verify whether countryCode should be 2-letter or 3-letter. Lines 162 and 174 set it to 'USA' or country.toUpperCase(). Confirm with Bridge documentation or working examples that this matches the API requirements.


136-139: Good fix of identifier normalization.

The duplicate account check now properly normalizes both the input identifier and saved account identifiers using sanitizeBankAccount, successfully addressing the concern from the previous review.


159-177: Incorrect concern about country code format
country is always provided via getCountryCodeForWithdraw, which guarantees a valid uppercase alpha-3 code, so using country.toUpperCase() for countryCode and address.country is correct.

Likely an incorrect or invalid review comment.


27-51: LGTM! Well-structured component API.

The props interface is comprehensive and the use of forwardRef to expose handleSubmit is a good pattern for external control of the form submission.


80-105: LGTM! Comprehensive form initialization.

The default values properly cascade from multiple sources (user data, initialData, persisted Redux state), with persisted data taking precedence. The validation modes (onBlur and onSubmit) provide good UX by avoiding premature validation while typing.


107-120: LGTM! Clean debouncing pattern for BIC validation.

The 500ms debounce prevents excessive API calls while the user is typing, and the useEffect properly validates only for IBAN accounts. Good separation of concerns.


136-145: LGTM! Proper normalization for account deduplication.

The use of sanitizeBankAccount on both the input identifier and saved account identifiers ensures consistent normalization (removes spaces, hyphens, dots, underscores, and lowercases). This correctly addresses the previous review comment about IBAN mismatch due to formatting differences.


236-270: LGTM! Clear step rendering logic.

The switch statement provides a straightforward way to render the appropriate step component. While a helper function could extract this, the current implementation is clear and maintainable.


272-319: LGTM! Well-structured JSX with proper error handling.

The render includes:

  • Clear step progression with StepIndicator
  • Conditional button label ("Next" vs "Review" on final step)
  • Proper use of twMerge for conditional styling
  • Appropriate error display prioritizing submission errors over prop errors
  • Correct button state management (disabled when invalid or submitting)
src/components/MultiStepBankAccountForm/stepConfig.ts (3)

4-15: Good refactor of validation logic.

The extraction of validatePersonalInfo and validateAddress into shared validators successfully addresses the DRY concern from the previous review. The implementation is clean and properly typed.


4-15: LGTM! Clean extraction of common validators.

The validation functions correctly check for required fields and absence of errors. Good DRY implementation that's reused across multiple account type configurations.


36-92: LGTM! Well-structured step configurations.

The per-account-type configurations correctly specify:

  • Total steps for each flow (US: 3, IBAN: 2, MX: 3)
  • Appropriate validation logic reusing common validators
  • Type-specific field checks (routingNumber for US, bic for IBAN, clabe for MX)

@Zishan-7 Zishan-7 marked this pull request as draft October 9, 2025 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants