Skip to content

[TASK-16296] Feat: Verification rework#1396

Merged
kushagrasarathe merged 16 commits intopeanut-wallet-devfrom
fix/verification-rework
Nov 7, 2025
Merged

[TASK-16296] Feat: Verification rework#1396
kushagrasarathe merged 16 commits intopeanut-wallet-devfrom
fix/verification-rework

Conversation

@Zishan-7
Copy link
Contributor

@Zishan-7 Zishan-7 commented Nov 4, 2025

image image image image image image image image

@vercel
Copy link

vercel bot commented Nov 4, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Nov 7, 2025 1:28pm

@notion-workspace
Copy link

Verification Flow REWORK

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 4, 2025

Walkthrough

Adds a regional identity-verification flow: new mobile App Router pages and layout, region/country views and components, a new useIdentityVerification hook and utilities, country/region data updates (including Mexico mapping), UI/icon changes (globe-lock), Radix accordion dependency, Tailwind accordion animations, and KYC-completion modal integration.

Changes

Cohort / File(s) Change Summary
Package
package.json
Adds runtime dependency @radix-ui/react-accordion ^1.2.12.
App Router pages (mobile)
src/app/(mobile-ui)/profile/identity-verification/page.tsx, src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx, src/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx
Adds three client pages: root regions page, region page (reads region param), and country page (client wrapper rendering IdentityVerification view).
Layout (mobile)
src/app/(mobile-ui)/profile/identity-verification/layout.tsx
New client layout that opens an ActionModal when a region/country is already verified and navigates to /profile on close.
Views & pages
src/components/Profile/views/IdentityVerification.view.tsx, src/components/Profile/views/RegionsPage.view.tsx, src/components/Profile/views/RegionsVerification.view.tsx
Reworks identity-verification flow: introduces StartVerificationModal, RegionsPage (region + country list), and RegionsVerification (locked/unlocked regions).
Identity verification UI
src/components/IdentityVerification/StartVerificationModal.tsx, src/components/Profile/components/CountryListSection.tsx, src/components/Profile/components/IdentityVerificationCountryList.tsx
Adds StartVerificationModal and accordion-based CountryListSection; IdentityVerificationCountryList provides search, filtering, and navigation behavior.
Profile & common components
src/components/Profile/index.tsx, src/components/ActionListCard/index.tsx, src/components/Common/CountryList.tsx
Profile item renamed to "Regions & Verification" with globe-lock icon; ActionListCard gains titleClassName?: string; CountryList gains inputDescription?: string and showLoadingState?: boolean; useKycStatus return shape trimmed.
Icons & assets
src/assets/icons/index.ts, src/components/Global/Icons/globe-lock.tsx, src/components/Global/Icons/Icon.tsx
Exports four region globe icons; adds globe-lock SVG component and registers it in the icon registry (extends IconName).
Hooks & types
src/hooks/useIdentityVerification.tsx, src/hooks/useKycStatus
Adds useIdentityVerification hook and exported Region/VerificationUnlockItem types; updates useKycStatus return shape to only include isUserKycApproved.
Data & utils
src/components/AddMoney/consts/index.ts, src/utils/identityVerification.tsx
Adds optional region to CountryData, introduces MEXICO_ALPHA3_TO_ALPHA2, updates many country entries with region, and adds getCountriesForRegion(region) to partition supported vs unsupported countries.
Home & KYC modal
src/app/(mobile-ui)/home/page.tsx, src/components/Home/KycCompletedModal/index.tsx, src/interfaces/interfaces.ts
Adds KycCompletedModal component and integrates it into Home; adds showKycCompletedModal: boolean to User interface.
Styling / config
tailwind.config.js
Adds accordion-down and accordion-up keyframes and animation aliases for accordion transitions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Extra attention areas:
    • src/hooks/useIdentityVerification.tsx — region/unlocked derivation, Manteca vs Bridge rules, exported types.
    • src/components/Profile/views/IdentityVerification.view.tsx — modal-driven selection flow, URL auto-open logic, redirect handling.
    • src/components/AddMoney/consts/index.ts — CountryData type changes and Mexico mapping merging/usage.
    • src/hooks/useKycStatus — changed return shape may impact callers across the codebase.

Possibly related PRs

Suggested reviewers

  • kushagrasarathe
  • jjramirezn
  • Hugo0

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[TASK-16296] Feat: Verification rework' is specific and clearly identifies the main change—a significant rework of the identity verification flow across multiple components, routes, and utilities.
Description check ✅ Passed The description consists entirely of screenshot images showing the UI/UX changes resulting from the verification rework, which is directly related to the changeset's objective of updating the verification flow and profile experience.
✨ 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 fix/verification-rework

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.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Nov 4, 2025
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: 3

🧹 Nitpick comments (4)
src/components/Profile/index.tsx (1)

56-62: Consider removing the dummy href workaround.

The href="/dummy" with the comment "Dummy link, wont be called" is a workaround to maintain link styling while using onClick for modal behavior. Consider one of these alternatives:

  • Make the href optional in ProfileMenuItem and handle styling accordingly
  • Use a button element styled as a link when no actual navigation is needed
  • Remove the href entirely if ProfileMenuItem gracefully handles its absence
src/components/Profile/views/RegionsPage.view.tsx (1)

27-38: Document the special case for the 'latam' region.

The verification button is hidden for the latam region without explanation. Consider adding a comment explaining why this region is treated differently.

+            {/* Latam uses country-specific verification flows instead of regional Bridge verification */}
             {region.path !== 'latam' && (
                 <div className="sticky bottom-4 flex justify-center">
src/utils/identityVerification.tsx (2)

4-6: Consider using a Set for supported countries lookup.

Currently, concat is used to merge the keys, which could create duplicates if a country appears in both mappings. While this doesn't break functionality (duplicates in an array used for includes() checks are harmless), using a Set would be more semantically correct and slightly more efficient.

-    const supportedCountriesIso3 = Object.keys(ALL_COUNTRIES_ALPHA3_TO_ALPHA2).concat(
-        Object.keys(MEXICO_ALPHA3_TO_ALPHA2) // Add Mexico as well, supported by bridge
-    )
+    const supportedCountriesIso3 = new Set([
+        ...Object.keys(ALL_COUNTRIES_ALPHA3_TO_ALPHA2),
+        ...Object.keys(MEXICO_ALPHA3_TO_ALPHA2), // Add Mexico as well, supported by bridge
+    ])

Then update line 14 to use has instead of includes:

-        if (country.iso3 && supportedCountriesIso3.includes(country.iso3)) {
+        if (country.iso3 && supportedCountriesIso3.has(country.iso3)) {

5-5: Clarify why Mexico needs separate handling.

The comment states "Add Mexico as well, supported by bridge," but it's unclear why Mexico isn't already included in ALL_COUNTRIES_ALPHA3_TO_ALPHA2 if Bridge supports it. Consider expanding the comment to explain the special case.

Example:

-        Object.keys(MEXICO_ALPHA3_TO_ALPHA2) // Add Mexico as well, supported by bridge
+        Object.keys(MEXICO_ALPHA3_TO_ALPHA2), // Mexico (MEX) requires separate mapping due to [explain reason]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efaa823 and 265b535.

⛔ Files ignored due to path filters (5)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/assets/icons/europe-globe.svg is excluded by !**/*.svg
  • src/assets/icons/latam-globe.svg is excluded by !**/*.svg
  • src/assets/icons/north-america-globe.svg is excluded by !**/*.svg
  • src/assets/icons/rest-of-world-globe.svg is excluded by !**/*.svg
📒 Files selected for processing (21)
  • package.json (1 hunks)
  • src/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx (1 hunks)
  • src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx (1 hunks)
  • src/app/(mobile-ui)/profile/identity-verification/layout.tsx (1 hunks)
  • src/app/(mobile-ui)/profile/identity-verification/page.tsx (1 hunks)
  • src/assets/icons/index.ts (1 hunks)
  • src/components/ActionListCard/index.tsx (3 hunks)
  • src/components/AddMoney/consts/index.ts (242 hunks)
  • src/components/Common/CountryList.tsx (4 hunks)
  • src/components/Global/Icons/Icon.tsx (3 hunks)
  • src/components/Global/Icons/globe-lock.tsx (1 hunks)
  • src/components/IdentityVerification/StartVerificationModal.tsx (1 hunks)
  • src/components/Profile/components/CountryListSection.tsx (1 hunks)
  • src/components/Profile/components/IdentityVerificationCountryList.tsx (1 hunks)
  • src/components/Profile/index.tsx (2 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (4 hunks)
  • src/components/Profile/views/RegionsPage.view.tsx (1 hunks)
  • src/components/Profile/views/RegionsVerification.view.tsx (1 hunks)
  • src/hooks/useIdentityVerification.tsx (1 hunks)
  • src/utils/identityVerification.tsx (1 hunks)
  • tailwind.config.js (2 hunks)
🧰 Additional context used
🧠 Learnings (16)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-10-08T17:13:13.155Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1299
File: src/app/(mobile-ui)/points/page.tsx:41-51
Timestamp: 2025-10-08T17:13:13.155Z
Learning: In `src/app/(mobile-ui)/points/page.tsx`, the icon name "invite-heart" is intentionally used (not "inviter-heart") when displaying who invited the current user, as this is a deliberate design choice despite semantic differences with UserHeader usage.

Applied to files:

  • src/components/Global/Icons/Icon.tsx
  • src/components/Profile/index.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx
  • src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx
  • src/components/Profile/components/IdentityVerificationCountryList.tsx
  • src/app/(mobile-ui)/profile/identity-verification/page.tsx
  • src/components/Profile/views/RegionsPage.view.tsx
  • src/app/(mobile-ui)/profile/identity-verification/layout.tsx
  • src/components/Profile/components/CountryListSection.tsx
  • src/components/Profile/views/RegionsVerification.view.tsx
  • src/components/Common/CountryList.tsx
  • src/components/IdentityVerification/StartVerificationModal.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-11T17:46:12.507Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1200
File: src/app/(mobile-ui)/recover-funds/page.tsx:9-9
Timestamp: 2025-09-11T17:46:12.507Z
Learning: Functions in Next.js that are not marked with "use server" and contain secrets are unsafe to import in client components, as they get bundled into the client JavaScript and can leak environment variables to the browser.

Applied to files:

  • src/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx
  • src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx
📚 Learning: 2024-10-22T18:11:36.864Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:32-49
Timestamp: 2024-10-22T18:11:36.864Z
Learning: In `src/app/request/pay/page.tsx`, the `id` parameter is accessed via `searchParams.id` in the `generateMetadata` function.

Applied to files:

  • src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx
📚 Learning: 2025-05-02T19:14:25.010Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 828
File: src/app/(mobile-ui)/request/[...username]/page.tsx:17-17
Timestamp: 2025-05-02T19:14:25.010Z
Learning: In Next.js App Router, catch-all routes (like `/request/[...username]`) will only match URLs with at least one parameter segment, ensuring that the page component only renders when there is at least one value in the params array.

Applied to files:

  • src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx
  • src/components/Profile/views/RegionsPage.view.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/Profile/components/IdentityVerificationCountryList.tsx
  • src/utils/identityVerification.tsx
  • src/components/AddMoney/consts/index.ts
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:0-0
Timestamp: 2025-08-13T18:22:01.941Z
Learning: In the DynamicBankAccountForm component, the countryName parameter from useParams will always resemble a country title, not a URL slug.

Applied to files:

  • src/components/Profile/components/IdentityVerificationCountryList.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-26T17:38:37.055Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/components/Common/ActionList.tsx:153-156
Timestamp: 2025-08-26T17:38:37.055Z
Learning: In ActionList.tsx, when there are circular dependency concerns with ACTION_METHODS being imported by other components, the preferred solution is to move ACTION_METHODS to a separate constants file (like src/constants/actionlist.consts.ts) rather than using prop drilling. This centralizes constants management and creates a cleaner dependency graph.

Applied to files:

  • src/components/ActionListCard/index.tsx
  • src/components/Profile/components/CountryListSection.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/components/Profile/index.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Profile/index.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the CountryData interface from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the ALL_METHODS_DATA structure from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-08-14T14:36:18.758Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:0-0
Timestamp: 2025-08-14T14:36:18.758Z
Learning: Bridge API requires ISO3 country codes (3-letter codes like "USA", "GBR") while flag display components need ISO2 codes (2-letter codes like "US", "GB").

Applied to files:

  • src/components/AddMoney/consts/index.ts
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
🧬 Code graph analysis (11)
src/components/Global/Icons/Icon.tsx (1)
src/components/Global/Icons/globe-lock.tsx (1)
  • GlobeLockIcon (3-16)
src/components/Profile/components/IdentityVerificationCountryList.tsx (3)
src/utils/identityVerification.tsx (1)
  • getCountriesForRegion (3-22)
src/components/SearchInput/index.tsx (1)
  • SearchInput (14-51)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/utils/identityVerification.tsx (1)
src/components/AddMoney/consts/index.ts (3)
  • ALL_COUNTRIES_ALPHA3_TO_ALPHA2 (2784-2787)
  • MEXICO_ALPHA3_TO_ALPHA2 (2775-2777)
  • countryData (292-2700)
src/hooks/useIdentityVerification.tsx (3)
src/context/authContext.tsx (1)
  • useAuth (185-191)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/components/AddMoney/consts/index.ts (2)
  • MantecaSupportedExchanges (10-21)
  • countryData (292-2700)
src/components/Profile/views/RegionsPage.view.tsx (2)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (57-125)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
src/app/(mobile-ui)/profile/identity-verification/layout.tsx (1)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (57-125)
src/components/Profile/index.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/components/Profile/components/CountryListSection.tsx (4)
src/components/AddMoney/consts/index.ts (1)
  • CountryData (155-165)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/ActionListCard/index.tsx (1)
  • ActionListCard (20-72)
src/components/Profile/views/RegionsVerification.view.tsx (5)
src/hooks/useIdentityVerification.tsx (2)
  • useIdentityVerification (57-125)
  • Region (9-14)
src/components/Global/EmptyStates/EmptyState.tsx (1)
  • EmptyState (13-28)
src/components/ActionListCard/index.tsx (1)
  • ActionListCard (20-72)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/components/IdentityVerification/StartVerificationModal.tsx (2)
src/components/AddMoney/consts/index.ts (1)
  • MantecaSupportedExchanges (10-21)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/components/Profile/views/IdentityVerification.view.tsx (4)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (57-125)
src/utils/general.utils.ts (2)
  • getRedirectUrl (782-784)
  • clearRedirectUrl (786-790)
src/components/AddMoney/consts/index.ts (1)
  • countryData (292-2700)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • MantecaGeoSpecificKycModal (87-151)
🪛 Biome (2.1.2)
src/components/Profile/views/RegionsVerification.view.tsx

[error] 56-76: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🪛 GitHub Actions: Tests
src/components/Profile/components/CountryListSection.tsx

[warning] 1-1: Code style issues found by Prettier. Run 'prettier --write' to fix.


[error] 1-1: Prettier check failed. Command 'pnpm prettier --check .' exited with code 1.

🔇 Additional comments (12)
src/components/Global/Icons/globe-lock.tsx (1)

1-16: LGTM!

The icon component is well-structured with proper TypeScript typing, uses currentColor for theming flexibility, and correctly spreads props to allow customization while providing sensible defaults.

src/assets/icons/index.ts (1)

26-29: LGTM!

The new globe icon exports follow the established naming convention and integrate cleanly with the existing icon asset structure to support the regional verification UI.

src/components/Global/Icons/Icon.tsx (1)

67-67: LGTM!

The globe-lock icon is properly registered across all three required locations (import, type union, and component mapping), following the established pattern for icon registration.

Also applies to: 136-136, 209-209

src/components/Profile/index.tsx (2)

68-75: LGTM!

The icon and label changes appropriately reflect the expanded scope from "Identity Verification" to "Regions & Verification", aligning well with the PR objectives for regional verification UI infrastructure. The globe-lock icon visually communicates the combined regional and verification aspects.


26-26: No issues found—component usage is valid.

The hook returns all four values (isUserBridgeKycApproved, isUserMantecaKycApproved, isUserKycApproved, isUserBridgeKycUnderReview) at line 35 of src/hooks/useKycStatus.tsx. The component in src/components/Profile/index.tsx intentionally destructures only isUserKycApproved, which is valid JavaScript/TypeScript—selective destructuring of object properties requires no changes.

package.json (1)

37-37: LGTM!

The Radix UI Accordion dependency addition is appropriate for the accordion UI components introduced in this PR.

src/components/Common/CountryList.tsx (1)

24-24: LGTM!

The inputDescription prop is cleanly implemented with proper typing, documentation, and conditional rendering. The addition enhances the component's flexibility for displaying additional context to users.

Also applies to: 40-40, 49-49, 103-103

tailwind.config.js (1)

200-207: LGTM!

The accordion animations are properly configured for Radix UI with appropriate timing (0.3s) and easing curves. The use of CSS custom properties (--radix-accordion-content-height) is standard for Radix UI accordion implementations.

Also applies to: 216-217

src/app/(mobile-ui)/profile/identity-verification/page.tsx (1)

1-5: LGTM!

The refactor to use RegionsVerification directly is clean and maintains the page's public API. The implementation follows a clear separation of concerns by delegating all logic to the view component.

src/app/(mobile-ui)/profile/identity-verification/[region]/page.tsx (1)

1-10: LGTM!

The implementation properly extracts the region route parameter using useParams and passes it to the RegionsPage view component. This follows Next.js App Router best practices for dynamic route handling.

src/app/(mobile-ui)/profile/identity-verification/[region]/[country]/page.tsx (1)

1-6: ****

The IdentityVerificationView component already uses useParams() internally (line 37 of src/components/Profile/views/IdentityVerification.view.tsx) and extracts the country parameter without requiring props. The page component correctly renders <IdentityVerificationView /> without passing route parameters—the component handles this internally. The suggested diff is unnecessary and conflicts with the actual component design.

Likely an incorrect or invalid review comment.

src/components/Profile/views/RegionsPage.view.tsx (1)

12-18: The review comment is based on incomplete understanding of the navigation flow.

Navigation to the [region] route is protected at the source. In RegionsVerification.view.tsx, the button that navigates to a region only executes when isLocked is true. This means only locked regions can trigger navigation to /profile/identity-verification/${region.path}.

RegionsPage's implementation—searching only lockedRegions and returning null if not found—is therefore correct and intentional. Users cannot reach this route with an unlocked region via the normal UI flow, so the blank screen scenario in the review comment does not occur.

Likely an incorrect or invalid review comment.

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

Caution

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

⚠️ Outside diff range comments (1)
src/components/Profile/index.tsx (1)

58-64: Remove unnecessary href prop when using onClick handler.

The href="/dummy" is unnecessary. The ProfileMenuItem component's href prop is optional, and when onClick is provided, the component uses the click handler directly without rendering a Link wrapper. Simply remove the href prop:

<ProfileMenuItem
    icon="smile"
    label="Invite friends to Peanut"
    onClick={() => setIsInviteFriendsModalOpen(true)}
    position="single"
/>
🧹 Nitpick comments (1)
src/components/Profile/components/CountryListSection.tsx (1)

17-17: Remove unused defaultOpen prop.

The defaultOpen prop is accepted and destructured but never used in the component. If you need to control the initial open state, you should pass it to a parent Accordion.Root component using defaultValue.

Apply this diff to remove the unused prop:

     isDisabled?: boolean
     value: string
-    defaultOpen?: boolean
 }
     isDisabled = false,
     value,
-    defaultOpen = false,
 }: CountryListSectionProps) => {

Also applies to: 28-28

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 265b535 and 59f1934.

📒 Files selected for processing (5)
  • src/components/AddMoney/consts/index.ts (242 hunks)
  • src/components/Common/CountryList.tsx (4 hunks)
  • src/components/Profile/components/CountryListSection.tsx (1 hunks)
  • src/components/Profile/index.tsx (2 hunks)
  • tailwind.config.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tailwind.config.js
  • src/components/Common/CountryList.tsx
  • src/components/AddMoney/consts/index.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-10-08T17:13:13.155Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1299
File: src/app/(mobile-ui)/points/page.tsx:41-51
Timestamp: 2025-10-08T17:13:13.155Z
Learning: In `src/app/(mobile-ui)/points/page.tsx`, the icon name "invite-heart" is intentionally used (not "inviter-heart") when displaying who invited the current user, as this is a deliberate design choice despite semantic differences with UserHeader usage.

Applied to files:

  • src/components/Profile/index.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/components/Profile/index.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Profile/index.tsx
📚 Learning: 2025-08-26T17:38:37.055Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1132
File: src/components/Common/ActionList.tsx:153-156
Timestamp: 2025-08-26T17:38:37.055Z
Learning: In ActionList.tsx, when there are circular dependency concerns with ACTION_METHODS being imported by other components, the preferred solution is to move ACTION_METHODS to a separate constants file (like src/constants/actionlist.consts.ts) rather than using prop drilling. This centralizes constants management and creates a cleaner dependency graph.

Applied to files:

  • src/components/Profile/components/CountryListSection.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/Profile/components/CountryListSection.tsx
🧬 Code graph analysis (2)
src/components/Profile/index.tsx (1)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/components/Profile/components/CountryListSection.tsx (4)
src/components/AddMoney/consts/index.ts (1)
  • CountryData (155-165)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/ActionListCard/index.tsx (1)
  • ActionListCard (20-72)
⏰ 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 (5)
src/components/Profile/components/CountryListSection.tsx (3)

32-41: Verify description placement is intentional.

The description (line 41) is rendered outside the Accordion.Trigger, making it non-clickable for toggling the accordion. If this is intentional to provide read-only context, that's fine. However, if users should be able to click the entire header area to toggle, consider moving the description inside the trigger.


31-31: Note: Component requires parent Accordion.Root wrapper.

This component renders a single Accordion.Item and must be wrapped in a Accordion.Root component to function properly. Based on the PR context, this is handled by the parent IdentityVerificationCountryList.tsx, but future consumers should be aware of this requirement.


56-68: ****

The suggestion to add a fallback icon contradicts the team's established preference. Per documented learnings, the team prioritizes showing accurate country flags without fallback handling. The codebase already correctly configures flagcdn.com via next.config.js wildcard remotePatterns, and the current error handling aligns with the team's preference to avoid generic fallbacks.

Likely an incorrect or invalid review comment.

src/components/Profile/index.tsx (2)

28-28: LGTM: Hook usage updated correctly.

The destructuring correctly uses only isUserKycApproved from the updated useKycStatus hook, consistent with the removal of isUserBridgeKycUnderReview from the hook's return type.


78-79: Icon inconsistency between menu item and modal.

The "Regions & Verification" menu item uses icon="globe-lock" (line 78), but the related modal at line 167 still uses icon="shield". For visual consistency, the modal should use the same icon as the menu item.

Apply this diff to align the modal icon:

         <ActionModal
             visible={showInitiateKycModal}
             onClose={() => setShowInitiateKycModal(false)}
             title="Verification, Only If You Need It"
             description="No need to verify unless you want to move money to or from your bank."
-            icon="shield"
+            icon="globe-lock"
             ctaClassName="flex-col sm:flex-col"

Also applies to: 167-167

⛔ Skipped due to learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1299
File: src/app/(mobile-ui)/points/page.tsx:41-51
Timestamp: 2025-10-08T17:13:13.155Z
Learning: In `src/app/(mobile-ui)/points/page.tsx`, the icon name "invite-heart" is intentionally used (not "inviter-heart") when displaying who invited the current user, as this is a deliberate design choice despite semantic differences with UserHeader usage.

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 (1)
src/components/Profile/views/RegionsVerification.view.tsx (1)

57-57: Past review comment resolved.

The missing key property flagged in the previous review has been correctly addressed. Using region.path as the key provides a stable identifier for React's list reconciliation.

🧹 Nitpick comments (2)
src/components/Profile/views/RegionsVerification.view.tsx (2)

28-29: Use consistent terminology.

The section headers use "regions" (lines 21, 36), but the empty state message uses "countries". For consistency, update the empty state to use "regions" instead.

Apply this diff:

-                        title="You haven't unlocked any countries yet."
-                        description="No countries unlocked yet. Complete verification to unlock countries and use supported payment methods."
+                        title="You haven't unlocked any regions yet."
+                        description="No regions unlocked yet. Complete verification to unlock regions and use supported payment methods."

56-77: Remove clickable affordance from unlocked regions.

Unlocked regions currently show cursor-pointer and hover effects from ActionListCard, but the onClick handler does nothing when isLocked is false. This creates a misleading affordance—users may expect an action when clicking unlocked regions.

Apply this diff to remove the clickable appearance while preserving the visual style:

             {regions.map((region, index) => (
                 <ActionListCard
                     key={region.path}
+                    className={!isLocked ? 'cursor-default hover:bg-white' : ''}
                     leftIcon={
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59f1934 and 821dc7d.

📒 Files selected for processing (2)
  • src/components/IdentityVerification/StartVerificationModal.tsx (1 hunks)
  • src/components/Profile/views/RegionsVerification.view.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/IdentityVerification/StartVerificationModal.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/Profile/views/RegionsVerification.view.tsx
🧬 Code graph analysis (1)
src/components/Profile/views/RegionsVerification.view.tsx (5)
src/hooks/useIdentityVerification.tsx (2)
  • useIdentityVerification (57-125)
  • Region (9-14)
src/components/Global/EmptyStates/EmptyState.tsx (1)
  • EmptyState (13-28)
src/components/ActionListCard/index.tsx (1)
  • ActionListCard (20-72)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
⏰ 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

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

🧹 Nitpick comments (1)
src/components/Profile/views/IdentityVerification.view.tsx (1)

114-122: Consider memoizing the helper function.

The isBridgeSupportedCountry function is recreated on every render. While this doesn't cause functional issues, wrapping it in useCallback would be a minor optimization and follow React best practices, especially since it's used within the component's logic flow.

+    const isBridgeSupportedCountry = useCallback((code: string) => {
-    const isBridgeSupportedCountry = (code: string) => {
         const upper = code.toUpperCase()
         return (
             upper === 'US' ||
             upper === 'MX' ||
             Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
             Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
         )
-    }
+    }, [])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 821dc7d and 80243fc.

📒 Files selected for processing (2)
  • src/components/Common/CountryList.tsx (4 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/Common/CountryList.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/Common/CountryList.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the CountryData interface from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:0-0
Timestamp: 2025-08-13T18:22:01.941Z
Learning: In the DynamicBankAccountForm component, the countryName parameter from useParams will always resemble a country title, not a URL slug.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the ALL_METHODS_DATA structure from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
🧬 Code graph analysis (1)
src/components/Profile/views/IdentityVerification.view.tsx (3)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (57-125)
src/components/AddMoney/consts/index.ts (1)
  • countryData (292-2700)
src/components/Kyc/InitiateMantecaKYCModal.tsx (1)
  • MantecaGeoSpecificKycModal (87-151)
⏰ 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/Common/CountryList.tsx (1)

25-60: LGTM! Well-designed enhancement to CountryList.

The new inputDescription and showLoadingState props are properly documented, have sensible defaults for backward compatibility, and are correctly integrated into the rendering logic. The conditional loading state display (lines 187-191) appropriately shows the Loading indicator only when showLoadingState is true and a country is actively clicked.

Also applies to: 107-107, 187-191

src/components/Profile/views/IdentityVerification.view.tsx (3)

125-140: Auto-initiation logic looks correct.

The useEffect properly handles the auto-open flow when a country parameter is present in the URL. It correctly:

  • Validates if the country is bridge-supported
  • Looks up the country in countryData
  • Falls back to a "Bridge" identity if the country isn't found
  • Opens the StartVerificationModal with the appropriate country selection

222-247: Modal rendering condition is correct by design.

The condition userClickedCountry && selectedCountryParams ensures the modal only opens when both the ID-issuing country (userClickedCountry) and the destination country from the previous page (selectedCountryParams from URL param) are available. This prevents users from starting verification when navigating directly to this page without proper context.

Based on learnings from past review discussion where this condition was clarified as intentional behavior.


41-51: Well-structured flow improvements.

The updated handleRedirect signature with the isSuccess parameter provides clear separation between success and cancellation paths. The integration with CountryList (using showLoadingState={false} since the click is synchronous) and the KYC success callbacks properly route users through the verification flow.

Also applies to: 199-207, 218-219

@Zishan-7 Zishan-7 changed the title [TASK-16296] Fix/verification rework [TASK-16296] Feat: Verification rework Nov 4, 2025
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

♻️ Duplicate comments (1)
src/components/IdentityVerification/StartVerificationModal.tsx (1)

51-53: Fix grammatical article for country names.

The hardcoded article "an" is only correct for countries starting with vowel sounds (e.g., "an Argentina Issued ID"). For countries like "Brazil" or "Mexico", it should be "a" (e.g., "a Brazil Issued ID").

Apply this diff to use the correct article:

         if (isSelectedCountryMantecaCountry && !isIdentityMantecaCountry) {
-            return `Without an ${selectedCountry.title} Issued ID, you can still pay in stores using QR codes but you won't be able to transfer money directly to bank accounts.`
+            return `Without a ${selectedCountry.title} issued ID, you can still pay in stores using QR codes but you won't be able to transfer money directly to bank accounts.`
         }

Note: Using lowercase "issued" is also more grammatically correct. For a more sophisticated solution, you could implement a helper function to choose "a" vs "an" based on the first letter of the country name, but for the current supported countries (Argentina, Brazil, Mexico), using "a" is sufficient since only Argentina starts with a vowel.

🧹 Nitpick comments (2)
src/components/Home/KycCompletedModal/index.tsx (1)

87-97: Consider a more robust filtering approach.

The string comparison item.title === 'QR Payments in Brazil and Argentina' is fragile and will break if the text is ever updated. Consider using a unique identifier or type property instead.

Consider enhancing the VerificationUnlockItem type to include a unique key, then filter by key instead of comparing JSX/string content:

// In the type definition (wherever VerificationUnlockItem is defined)
type VerificationUnlockItem = {
    key: string  // Add unique identifier
    title: ReactNode
    type: 'bridge' | 'manteca'
}

// Then in the filtering logic:
items.filter((item) => {
    if (kycApprovalType === 'all') {
        return item.key !== 'manteca-qr-payments'
    }
    return item.type === kycApprovalType
})
src/components/IdentityVerification/StartVerificationModal.tsx (1)

31-39: Optimize dependency array to prevent unnecessary re-renders.

Line 38 includes the full selectedCountry object in the dependency array. If the object reference changes but the id remains the same, this will cause unnecessary re-computation.

Apply this diff to use only the id in the dependency:

     const isSelectedCountryMantecaCountry = useMemo(
         () => Object.prototype.hasOwnProperty.call(MantecaSupportedExchanges, selectedCountry.id.toUpperCase()),
-        [selectedCountry]
+        [selectedCountry.id]
     )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80243fc and 70383be.

📒 Files selected for processing (5)
  • src/app/(mobile-ui)/home/page.tsx (3 hunks)
  • src/components/Home/KycCompletedModal/index.tsx (1 hunks)
  • src/components/IdentityVerification/StartVerificationModal.tsx (1 hunks)
  • src/hooks/useIdentityVerification.tsx (1 hunks)
  • src/interfaces/interfaces.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useIdentityVerification.tsx
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
  • src/app/(mobile-ui)/home/page.tsx
📚 Learning: 2025-08-11T10:35:02.715Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1078
File: src/hooks/useKycFlow.ts:129-141
Timestamp: 2025-08-11T10:35:02.715Z
Learning: In the KYC flow implementation in `src/hooks/useKycFlow.ts`, when Terms of Service (ToS) is accepted, there will always be a KYC link available in the `apiResponse`. The system ensures this invariant, so defensive checks for missing KYC links after ToS acceptance are unnecessary.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
  • src/app/(mobile-ui)/home/page.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/IdentityVerification/StartVerificationModal.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/IdentityVerification/StartVerificationModal.tsx
📚 Learning: 2025-09-29T18:34:33.596Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1251
File: src/components/Invites/JoinWaitlistPage.tsx:41-55
Timestamp: 2025-09-29T18:34:33.596Z
Learning: In the JoinWaitlistPage component, after successfully accepting an invite via invitesApi.acceptInvite(), calling fetchUser() is sufficient to update the user state and automatically display the app. No manual navigation to /home or other pages is required since the user is already on the home page and the app will be displayed once user.hasAppAccess is updated.

Applied to files:

  • src/app/(mobile-ui)/home/page.tsx
🧬 Code graph analysis (3)
src/components/Home/KycCompletedModal/index.tsx (4)
src/context/authContext.tsx (1)
  • useAuth (192-198)
src/components/AddMoney/consts/index.ts (3)
  • CountryData (155-165)
  • MantecaSupportedExchanges (10-21)
  • countryData (292-2700)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (63-180)
src/components/IdentityVerification/StartVerificationModal.tsx (3)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (63-180)
src/components/AddMoney/consts/index.ts (1)
  • MantecaSupportedExchanges (10-21)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (212-221)
src/app/(mobile-ui)/home/page.tsx (1)
src/app/actions/users.ts (1)
  • updateUserById (12-35)
⏰ 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 (5)
src/interfaces/interfaces.ts (1)

261-261: LGTM!

The property addition is clean and follows existing conventions in the User interface.

src/app/(mobile-ui)/home/page.tsx (2)

41-42: LGTM!

Import additions are appropriate for the new KYC completion modal integration.


70-70: LGTM!

State initialization correctly uses optional chaining and provides a sensible default.

src/components/Home/KycCompletedModal/index.tsx (1)

12-31: LGTM!

The KYC approval type derivation logic correctly handles all combinations of Bridge and Manteca KYC approval states.

src/components/IdentityVerification/StartVerificationModal.tsx (1)

84-94: LGTM!

The unlock items filtering logic correctly branches based on whether the identity country supports Manteca transfers.

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

🧹 Nitpick comments (2)
src/components/Home/KycCompletedModal/index.tsx (2)

67-67: Remove redundant type assertion.

The 'globe-lock' literal is already part of the IconName union type (confirmed in Icon.tsx), so the type assertion is unnecessary and can be removed for cleaner code.

Apply this diff:

-            icon={'globe-lock' as IconName}
+            icon="globe-lock"

87-97: Consider more robust item identification.

The filtering logic on line 92 relies on exact string matching of the title property. While this works, it's fragile—a minor wording change in getVerificationUnlockItems would silently break the filter.

Consider enhancing the VerificationUnlockItem type with an explicit id or key field for more reliable identification.

For example, update the type and filter:

// In the type definition
type VerificationUnlockItem = {
    id: string;
    title: React.ReactNode;
    type: 'bridge' | 'manteca';
}

// Then filter by id instead
.filter((item) => {
    if (kycApprovalType === 'all') {
        return item.id !== 'manteca-qr-payments'
    }
    return item.type === kycApprovalType
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70383be and a12d51d.

📒 Files selected for processing (1)
  • src/components/Home/KycCompletedModal/index.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-11-04T17:47:06.311Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.311Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-06-22T16:10:53.167Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 915
File: src/hooks/useKycFlow.ts:96-124
Timestamp: 2025-06-22T16:10:53.167Z
Learning: The `initiateKyc` function in `src/app/actions/users.ts` already includes comprehensive error handling with try-catch blocks and returns structured responses with either `{ data }` or `{ error }` fields, so additional try-catch blocks around its usage are not needed.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Home/KycCompletedModal/index.tsx
🧬 Code graph analysis (1)
src/components/Home/KycCompletedModal/index.tsx (5)
src/context/authContext.tsx (1)
  • useAuth (192-198)
src/components/AddMoney/consts/index.ts (3)
  • CountryData (155-165)
  • MantecaSupportedExchanges (10-21)
  • countryData (292-2700)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (63-180)
src/components/Global/Icons/Icon.tsx (1)
  • IconName (70-136)
⏰ 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 (1)
src/components/Home/KycCompletedModal/index.tsx (1)

37-61: Dependency issue resolved.

The missing user dependency that was flagged in the previous review has been correctly added to the dependency array on line 61. The effect will now properly re-run when user data changes.

@Hugo0
Copy link
Contributor

Hugo0 commented Nov 7, 2025

@coderabbitai review in depth.

Evaluate:

  • do the flows make sense
  • code quality and DRY
  • possiblity of introducing new bugs or breaking existing flows

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the core hook that manages identity verification logic
cat src/hooks/useIdentityVerification.tsx

Length of output: 5870


🏁 Script executed:

#!/bin/bash
# Read the main verification views
cat src/components/Profile/views/RegionsVerification.view.tsx
cat src/components/Profile/views/RegionsPage.view.tsx
cat src/components/Profile/views/IdentityVerification.view.tsx

Length of output: 15025


🏁 Script executed:

#!/bin/bash
# Read the layout and routing files
cat src/app/\(mobile-ui\)/profile/identity-verification/layout.tsx
cat src/app/\(mobile-ui\)/profile/identity-verification/page.tsx
cat src/app/\(mobile-ui\)/profile/identity-verification/\[region\]/page.tsx
cat src/app/\(mobile-ui\)/profile/identity-verification/\[region\]/\[country\]/page.tsx

Length of output: 2929


🏁 Script executed:

#!/bin/bash
# Read key components
cat src/components/IdentityVerification/StartVerificationModal.tsx
cat src/components/Profile/components/CountryListSection.tsx
cat src/components/Profile/components/IdentityVerificationCountryList.tsx

Length of output: 10434


🏁 Script executed:

#!/bin/bash
# Read the utility function
cat src/utils/identityVerification.tsx

Length of output: 863


🏁 Script executed:

#!/bin/bash
# Check the modified Profile component to see integration
cat src/components/Profile/index.tsx

Length of output: 8520


🏁 Script executed:

#!/bin/bash
# Read the KycCompletedModal to understand the completion flow
cat src/components/Home/KycCompletedModal/index.tsx

Length of output: 4201


🏁 Script executed:

#!/bin/bash
# Check useKycStatus to understand the KYC state management
cat src/hooks/useKycStatus.tsx

Length of output: 1456


@Hugo0, I've completed an in-depth review of the verification rework. Here's my comprehensive analysis:

🔄 Flow Analysis

The overall flow architecture makes sense conceptually:

  1. Profile → Regions list → Country selection → Verification modal → KYC flow
  2. Layout-based guard for already-verified users
  3. Separate paths for Bridge (international) vs Manteca (local) KYC

However, there are several flow logic issues:

Critical Flow Issues

1. Race Condition Between Layout and View Modals

In IdentityVerificationView.tsx, the useEffect auto-opens StartVerificationModal when a country param exists:

useEffect(() => {
    if (countryParam) {
        if (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge') {
            setIsStartVerificationModalOpen(true)
        }
    }
}, [countryParam])

Meanwhile, layout.tsx checks if the user is already verified:

useEffect(() => {
    const isAlreadyVerified =
        (countryParams && isVerifiedForCountry(countryParams)) ||
        (regionParams && isRegionAlreadyUnlocked(regionParams))

    if (isAlreadyVerified) {
        setIsAlreadyVerifiedModalOpen(true)
    }
}, [countryParams, regionParams, isVerifiedForCountry, isRegionAlreadyUnlocked])

Problem: Both effects could fire simultaneously, causing modal conflicts. The layout's check should complete before the view renders.

Recommendation: Add a loading state to the layout that blocks children rendering until verification status is checked:

const [isCheckingVerification, setIsCheckingVerification] = useState(true)

useEffect(() => {
    const isAlreadyVerified = /* ... */
    setIsCheckingVerification(false)
    if (isAlreadyVerified) {
        setIsAlreadyVerifiedModalOpen(true)
    }
}, [/* deps */])

return (
    <PageContainer>
        {isCheckingVerification ? null : children}
        {/* modals */}
    </PageContainer>
)

2. Potential Navigation Loop

In IdentityVerificationView.tsx:

const handleRedirect = (isSuccess: boolean = false) => {
    const redirectUrl = getRedirectUrl()
    if (redirectUrl) {
        clearRedirectUrl()
        router.push(redirectUrl)
    } else if (isSuccess) {
        router.push('/profile')
    } else {
        router.back()  // ⚠️ Could go back to verification page
    }
}

If router.back() returns to the same verification route, users get stuck in a loop.

Recommendation: Always fallback to a safe route:

} else {
    // router.back() might loop back to verification
    router.push('/profile') 
}

3. Incomplete Type Safety for Bridge Fallback

In IdentityVerificationView.tsx:

const selectedCountryParams = useMemo(() => {
    if (countryParam) {
        const country = countryData.find(...)
        if (country) {
            return country  // CountryData type
        } else {
            return { title: 'Bridge', id: 'bridge' }  // ⚠️ Incomplete type
        }
    }
    return null
}, [countryParam])

The Bridge fallback is missing fields like currency, iso2, region, etc. This could cause runtime errors if code expects these fields.

Recommendation: Create a proper Bridge constant:

const BRIDGE_COUNTRY: CountryData = {
    id: 'bridge',
    title: 'Bridge',
    currency: '',
    iso2: '',
    // ... other required fields
}

📝 Code Quality & DRY Violations

Major DRY Issues

1. Duplicate Country Support Checking

Bridge country checking is duplicated in multiple files:

// IdentityVerificationView.tsx
const isBridgeSupportedCountry = (code: string) => {
    const upper = code.toUpperCase()
    return (
        upper === 'US' ||
        upper === 'MX' ||
        Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
        Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
    )
}

// StartVerificationModal.tsx
const isIdentityMantecaCountry = useMemo(
    () => Object.prototype.hasOwnProperty.call(MantecaSupportedExchanges, selectedIdentityCountry.id.toUpperCase()),
    [selectedIdentityCountry.id]
)

Recommendation: Extract to useIdentityVerification hook:

// In useIdentityVerification.tsx
const isBridgeSupportedCountry = useCallback((code: string) => {
    const upper = code.toUpperCase()
    return (
        upper === 'US' ||
        upper === 'MX' ||
        Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
        Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
    )
}, [])

return {
    // ... existing returns
    isBridgeSupportedCountry,
}

2. Redundant Country Data Lookups

In IdentityVerificationView.tsx, the same country lookup happens twice:

// In useEffect
const country = countryData.find((country) => country.id.toUpperCase() === countryParam.toUpperCase())

// In useMemo (selectedCountryParams)
const country = countryData.find((country) => country.id.toUpperCase() === countryParam.toUpperCase())

Recommendation: Use selectedCountryParams in the useEffect:

useEffect(() => {
    if (selectedCountryParams && countryParam) {
        if (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge') {
            setUserClickedCountry({ title: selectedCountryParams.title, id: selectedCountryParams.id })
            setIsStartVerificationModalOpen(true)
        }
    }
}, [countryParam, selectedCountryParams])

3. Magic Strings Without Constants

Strings like 'bridge', 'latam', 'manteca' appear throughout:

if (region.path !== 'latam') { ... }
if (isLatam) { ... }
return item.type === 'manteca'

Recommendation: Define constants:

// In a constants file
export const PROVIDER_TYPES = {
    BRIDGE: 'bridge',
    MANTECA: 'manteca',
} as const

export const REGION_PATHS = {
    EUROPE: 'europe',
    NORTH_AMERICA: 'north-america',
    LATAM: 'latam',
    REST_OF_WORLD: 'rest-of-the-world',
} as const

export const SPECIAL_COUNTRY_IDS = {
    BRIDGE: 'bridge',
    US: 'US',
    MX: 'MX',
} as const

4. Repeated Manteca Verification Logic

Finding active Manteca verifications is repeated in useIdentityVerification and KycCompletedModal:

// useIdentityVerification.tsx
const mantecaActive = user?.user.kycVerifications?.some(
    (v) =>
        v.provider === 'MANTECA' &&
        (v.mantecaGeo || '').toUpperCase() === upper &&
        v.status === MantecaKycStatus.ACTIVE
) ?? false

// KycCompletedModal/index.tsx
user?.user.kycVerifications?.forEach((v) => {
    if (
        v.provider === 'MANTECA' &&
        supportedCountries.includes((v.mantecaGeo || '').toUpperCase()) &&
        v.status === MantecaKycStatus.ACTIVE
    ) {
        approvedCountry = v.mantecaGeo
    }
})

Recommendation: Extract to utility function:

// utils/kycVerification.ts
export const getMantecaActiveCountry = (
    kycVerifications: KycVerification[] | undefined
): string | null => {
    if (!kycVerifications) return null
    
    const activeVerification = kycVerifications.find(
        (v) => v.provider === 'MANTECA' && v.status === MantecaKycStatus.ACTIVE
    )
    
    return activeVerification?.mantecaGeo?.toUpperCase() ?? null
}

🐛 Potential Bugs

1. Missing Dependency in useEffect

useEffect(() => {
    if (countryParam) {
        if (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge') {
            // ...
        }
    }
}, [countryParam])  // ⚠️ Missing isBridgeSupportedCountry

While isBridgeSupportedCountry is stable, it's better to include it or wrap in useCallback.

2. No Error Handling for Invalid Regions

In RegionsPage.view.tsx:

const region = lockedRegions.find((region) => region.path === path)

if (!region) {
    return null  // ⚠️ Silent failure
}

If a user accesses an invalid region or an unlocked region, they see a blank page.

Recommendation: Show an error state or redirect:

if (!region) {
    return (
        <div className="flex flex-col items-center justify-center h-full">
            <EmptyState
                title="Region not found"
                description="This region doesn't exist or you already have access to it."
                icon="globe-lock"
            />
            <Button onClick={() => router.push('/profile/identity-verification')}>
                Back to regions
            </Button>
        </div>
    )
}

3. Empty Country List Edge Case

In getCountriesForRegion:

const countries = countryData.filter((country) => country.region === region)

If no countries have the specified region, both supportedCountries and unsupportedCountries will be empty. The UI shows empty accordions with no feedback.

Recommendation: Add validation and feedback:

export const getCountriesForRegion = (region: string) => {
    const countries = countryData.filter((country) => country.region === region)
    
    if (countries.length === 0) {
        console.warn(`No countries found for region: ${region}`)
    }
    
    // ... rest of logic
    
    return { supportedCountries, unsupportedCountries, isEmpty: countries.length === 0 }
}

4. Modal State Persists on Navigation

The isStartVerificationModalOpen state doesn't reset when navigating away:

const [isStartVerificationModalOpen, setIsStartVerificationModalOpen] = useState(false)

If a user navigates back, the modal might be in an unexpected state.

Recommendation: Reset on unmount or use URL state:

useEffect(() => {
    return () => {
        setIsStartVerificationModalOpen(false)
    }
}, [])

⚠️ Breaking Changes Risk

1. Profile Menu Item Changes

// OLD (based on AI summary):
icon="shield" label="Identity Verification"

// NEW:
icon="globe-lock" label="Regions & Verification"

Any tests or E2E scripts looking for "Identity Verification" text or shield icon will break.

2. Route Structure Changes

The route /profile/identity-verification now has nested routes. Any direct links to specific flows need updating.

3. useKycStatus Discrepancy

The AI summary states isUserBridgeKycUnderReview was removed, but it's still in the code. Verify if this was intentional or if the summary is incorrect. If it was removed elsewhere, this could break dependent code.

✅ Positive Aspects

  • Good separation of concerns with the useIdentityVerification hook
  • Proper use of useMemo and useCallback for optimization
  • Layout-based guards for already-verified users
  • Clear component hierarchy
  • Proper loading states in most places

🎯 Priority Recommendations

  1. HIGH: Fix the modal race condition between layout and view
  2. HIGH: Fix navigation loop in handleRedirect
  3. HIGH: Add proper error handling for invalid regions/countries
  4. MEDIUM: Extract isBridgeSupportedCountry to the hook
  5. MEDIUM: Define constants for magic strings
  6. MEDIUM: Consolidate duplicate country lookups
  7. LOW: Extract repeated Manteca verification logic

Would you like me to create follow-up issues for any of these findings?


🧠 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.

Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 823
File: src/context/kernelClient.context.tsx:79-86
Timestamp: 2025-04-29T19:36:38.121Z
Learning: When Hugo0 asks to "resolve coderabbit comments", they want to acknowledge the comment without necessarily implementing the suggested changes, as the current implementation might be intentional for their specific use case.

Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.

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 (3)
src/components/Common/CountryList.tsx (1)

145-148: Unify Manteca support check with the hook

Since we already depend on useIdentityVerification, lean on its isMantecaSupportedCountry helper instead of re-deriving from MantecaSupportedExchanges. That avoids duplicating the lookup logic and keeps the ISO2/ISO3 normalization in one place.

-                            const isMantecaSupportedCountry = Object.keys(MantecaSupportedExchanges).includes(
-                                country.id
-                            )
+                            const isMantecaSupportedCountry = isMantecaSupportedCountryHook(country.id)

Also pull the helper in alongside the bridge check:

const {
    isBridgeSupportedCountry: isBridgeSupportedCountryHook,
    isMantecaSupportedCountry: isMantecaSupportedCountryHook,
} = useIdentityVerification()
src/components/Profile/views/IdentityVerification.view.tsx (2)

206-232: Add clarifying comment for close handler logic.

The conditional redirect behavior (lines 211-218) could benefit from a comment explaining why bridge countries trigger a redirect while others allow returning to the country list.

 onClose={() => {
-    // we dont show ID issuer country list for bridge countries
+    // Bridge country flows: user came directly with a destination country, 
+    // so closing the modal means canceling the entire verification - redirect away.
+    // Non-bridge flows: user is browsing the country list, 
+    // so they can close and select a different ID issuer country.
     if (
         isBridgeSupportedCountry(selectedCountryParams.id) ||
         selectedCountryParams.id === 'bridge'
     ) {
         handleRedirect()
     } else {
         setIsStartVerificationModalOpen(false)
     }
 }}

Note: This modal depends on selectedCountryParams having complete country data. Ensure the bridge fallback object issue (lines 111-122) is resolved first.


111-122: Consider extracting 'bridge' magic string to a constant.

The string literal 'bridge' appears multiple times (lines 118, 213). Consider defining a constant for maintainability.

+const BRIDGE_COUNTRY_ID = 'bridge'
+
 const selectedCountryParams = useMemo(() => {
     if (countryParam) {
         const country = countryData.find((country) => country.id.toUpperCase() === countryParam.toUpperCase())
         if (country) {
             return country
         } else {
-            return { title: 'Bridge', id: 'bridge', type: 'bridge', description: '', path: 'bridge' }
+            return { title: 'Bridge', id: BRIDGE_COUNTRY_ID, type: 'bridge', description: '', path: BRIDGE_COUNTRY_ID }
         }
     }
     return null
 }, [countryParam])

And use BRIDGE_COUNTRY_ID at line 213:

 if (
     isBridgeSupportedCountry(selectedCountryParams.id) ||
-    selectedCountryParams.id === 'bridge'
+    selectedCountryParams.id === BRIDGE_COUNTRY_ID
 ) {

Also applies to: 206-232

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a12d51d and 3d3f659.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • package.json (1 hunks)
  • src/app/(mobile-ui)/home/page.tsx (3 hunks)
  • src/components/ActionListCard/index.tsx (3 hunks)
  • src/components/Common/CountryList.tsx (6 hunks)
  • src/components/Profile/views/IdentityVerification.view.tsx (5 hunks)
  • src/hooks/useIdentityVerification.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/ActionListCard/index.tsx
  • src/app/(mobile-ui)/home/page.tsx
  • src/hooks/useIdentityVerification.tsx
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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/Common/CountryList.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 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/Common/CountryList.tsx
  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-14T14:36:18.758Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:0-0
Timestamp: 2025-08-14T14:36:18.758Z
Learning: Bridge API requires ISO3 country codes (3-letter codes like "USA", "GBR") while flag display components need ISO2 codes (2-letter codes like "US", "GB").

Applied to files:

  • src/components/Common/CountryList.tsx
📚 Learning: 2025-11-04T17:47:06.328Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1396
File: src/app/(mobile-ui)/home/page.tsx:295-304
Timestamp: 2025-11-04T17:47:06.328Z
Learning: In src/app/(mobile-ui)/home/page.tsx, when closing the KycCompletedModal, updateUserById is called without awaiting to provide instant feedback to the user. This fire-and-forget pattern for modal dismissals and UI preference updates is intentional and consistent across the codebase—user experience with instant UI feedback takes priority over waiting for backend sync operations.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2024-10-25T11:33:46.776Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 484
File: src/components/Cashout/Components/Initial.view.tsx:273-274
Timestamp: 2024-10-25T11:33:46.776Z
Learning: In the `InitialCashoutView` component (`src/components/Cashout/Components/Initial.view.tsx`), linked bank accounts should not generate error states, and the `ValidatedInput` component will clear any error messages if needed. Therefore, it's unnecessary to manually clear the error state when selecting or clearing linked bank accounts.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the CountryData interface from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-13T18:22:01.941Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/components/AddWithdraw/DynamicBankAccountForm.tsx:0-0
Timestamp: 2025-08-13T18:22:01.941Z
Learning: In the DynamicBankAccountForm component, the countryName parameter from useParams will always resemble a country title, not a URL slug.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-03T12:23:31.845Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1173
File: src/components/Common/SavedAccountsView.tsx:86-87
Timestamp: 2025-09-03T12:23:31.845Z
Learning: In the ALL_METHODS_DATA structure from src/components/AddMoney/consts, the path field is required (not optional) for all country entries.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
📚 Learning: 2025-08-20T09:08:19.266Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 1112
File: src/components/Claim/Link/views/BankFlowManager.view.tsx:336-343
Timestamp: 2025-08-20T09:08:19.266Z
Learning: In the KYC flow implementation, `setJustCompletedKyc` must be called after `await fetchUser()` in the `handleKycSuccess` callback. Setting `justCompletedKyc` before fetching the user would cause a re-fetching loop because `handleKycSuccess` is set in a useEffect inside the KYC hook, which would cause the UI flow to get stuck in one view.

Applied to files:

  • src/components/Profile/views/IdentityVerification.view.tsx
🧬 Code graph analysis (2)
src/components/Common/CountryList.tsx (1)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (89-264)
src/components/Profile/views/IdentityVerification.view.tsx (3)
src/hooks/useIdentityVerification.tsx (1)
  • useIdentityVerification (89-264)
src/utils/general.utils.ts (2)
  • getRedirectUrl (792-794)
  • clearRedirectUrl (796-800)
src/components/AddMoney/consts/index.ts (1)
  • countryData (292-2700)
⏰ 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 (5)
package.json (1)

37-37: Verify build configuration handles mixed Radix UI versions.

The addition of @radix-ui/react-accordion@^1.2.12 uses the latest stable version with no known security vulnerabilities. However, this package (v1) mixes with existing packages like @radix-ui/react-select@^2.2.6 (v2). While these can be used together, known issues exist with import/export compatibility, and proper bundler and tsconfig configuration is needed to handle mixed ESM/CJS exports. Ensure your build setup is configured to handle this version mix correctly.

src/components/Profile/views/IdentityVerification.view.tsx (4)

41-49: Good fix for navigation loop risk.

The fallback to router.push('/profile') instead of router.back() prevents the potential navigation loop mentioned in the PR review (where router.back() could navigate back to the verification page).


132-136: Good practice to reset modal state on unmount.

This cleanup prevents the modal state from persisting when navigating away, addressing potential stale state issues.


183-184: Clearer prompts for ID verification.

The updated text better communicates that users should select where their ID was issued.


186-192: Simplified country selection flow.

Moving the verification logic into StartVerificationModal improves separation of concerns and makes the click handler cleaner.

Comment on lines +111 to +122
// Memoized country lookup from URL param
const selectedCountryParams = useMemo(() => {
if (countryParam) {
const country = countryData.find((country) => country.id.toUpperCase() === countryParam.toUpperCase())
if (country) {
return country
} else {
return { title: 'Bridge', id: 'bridge', type: 'bridge', description: '', path: 'bridge' }
}
}
return null
}, [countryParam])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Incomplete Bridge fallback object risks runtime errors.

The fallback object at line 118 is missing fields present in CountryData entries (currency, iso2, region). If downstream code (like StartVerificationModal or other components) expects these fields, this could cause undefined behavior or errors.

Define a proper constant with all required fields:

+// At the top of the file or in a constants file
+const BRIDGE_FALLBACK_COUNTRY = {
+    title: 'Bridge',
+    id: 'bridge',
+    type: 'bridge' as const,
+    description: '',
+    path: 'bridge',
+    currency: '',
+    iso2: '',
+    region: ''
+}
+
 const selectedCountryParams = useMemo(() => {
     if (countryParam) {
         const country = countryData.find((country) => country.id.toUpperCase() === countryParam.toUpperCase())
         if (country) {
             return country
         } else {
-            return { title: 'Bridge', id: 'bridge', type: 'bridge', description: '', path: 'bridge' }
+            return BRIDGE_FALLBACK_COUNTRY
         }
     }
     return null
 }, [countryParam])
🤖 Prompt for AI Agents
In src/components/Profile/views/IdentityVerification.view.tsx around lines 111
to 122, the inline bridge fallback object is missing fields present on
CountryData (e.g., currency, iso2, region) which can cause downstream undefined
errors; replace the inline object with a properly defined constant (typed to the
CountryData shape) that includes all required fields (title, id, type,
description, path, currency, iso2, region, etc.) and use that constant in the
useMemo return; ensure the constant’s types match the CountryData interface and
export or reuse it where needed so downstream components always receive a
complete object.

Comment on lines +124 to +130
// Skip country selection if coming from a supported bridge country
useEffect(() => {
if (selectedCountryParams && (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge')) {
setUserClickedCountry({ title: selectedCountryParams.title, id: selectedCountryParams.id })
setIsStartVerificationModalOpen(true)
}
}, [countryParam, isBridgeSupportedCountry, selectedCountryParams])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify no modal conflicts with layout verification guard.

The PR review notes a potential race condition where the layout guard (checking already-verified users) and this auto-opening modal can fire simultaneously. Ensure the parent layout blocks rendering this component until verification checks complete, or coordinate modal state between layout and view.

Run the following script to examine the layout file for verification guards:


🏁 Script executed:

#!/bin/bash
# Description: Check for verification status checks in the identity verification layout

# Find the layout file for this route
fd -e tsx -e ts "layout" src/app | rg -i "identity|verification" | head -5

# Search for verification checks in layout files
rg -A 10 -B 3 "isUserBridgeKycApproved|isUserMantecaKycApproved|kycVerifications" --type tsx src/app | rg -C 5 "layout"

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Description: Examine the identity verification layout for verification guards

# Read the layout.tsx file directly
echo "=== Layout file content ==="
cat src/app/\(mobile-ui\)/profile/identity-verification/layout.tsx

# Search for verification-related checks in the directory
echo ""
echo "=== Verification status checks in identity-verification route ==="
rg "isUserBridgeKycApproved|isUserMantecaKycApproved|kycVerifications|isKycApproved" -A 5 -B 2 src/app/\(mobile-ui\)/profile/identity-verification/ 2>/dev/null || echo "No direct checks found"

# Look for redirect or conditional rendering logic
echo ""
echo "=== Redirect or guard patterns ==="
rg "redirect|useRouter|if.*verified|if.*kyc" -i -A 3 -B 1 src/app/\(mobile-ui\)/profile/identity-verification/ 2>/dev/null || echo "No redirect patterns found"

Length of output: 275


🏁 Script executed:

#!/bin/bash
# Description: Find and examine identity verification route files

# Find the layout file using fd with proper path handling
echo "=== Finding layout file ==="
LAYOUT_FILE=$(fd "layout.tsx" src/app/ | grep identity-verification)
echo "Found layout file: $LAYOUT_FILE"

# Read it if found
if [ -n "$LAYOUT_FILE" ]; then
  echo ""
  echo "=== Layout file content ==="
  cat "$LAYOUT_FILE"
fi

# Find the IdentityVerification.view.tsx file
echo ""
echo "=== Finding IdentityVerification view file ==="
VIEW_FILE=$(fd "IdentityVerification.view.tsx" src/components/)
echo "Found view file: $VIEW_FILE"

# List all files in identity-verification route
echo ""
echo "=== Files in identity-verification route ==="
fd "." src/app | grep identity-verification | head -20

# Search broadly for verification status checks that might trigger redirects
echo ""
echo "=== Verification status checks in profile route ==="
rg "isUserBridgeKycApproved|isUserMantecaKycApproved" -B 3 -A 5 src/app/ 2>/dev/null | head -40

Length of output: 5886


Coordinate modal state or gate component rendering to prevent simultaneous modals.

The layout's verification guard (isVerifiedForCountry) and the view's auto-opening modal (isBridgeSupportedCountry) can both trigger independently. The layout renders children even when showing the "already verified" modal, creating a race condition where both modals may open simultaneously. Either:

  • Prevent rendering the view component until verification checks complete in the layout, or
  • Add shared state to ensure only one modal is active at a time.
🤖 Prompt for AI Agents
In src/components/Profile/views/IdentityVerification.view.tsx around lines
124-130, the view auto-opens the start-verification modal based solely on
selectedCountryParams/isBridgeSupportedCountry which can race with the layout's
isVerifiedForCountry modal; update the component to avoid opening its modal
while the layout's verification check/modal is active by either: (1) accepting a
prop or reading shared state (e.g., from context or a parent flag) that
indicates verification check/other modal is open and early-returning (or
skipping the useEffect) while that flag is true, or (2) modifying the useEffect
to include the layout verification status (isVerifiedForCountry or a new
isVerificationCheckComplete flag) in its dependency list and only call
setIsStartVerificationModalOpen when the verification check is complete and not
already showing another modal; ensure the new flag is passed/consumed
consistently so only one modal can be active at a time.

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

🧹 Nitpick comments (2)
src/hooks/useIdentityVerification.tsx (2)

59-74: Consider hosting flag images locally.

The flag icons use an external CDN (flagcdn.com) which creates a runtime dependency on a third-party service. If the service experiences downtime, flags won't load.

Consider hosting these flag images locally in the /assets directory (similar to the globe icons) for better reliability and performance.


21-21: Consider extracting KYC provider names as constants.

The string literals 'bridge' and 'manteca' (and 'MANTECA') appear multiple times throughout the file. Extracting these as constants would improve maintainability and reduce typo risks.

Consider adding constants at the top of the file:

const KYC_PROVIDER = {
  BRIDGE: 'bridge',
  MANTECA: 'manteca',
  MANTECA_UPPER: 'MANTECA',
} as const

Then use them throughout:

  • Line 21: type: typeof KYC_PROVIDER.BRIDGE | typeof KYC_PROVIDER.MANTECA
  • Line 129: v.provider === KYC_PROVIDER.MANTECA_UPPER
  • Lines 212-247: type: KYC_PROVIDER.BRIDGE or type: KYC_PROVIDER.MANTECA

Also applies to: 129-129, 212-247

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3f659 and 2089f0e.

📒 Files selected for processing (1)
  • src/hooks/useIdentityVerification.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 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".
🧬 Code graph analysis (1)
src/hooks/useIdentityVerification.tsx (3)
src/context/authContext.tsx (1)
  • useAuth (192-198)
src/hooks/useKycStatus.tsx (1)
  • useKycStatus (12-36)
src/components/AddMoney/consts/index.ts (3)
  • MantecaSupportedExchanges (10-21)
  • countryData (292-2700)
  • BRIDGE_ALPHA3_TO_ALPHA2 (2731-2773)
⏰ 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 (7)
src/hooks/useIdentityVerification.tsx (7)

10-22: LGTM! Well-documented type definitions.

The type definitions are clear and well-documented with JSDoc comments.


106-109: LGTM! Safe implementation for checking Manteca support.

The callback correctly uses Object.prototype.hasOwnProperty.call for safe property checking and has the correct empty dependency array.


121-138: LGTM! Correct verification logic with proper dependencies.

The callback correctly distinguishes between Manteca (country-specific) and Bridge (general) verification requirements, and the dependency array includes all necessary external values.


149-174: LGTM! Well-structured region unlock logic.

The useMemo correctly calculates locked and unlocked regions based on KYC status, including the special case for Bridge users getting QR access to Argentina and Brazil. Dependencies are correct.


181-195: LGTM! Correctly implemented utility callbacks.

Both isRegionAlreadyUnlocked and getCountryTitle are properly memoized with correct dependency arrays.


204-250: LGTM! Comprehensive unlock items list.

The callback provides a well-documented list of features unlocked by verification, including the important note about Manteca bank transfers using the user's verified ID country (lines 239-241).


47-50: Confirmed: "Rest of the world" region is always locked and never unlocked.

The region is defined in SUPPORTED_REGIONS but excluded from both BRIDGE_SUPPORTED_REGIONS (containing only 'North America' and 'Europe') and MANTECA_SUPPORTED_REGIONS (containing only 'LATAM'). The unlock logic at lines 154-159 only unlocks regions present in these arrays, so "Rest of the world" will always appear in lockedRegions.

Verify with the team whether this is intentional (e.g., placeholder for future expansion) or requires unlock logic.

Comment on lines +252 to +260
const isBridgeSupportedCountry = (code: string) => {
const upper = code.toUpperCase()
return (
upper === 'US' ||
upper === 'MX' ||
Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Wrap isBridgeSupportedCountry in useCallback for consistency.

This function is returned from the hook but isn't wrapped in useCallback, causing it to be recreated on every render. This breaks referential equality and can cause unnecessary re-renders in consuming components that depend on this function.

Apply this diff to wrap the function in useCallback:

-    const isBridgeSupportedCountry = (code: string) => {
+    const isBridgeSupportedCountry = useCallback((code: string) => {
         const upper = code.toUpperCase()
         return (
             upper === 'US' ||
             upper === 'MX' ||
             Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
             Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
         )
-    }
+    }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isBridgeSupportedCountry = (code: string) => {
const upper = code.toUpperCase()
return (
upper === 'US' ||
upper === 'MX' ||
Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
)
}
const isBridgeSupportedCountry = useCallback((code: string) => {
const upper = code.toUpperCase()
return (
upper === 'US' ||
upper === 'MX' ||
Object.keys(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper) ||
Object.values(BRIDGE_ALPHA3_TO_ALPHA2).includes(upper)
)
}, [])
🤖 Prompt for AI Agents
In src/hooks/useIdentityVerification.tsx around lines 252 to 260, the
isBridgeSupportedCountry function is defined inline and returned from the hook
but not memoized; wrap it in React's useCallback to preserve referential
equality across renders by replacing the plain function with a useCallback hook
that returns the same implementation and include BRIDGE_ALPHA3_TO_ALPHA2 (and
React if needed) in the dependency array so the callback updates if that mapping
changes.

@kushagrasarathe
Copy link
Contributor

lgtm

( actually some concerns around states and DRY, but we can handle that later, need to merge this and test for prod release, overall nothing entirely wrong pops-up )

image

@kushagrasarathe kushagrasarathe merged commit c64475c into peanut-wallet-dev Nov 7, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 16, 2026
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