[TASK-16296] Feat: Verification rework#1396
[TASK-16296] Feat: Verification rework#1396kushagrasarathe merged 16 commits intopeanut-wallet-devfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds 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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
latamregion 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,
concatis 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 forincludes()checks are harmless), using aSetwould 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
hasinstead ofincludes:- 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_ALPHA2if 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
⛔ Files ignored due to path filters (5)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/icons/europe-globe.svgis excluded by!**/*.svgsrc/assets/icons/latam-globe.svgis excluded by!**/*.svgsrc/assets/icons/north-america-globe.svgis excluded by!**/*.svgsrc/assets/icons/rest-of-world-globe.svgis 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.tsxsrc/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.tsxsrc/app/(mobile-ui)/profile/identity-verification/[region]/page.tsxsrc/components/Profile/components/IdentityVerificationCountryList.tsxsrc/app/(mobile-ui)/profile/identity-verification/page.tsxsrc/components/Profile/views/RegionsPage.view.tsxsrc/app/(mobile-ui)/profile/identity-verification/layout.tsxsrc/components/Profile/components/CountryListSection.tsxsrc/components/Profile/views/RegionsVerification.view.tsxsrc/components/Common/CountryList.tsxsrc/components/IdentityVerification/StartVerificationModal.tsxsrc/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.tsxsrc/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.tsxsrc/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.tsxsrc/utils/identityVerification.tsxsrc/components/AddMoney/consts/index.tssrc/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.tsxsrc/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.tsxsrc/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.tsxsrc/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
currentColorfor 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 ofsrc/hooks/useKycStatus.tsx. The component insrc/components/Profile/index.tsxintentionally destructures onlyisUserKycApproved, 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
inputDescriptionprop 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
RegionsVerificationdirectly 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
regionroute parameter usinguseParamsand passes it to theRegionsPageview 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
IdentityVerificationViewcomponent already usesuseParams()internally (line 37 ofsrc/components/Profile/views/IdentityVerification.view.tsx) and extracts thecountryparameter 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. InRegionsVerification.view.tsx, the button that navigates to a region only executes whenisLockedis true. This means only locked regions can trigger navigation to/profile/identity-verification/${region.path}.RegionsPage's implementation—searching only
lockedRegionsand returningnullif 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.
There was a problem hiding this comment.
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 unnecessaryhrefprop when usingonClickhandler.The
href="/dummy"is unnecessary. TheProfileMenuItemcomponent'shrefprop is optional, and whenonClickis provided, the component uses the click handler directly without rendering aLinkwrapper. Simply remove thehrefprop:<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 unuseddefaultOpenprop.The
defaultOpenprop 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 parentAccordion.Rootcomponent usingdefaultValue.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
📒 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 parentAccordion.Rootwrapper.This component renders a single
Accordion.Itemand must be wrapped in aAccordion.Rootcomponent to function properly. Based on the PR context, this is handled by the parentIdentityVerificationCountryList.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.comvianext.config.jswildcard 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
isUserKycApprovedfrom the updateduseKycStatushook, consistent with the removal ofisUserBridgeKycUnderReviewfrom 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 usesicon="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.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/components/Profile/views/RegionsVerification.view.tsx (1)
57-57: Past review comment resolved.The missing
keyproperty flagged in the previous review has been correctly addressed. Usingregion.pathas 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-pointerand hover effects fromActionListCard, but the onClick handler does nothing whenisLockedis 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
📒 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
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/components/Profile/views/IdentityVerification.view.tsx (1)
114-122: Consider memoizing the helper function.The
isBridgeSupportedCountryfunction is recreated on every render. While this doesn't cause functional issues, wrapping it inuseCallbackwould 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
📒 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.tsxsrc/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.tsxsrc/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
inputDescriptionandshowLoadingStateprops 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 whenshowLoadingStateis 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 && selectedCountryParamsensures 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
handleRedirectsignature with theisSuccessparameter provides clear separation between success and cancellation paths. The integration with CountryList (usingshowLoadingState={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
There was a problem hiding this comment.
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
VerificationUnlockItemtype 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
selectedCountryobject in the dependency array. If the object reference changes but theidremains 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
📒 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.tsxsrc/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.tsxsrc/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.
There was a problem hiding this comment.
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 theIconNameunion 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
titleproperty. While this works, it's fragile—a minor wording change ingetVerificationUnlockItemswould silently break the filter.Consider enhancing the
VerificationUnlockItemtype with an explicitidorkeyfield 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
📒 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
userdependency 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.
|
@coderabbitai review in depth. Evaluate:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 5870 🏁 Script executed: Length of output: 15025 🏁 Script executed: Length of output: 2929 🏁 Script executed: Length of output: 10434 🏁 Script executed: Length of output: 863 🏁 Script executed: Length of output: 8520 🏁 Script executed: Length of output: 4201 🏁 Script executed: Length of output: 1456 @Hugo0, I've completed an in-depth review of the verification rework. Here's my comprehensive analysis: 🔄 Flow AnalysisThe overall flow architecture makes sense conceptually:
However, there are several flow logic issues: Critical Flow Issues1. Race Condition Between Layout and View Modals In useEffect(() => {
if (countryParam) {
if (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge') {
setIsStartVerificationModalOpen(true)
}
}
}, [countryParam])Meanwhile, 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 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 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 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 Recommendation: Create a proper Bridge constant: const BRIDGE_COUNTRY: CountryData = {
id: 'bridge',
title: 'Bridge',
currency: '',
iso2: '',
// ... other required fields
}📝 Code Quality & DRY ViolationsMajor DRY Issues1. 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 // 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 // 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 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 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 const4. Repeated Manteca Verification Logic Finding active Manteca verifications is repeated in // 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 Bugs1. Missing Dependency in useEffect useEffect(() => {
if (countryParam) {
if (isBridgeSupportedCountry(countryParam) || countryParam === 'bridge') {
// ...
}
}
}, [countryParam]) // ⚠️ Missing isBridgeSupportedCountryWhile 2. No Error Handling for Invalid Regions In 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 const countries = countryData.filter((country) => country.region === region)If no countries have the specified region, both 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 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)
}
}, [])
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/components/Common/CountryList.tsx (1)
145-148: Unify Manteca support check with the hookSince we already depend on
useIdentityVerification, lean on itsisMantecaSupportedCountryhelper instead of re-deriving fromMantecaSupportedExchanges. 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
selectedCountryParamshaving 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_IDat 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.tsxsrc/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.tsxsrc/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.12uses 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 ofrouter.back()prevents the potential navigation loop mentioned in the PR review (whererouter.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.
| // 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]) |
There was a problem hiding this comment.
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.
| // 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]) |
There was a problem hiding this comment.
🧩 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 -40Length 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.
There was a problem hiding this comment.
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
/assetsdirectory (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 constThen 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.BRIDGEortype: KYC_PROVIDER.MANTECAAlso applies to: 129-129, 212-247
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.callfor 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
useMemocorrectly 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
isRegionAlreadyUnlockedandgetCountryTitleare 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_REGIONSbut excluded from bothBRIDGE_SUPPORTED_REGIONS(containing only 'North America' and 'Europe') andMANTECA_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 inlockedRegions.Verify with the team whether this is intentional (e.g., placeholder for future expansion) or requires unlock logic.
| 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) | ||
| ) | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| 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.

Uh oh!
There was an error while loading. Please reload this page.