Conversation
|
Caution Review failedThe pull request is closed. WalkthroughReplaces BusinessIntegrate with DropLink in landing exports and page composition; adds RegulatedRails and Footer; introduces new landing components, icons/illustrations, a currency picker, exchange-rate API + hook, country-currency mappings, an Exchange page, and a comparison script. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these settings in your CodeRabbit configuration. 📒 Files selected for processing (2)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@Hugo0 exchange rate widget API integration and incomplete and on small hand icon is missing in your money component. Rest everything is done |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (10)
src/app/api/bridge/exchange-rate/route.ts (1)
46-52: Consider more specific error handling.The current implementation forwards the Bridge API's HTTP status code directly. Consider mapping specific Bridge API error codes to more appropriate client-facing status codes if needed.
For example:
if (!response.ok) { console.error(`Bridge API error: ${response.status} ${response.statusText}`) + const statusCode = response.status === 404 ? 400 : response.status return NextResponse.json( { error: 'Failed to fetch exchange rates from Bridge API' }, - { status: response.status } + { status: statusCode } ) }src/components/LandingPage/dropLink.tsx (1)
7-8: Consider moving the color constant to a design system or theme file.The hardcoded color
#90A8EDshould ideally be part of a centralized design system or theme configuration for better maintainability and consistency across components.-// Define the background color as a constant -const businessBgColor = '#90A8ED'Move this to a theme file or use Tailwind's color system:
// In a theme/colors.ts file export const colors = { business: '#90A8ED' }src/components/LandingPage/Footer.tsx (2)
68-68: Incorrect alt text for hand middle finger image.The alt text says "Hand waving" but should describe the actual image content.
- <Image src={handMiddleFinger.src} alt="Hand waving" width={20} height={20} /> + <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} />
60-70: Consider accessibility for decorative hand icons.Most hand icons appear to be decorative except for the clickable peace sign. Consider adding
aria-hidden="true"to decorative images and proper accessibility attributes to the clickable one.<Image src={handPeace.src} alt="Hand peace" width={20} height={20} + role="button" + tabIndex={0} + onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') window.open('https://youtube.com/shorts/qd2FbzLS380?si=T5xk7xrTGYiIiWFu', '_blank') }} onClick={() => window.open('https://youtube.com/shorts/qd2FbzLS380?si=T5xk7xrTGYiIiWFu', '_blank')} /> - <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} /> + <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} aria-hidden="true" /> - <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} /> + <Image src={handMiddleFinger.src} alt="Hand middle finger" width={20} height={20} aria-hidden="true" /> - <Image src={handWaving.src} alt="Hand waving" width={25} height={25} /> + <Image src={handWaving.src} alt="Hand waving" width={25} height={25} aria-hidden="true" />src/components/LandingPage/CurrencySelect.tsx (3)
15-31: Consider loading currency data from an external source.The hardcoded currency list limits scalability. As the component grows, consider loading currency data from a configuration file or API for better maintainability.
Create a separate file for currency data:
// utils/currencies.ts export const currencies = [ // ... currency data ]
22-24: Incorrect country name for EUR."Euro" is not a country name. Consider using "European Union" or a more appropriate designation.
{ countryCode: 'eu', - country: 'Euro', + country: 'European Union', currency: 'EUR', },
54-54: Clean up commented code.The commented height value should be removed or properly documented if it's intended for future use.
- height={'52'} //{'72'} commented out for now, this will be final height when we add all the currencies + height={'52'}If this is a planned change, consider adding a proper TODO comment:
- height={'52'} //{'72'} commented out for now, this will be final height when we add all the currencies + height={'52'} // TODO: Change to '72' when adding all currenciessrc/components/LandingPage/yourMoney.tsx (1)
20-48: Consider removing unused features arrayThe
featuresarray and its associated imports (lines 1-6, 11-48) are no longer used in the component but remain in the code.Remove the unused code to improve maintainability:
-import iphoneYourMoney1 from '@/assets/iphone-ss/iphone-your-money-1.png' -import iphoneYourMoney2 from '@/assets/iphone-ss/iphone-your-money-2.png' -import iphoneYourMoney3 from '@/assets/iphone-ss/iphone-your-money-3.png' -import freeGlobalTransfers from '@/assets/illustrations/free-global-transfers.svg' -import payAnyoneAnywhere from '@/assets/illustrations/pay-anyone-anywhere.svg' -import getPaidWorldwide from '@/assets/illustrations/get-paid-worldwide.svg' import Image from 'next/image' import { LandingCountries } from '@/assets' import { Button } from '../0_Bruddle' -interface Feature { - id: number - title: string - titleSvg: any - description: string - imageSrc: any - imageAlt: string -} - -const features: Feature[] = [ - { - id: 1, - title: 'FREE GLOBAL TRANSFERS', - titleSvg: freeGlobalTransfers, - description: - 'Move money between your own accounts in 140+ countries and 50+ currencies, no fees, live FX rates.', - imageSrc: iphoneYourMoney1, - imageAlt: 'iPhone showing global transfer screen', - }, - { - id: 2, - title: 'PAY ANYONE, ANYWHERE', - titleSvg: payAnyoneAnywhere, - description: - 'Send funds in seconds through WhatsApp, a phone number, or a QR code. No bank details, no friction.', - imageSrc: iphoneYourMoney2, - imageAlt: 'iPhone showing payment options screen', - }, - { - id: 3, - title: 'GET PAID WORLDWIDE', - titleSvg: getPaidWorldwide, - description: - 'Get paid by clients in 140+ countries, direct to your account, and settle in the currency you prefer.', - imageSrc: iphoneYourMoney3, - imageAlt: 'iPhone showing payment request screen', - }, -] - export function YourMoney() {src/components/LandingPage/noFees.tsx (2)
252-256: Format exchange rate display for consistencyThe exchange rate should be formatted to a consistent number of decimal places for better readability.
{destinationAmount > 0 && ( <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1"> - 1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency} + 1 {sourceCurrency} = {currentExchangeRate.toFixed(4)} {destinationCurrency} </div> )}
234-234: Format destination amount displayThe destination amount should be formatted to 2 decimal places for currency display consistency.
- value={destinationAmount} + value={destinationAmount.toFixed(2)}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (17)
src/assets/icons/bbva-logo.svgis excluded by!**/*.svgsrc/assets/icons/brubank-logo.svgis excluded by!**/*.svgsrc/assets/icons/github-white.pngis excluded by!**/*.pngsrc/assets/icons/mercado-pago-logo.svgis excluded by!**/*.svgsrc/assets/icons/n26-logo.svgis excluded by!**/*.svgsrc/assets/icons/pix-logo.svgis excluded by!**/*.svgsrc/assets/icons/revolut-logo.svgis excluded by!**/*.svgsrc/assets/icons/santander-logo.svgis excluded by!**/*.svgsrc/assets/icons/stripe-logo.svgis excluded by!**/*.svgsrc/assets/icons/wise-logo.svgis excluded by!**/*.svgsrc/assets/illustrations/hand-middle-finger.svgis excluded by!**/*.svgsrc/assets/illustrations/landing-countries.svgis excluded by!**/*.svgsrc/assets/illustrations/mobile-send-in-seconds.svgis excluded by!**/*.svgsrc/assets/illustrations/no-hidden-fees.svgis excluded by!**/*.svgsrc/assets/illustrations/pay-zero-fees.svgis excluded by!**/*.svgsrc/assets/iphone-ss/iphone-drop-a-link-mobile.pngis excluded by!**/*.pngsrc/assets/iphone-ss/iphone-drop-a-link.pngis excluded by!**/*.png
📒 Files selected for processing (16)
src/app/api/bridge/exchange-rate/route.ts(1 hunks)src/app/page.tsx(3 hunks)src/assets/icons/index.ts(1 hunks)src/assets/illustrations/index.ts(1 hunks)src/components/Global/Icons/Icon.tsx(3 hunks)src/components/Global/Icons/chevron-down.tsx(1 hunks)src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/Footer.tsx(1 hunks)src/components/LandingPage/RegulatedRails.tsx(1 hunks)src/components/LandingPage/businessIntegrate.tsx(0 hunks)src/components/LandingPage/dropLink.tsx(1 hunks)src/components/LandingPage/imageAssets.tsx(5 hunks)src/components/LandingPage/index.ts(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/components/LandingPage/securityBuiltIn.tsx(2 hunks)src/components/LandingPage/yourMoney.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- src/components/LandingPage/businessIntegrate.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/components/LandingPage/yourMoney.tsx
🧬 Code Graph Analysis (7)
src/app/api/bridge/exchange-rate/route.ts (1)
src/app/actions/exchange-rate.ts (1)
ExchangeRateResponse(8-15)
src/components/Global/Icons/Icon.tsx (1)
src/components/Global/Icons/chevron-down.tsx (1)
ChevronDownIcon(3-10)
src/components/LandingPage/CurrencySelect.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)
src/components/LandingPage/dropLink.tsx (1)
src/components/0_Bruddle/Button.tsx (1)
Button(75-266)
src/components/LandingPage/RegulatedRails.tsx (1)
src/components/Global/MarqueeWrapper/index.tsx (1)
MarqueeWrapper(16-33)
src/components/LandingPage/yourMoney.tsx (1)
src/components/0_Bruddle/Button.tsx (1)
Button(75-266)
src/app/page.tsx (5)
src/components/LandingPage/RegulatedRails.tsx (1)
RegulatedRails(34-127)src/components/LandingPage/marquee.tsx (1)
Marquee(11-24)src/components/LandingPage/yourMoney.tsx (1)
YourMoney(50-87)src/components/LandingPage/securityBuiltIn.tsx (1)
SecurityBuiltIn(48-91)src/components/LandingPage/faq.tsx (1)
FAQs(16-44)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 32-32: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 38-38: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 45-45: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
[error] 52-52: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
src/components/LandingPage/RegulatedRails.tsx
[error] 119-119: 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)
🔇 Additional comments (18)
src/components/LandingPage/imageAssets.tsx (2)
4-4: LGTM! Clean asset replacement.The import change from
CloudtoborderCloudaligns with the updated visual design.
59-59: LGTM! Consistent asset usage throughout.All cloud animations now consistently use the new
borderCloudasset, maintaining visual coherence across the landing page.Also applies to: 68-68, 89-89, 99-99
src/components/Global/Icons/chevron-down.tsx (1)
3-10: LGTM! Well-implemented SVG icon component.The component follows React best practices:
- Proper TypeScript typing with
SVGProps<SVGSVGElement>- Uses
currentColorfor flexible theming- Spreads props correctly for customization
- Clean, semantic SVG structure
src/app/api/bridge/exchange-rate/route.ts (2)
27-31: LGTM! Proper environment variable validation.Good security practice to validate the API key presence before making external requests.
34-36: LGTM! Appropriate parameter validation.Proper validation of required query parameters with clear error messaging.
src/components/LandingPage/securityBuiltIn.tsx (4)
21-24: LGTM! Content update aligns with new messaging.The updated title "TOTAL SECURITY" and description clearly communicate the self-custodial nature and passkey authentication features.
30-33: LGTM! Clear messaging about KYC approach.The "TRUE CONTROL" title and description effectively communicate the selective KYC approach, emphasizing user control and reduced friction.
39-42: LGTM! Support messaging is clear and compelling.The "24/7 HELP" title and description emphasize human support accessibility, which is a strong differentiator.
53-55: LGTM! Consistent heading alignment.The updated heading "SECURITY. CONTROL. SUPPORT." with left alignment maintains visual consistency with the content structure.
src/assets/illustrations/index.ts (1)
14-14: LGTM! Clean export addition.The new
LandingCountriesexport follows the existing pattern and supports the landing page redesign.src/components/Global/Icons/Icon.tsx (1)
58-58: LGTM! Clean icon implementation.The new
chevron-downicon is properly integrated with correct import, type definition, and component mapping. The implementation follows the established pattern consistently.Also applies to: 118-118, 183-183
src/components/LandingPage/index.ts (1)
1-1: LGTM! Export replacement aligns with component refactoring.The export change from
businessIntegratetodropLinkis consistent with the component replacement mentioned in the AI summary and maintains the public API structure.src/components/LandingPage/dropLink.tsx (1)
46-56: LGTM! Smooth animation implementation.The Framer Motion animation provides a subtle, engaging effect that enhances the user experience without being distracting. The configuration with rotation and translation creates a natural floating appearance.
src/app/page.tsx (3)
5-13: Component import reorganization looks goodThe updated imports properly reflect the landing page reorganization, replacing
BusinessIntegratewithDropLinkand adding the new components.
191-195: Good structural improvements to the landing page flowThe reordering of components creates a better narrative flow:
- Hero → NoFees (engagement)
- RegulatedRails (trust/compliance)
- YourMoney → SecurityBuiltIn (features)
- SendInSeconds → DropLink (CTAs)
This structure progressively builds trust while maintaining user engagement through the Marquee separators.
203-203: FAQs component correctly receives marquee propThe
marqueeprop is properly passed to the FAQs component withvisible: falseto disable the internal marquee.src/components/LandingPage/yourMoney.tsx (1)
53-85: Effective redesign with improved visual hierarchyThe new two-column layout with the overlaid CTA button creates better visual engagement and clearer user action paths. The positioning of the button over the countries illustration is particularly effective.
src/assets/icons/index.ts (1)
10-19: New icon exports properly addedThe bank and payment provider icons are correctly exported and align with the landing page requirements.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (4)
src/components/LandingPage/noFees.tsx (3)
208-216: Improve amount input UX: handle empty input and limit decimals; add step/placeholderThis avoids flickering to 0 while typing empty values and standardizes to 2 decimals. Complements the earlier validation comment.
<input min={0} value={sourceAmount} - onChange={(e) => { - const value = parseFloat(e.target.value) - setSourceAmount(isNaN(value) || value < 0 ? 0 : value) - }} + onChange={(e) => { + const raw = e.target.value + if (raw === '') { + setSourceAmount(0) + return + } + const num = Number(raw) + if (Number.isFinite(num) && num >= 0) { + // limit to 2 decimals + setSourceAmount(Math.round(num * 100) / 100) + } + }} type="number" + step="0.01" + placeholder="0.00" className="w-full bg-transparent outline-none" />
105-116: Make exchange-rate fetch resilient: add try/catch, response.ok check, abort on unmount, and guard against stale/invalid dataBuilds on the earlier bot comment. Also avoids race conditions when params change quickly and prevents NaN from reaching state. Prefer
toLowerCase()for deterministic casing.- useEffect(() => { - const fetchExchangeRate = async () => { - const _sourceCurrency = sourceCurrency.toLocaleLowerCase() - const _destinationCurrency = destinationCurrency.toLocaleLowerCase() - const response = await fetch(`/api/bridge/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`) - const data = await response.json() - setCurrentExchangeRate(data.rates.buy) - setDestinationAmount(debouncedSourceAmount * data.rates.buy) - } - - fetchExchangeRate() - }, [sourceCurrency, destinationCurrency, debouncedSourceAmount]) + useEffect(() => { + let isMounted = true + const controller = new AbortController() + + const fetchExchangeRate = async () => { + try { + const from = sourceCurrency.toLowerCase() + const to = destinationCurrency.toLowerCase() + if (!from || !to) return + + const response = await fetch( + `/api/bridge/exchange-rate?from=${from}&to=${to}`, + { signal: controller.signal } + ) + if (!response.ok) { + // Keep previous rate, reset destination amount for safety + if (isMounted) { + setCurrentExchangeRate(0) + setDestinationAmount(0) + } + return + } + + const data = await response.json() + const rate = Number(data?.rates?.buy) + if (!Number.isFinite(rate) || rate <= 0) { + if (isMounted) { + setCurrentExchangeRate(0) + setDestinationAmount(0) + } + return + } + + if (isMounted) { + setCurrentExchangeRate(rate) + setDestinationAmount(Number.isFinite(debouncedSourceAmount) ? debouncedSourceAmount * rate : 0) + } + } catch (err: any) { + if (err?.name !== 'AbortError') { + console.error('Error fetching exchange rate:', err) + } + if (isMounted) { + setCurrentExchangeRate(0) + setDestinationAmount(0) + } + } + } + + fetchExchangeRate() + return () => { + isMounted = false + controller.abort() + } + }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])
272-274: Wire up the Send Money button (and disable when not ready)Currently non-functional; route to the appropriate page with current params and guard against empty/invalid amounts.
- <Button icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold"> + <Button + icon="arrow-up-right" + iconSize={13} + shadowSize="4" + className="w-full text-base font-bold" + disabled={!Number.isFinite(destinationAmount) || destinationAmount <= 0} + onClick={() => { + // TODO: confirm destination route + router.push(`/setup?from=${encodeURIComponent(sourceCurrency)}&to=${encodeURIComponent(destinationCurrency)}&amount=${encodeURIComponent(String(sourceAmount))}`) + }} + > Send Money </Button>If the actual route differs (e.g., /send or /request/pay), please confirm the correct path and I’ll update the handler accordingly.
src/components/LandingPage/Footer.tsx (1)
18-20: Add missing rel="noopener noreferrer" to external link (security).The Squirrel Labs link opens a new tab without rel, which enables reverse-tabnabbing.
Apply this diff:
- <a className="underline" href="https://squirrellabs.dev/" target="_blank"> + <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
🧹 Nitpick comments (3)
src/components/LandingPage/noFees.tsx (2)
90-103: Remove unused parameter from createCloudAnimation signatureThe
toparg isn’t used insidecreateCloudAnimation(positioning is done via thestyleprop at call sites). Simplify the signature and call sites.- const createCloudAnimation = (side: 'left' | 'right', top: string, width: number, speed: number) => { + const createCloudAnimation = (side: 'left' | 'right', width: number, speed: number) => { const vpWidth = screenWidth || 1080 const totalDistance = vpWidth + width return { initial: { x: side === 'left' ? -width : vpWidth }, animate: { x: side === 'left' ? vpWidth : -width }, transition: { ease: 'linear', duration: totalDistance / speed, repeat: Infinity, }, } } @@ - {...createCloudAnimation('left', '20%', 200, 35)} + {...createCloudAnimation('left', 200, 35)} @@ - {...createCloudAnimation('right', '60%', 220, 40)} + {...createCloudAnimation('right', 220, 40)}Also applies to: 127-135
252-256: Format the displayed rate for readabilityShow a bounded number of fractional digits.
- 1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency} + 1 {sourceCurrency} = {new Intl.NumberFormat(undefined, { maximumFractionDigits: 6 }).format(currentExchangeRate)} {destinationCurrency}src/components/LandingPage/Footer.tsx (1)
26-29: Social icons are non-interactive; either link them or mark them decorative.If these are intended to link to your socials, wrap each in with href/rel/aria-label. If they’re purely decorative, set alt="" and aria-hidden="true".
Example (link version):
<a href="https://discord.gg/your-invite" target="_blank" rel="noopener noreferrer" aria-label="Discord"> <Image src={DISCORD_ICON} alt="" width={20} height={20} /> </a>Example (decorative version):
- <Image src={DISCORD_ICON} alt="Discord" width={20} height={20} /> - <Image src={TWITTER_ICON} alt="Twitter" width={20} height={20} /> - <Image src={GITHUB_WHITE_ICON} alt="Github" width={20} height={20} /> + <Image src={DISCORD_ICON} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={TWITTER_ICON} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={GITHUB_WHITE_ICON} alt="" width={20} height={20} aria-hidden="true" />If you keep meaningful alt text later, prefer “GitHub” casing.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/assets/illustrations/landing-countries.svgis excluded by!**/*.svg
📒 Files selected for processing (4)
src/components/LandingPage/Footer.tsx(1 hunks)src/components/LandingPage/RegulatedRails.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/components/LandingPage/yourMoney.tsx(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/components/LandingPage/RegulatedRails.tsx
- src/components/LandingPage/yourMoney.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
🧬 Code Graph Analysis (1)
src/components/LandingPage/noFees.tsx (3)
src/hooks/useDebounce.ts (1)
useDebounce(9-23)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)src/components/0_Bruddle/Button.tsx (1)
Button(75-266)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
⏰ 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 (2)
src/components/LandingPage/noFees.tsx (1)
233-238: Good fix: make destination amount read-onlyPrevents users from overriding the calculated value. This aligns with the calculated flow tied to the exchange rate.
src/components/LandingPage/Footer.tsx (1)
31-64: Nice fix: external links hardened and Terms URL whitespace removed.Good job adding rel="noopener noreferrer" to all central external links and correcting the Terms URL.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (7)
src/components/LandingPage/Footer.tsx (3)
67-75: Nice fix: replaced window.open with a semantic, secure, and accessible anchor.Anchor + rel="noopener noreferrer" + aria-label is the right pattern. Alt="" on the icon is correct since the link has an accessible name.
18-20: Add missing rel="noopener noreferrer" on external link opening in new tab.Security and lint issue: target="_blank" without rel opens you up to reverse-tabnabbing. All other links are correct; this one is the exception.
Apply this diff:
- <a className="underline" href="https://squirrellabs.dev/" target="_blank"> + <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
76-78: Fix a11y and consistency: avoid .src on SVG imports; mark decorative icons properly.Use the imported SVG modules directly (not .src) and hide decorative imagery from SRs with empty alt and aria-hidden.
Apply this diff:
- <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} /> - <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} /> - <Image src={handWaving.src} alt="Hand waving" width={25} height={25} /> + <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={handMiddleFinger} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />Note: This aligns with your previously stated preference for decorative images using empty alt attributes to avoid layout/SR issues.
src/components/LandingPage/noFees.tsx (4)
297-299: Send Money button needs functionalityThe Send Money button doesn't have an onClick handler, making it non-functional. Based on the PR objectives mentioning incomplete API integration, this appears to be pending implementation.
Would you like me to help implement the Send Money button functionality? This could include:
- Navigation to a payment flow with pre-filled values
- Opening a modal for transaction details
- Integration with the payment processing API
Apply this diff as a starting point:
+ import { useRouter } from 'next/navigation' + // ... existing imports + + // In the component (already imported at line 13) + // const router = useRouter() + <Button icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold" + onClick={() => { + // Navigate to payment flow with pre-filled values + const params = new URLSearchParams({ + from: sourceCurrency, + to: destinationCurrency, + amount: sourceAmount.toString() + }) + router.push(`/send?${params.toString()}`) + }} > Send Money </Button>
25-25: Improve robustness of URL parameter parsingThe current parsing of the
amountparameter could result inNaNor negative values if invalid input is provided in the URL. This could lead to unexpected UI behavior.Apply this diff to add proper validation:
- const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10 + const amountParam = searchParams.get('amount') + const parsedAmount = amountParam !== null ? parseFloat(amountParam) : 10 + const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
116-127: Add error handling for exchange rate API callsThe exchange rate fetch lacks error handling, which could cause the component to display incorrect data or have undefined behavior if the request fails.
Apply this diff to add proper error handling:
useEffect(() => { const fetchExchangeRate = async () => { + try { const _sourceCurrency = sourceCurrency const _destinationCurrency = destinationCurrency const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`) + + if (!response.ok) { + console.error('Failed to fetch exchange rate:', response.status) + // Optionally set an error state or use a fallback rate + return + } + const data = await response.json() + + // Validate the response structure + if (typeof data.rate !== 'number') { + console.error('Invalid exchange rate response:', data) + return + } + setCurrentExchangeRate(data.rate) setDestinationAmount(debouncedSourceAmount * data.rate) + } catch (error) { + console.error('Error fetching exchange rate:', error) + // Optionally set an error state to show user feedback + } } fetchExchangeRate() }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])
220-225: Improve input validation and UX for amount entryThe amount input needs better validation to handle edge cases like empty strings, leading dots, and provide better formatting for currency values.
Apply this diff to improve the input handling:
<input min={0} value={sourceAmount} onChange={(e) => { - const value = parseFloat(e.target.value) - setSourceAmount(isNaN(value) || value < 0 ? 0 : value) + const value = e.target.value + // Allow empty string for better UX + if (value === '') { + setSourceAmount(0) + return + } + const numValue = parseFloat(value) + if (!isNaN(numValue) && numValue >= 0) { + // Limit to 2 decimal places for currency + setSourceAmount(Math.round(numValue * 100) / 100) + } }} type="number" + step="0.01" + placeholder="0.00" className="w-full bg-transparent outline-none" />
🧹 Nitpick comments (4)
src/components/LandingPage/Footer.tsx (3)
26-29: Clarify intent of social icons (decorative vs. links) and adjust for a11y accordingly.If these are decorative, hide them from SRs; if actionable, wrap with anchors and provide accessible names.
If decorative, apply:
- <Image src={DISCORD_ICON} alt="Discord" width={20} height={20} /> - <Image src={TWITTER_ICON} alt="Twitter" width={20} height={20} /> - <Image src={GITHUB_WHITE_ICON} alt="Github" width={20} height={20} /> + <Image src={DISCORD_ICON} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={TWITTER_ICON} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={GITHUB_WHITE_ICON} alt="" width={20} height={20} aria-hidden="true" />If they should link out, I can provide a follow-up diff wrapping each in with target="_blank" rel attrs and visually hidden text.
31-64: Optional: wrap the footer link list in a nav for better semantics.Improves landmark navigation and a11y without visual changes.
Apply this diff:
- <div className="flex gap-2"> + <nav aria-label="Footer links" className="flex gap-2"> ... - </div> + </nav>
14-15: Nit: brand logo alt text.Consider alt="Peanut" (brand name) or alt="" if the logo is purely decorative and adjacent text already names the brand.
src/components/LandingPage/noFees.tsx (1)
277-281: Consider formatting the exchange rate displayThe exchange rate display might show many decimal places. Consider formatting it to a fixed number of decimal places for better readability.
Apply this diff to improve the display:
{destinationAmount > 0 && ( <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1"> - 1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency} + 1 {sourceCurrency} = {currentExchangeRate.toFixed(4)} {destinationCurrency} </div> )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/app/api/exchange-rate/route.ts(1 hunks)src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/Footer.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/constants/countryCurrencyMapping.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/app/api/exchange-rate/route.tssrc/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.
Applied to files:
src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (1)
src/app/api/exchange-rate/route.ts (1)
src/app/actions/exchange-rate.ts (1)
ExchangeRateResponse(8-15)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
⏰ 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/LandingPage/noFees.tsx (1)
251-255: Good implementation of read-only destination amountThe destination amount input is correctly set as read-only, preventing users from manually editing the calculated value. This ensures the exchange rate calculation logic remains intact.
|
|
nit: clouds despawn ahead of time |
There was a problem hiding this comment.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
src/components/LandingPage/hero.tsx (1)
104-123: Remove unused scroll listener and state; it causes unnecessary re-rendersscrollY is never used. The scroll event handler will trigger re-renders needlessly on scroll.
- const [scrollY, setScrollY] = useState(0) + // Removed unused scroll state @@ - const handleScroll = () => { - setScrollY(window.scrollY) - } @@ - window.addEventListener('scroll', handleScroll) + // Removed scroll listener @@ - window.removeEventListener('scroll', handleScroll) + // Removed scroll listener cleanup
♻️ Duplicate comments (3)
src/components/LandingPage/Footer.tsx (1)
18-21: Add missing rel attributes to external link (reverse tabnabbing)Squirrel Labs link opens a new tab without rel. Add rel="noopener noreferrer".
- <a className="underline" href="https://squirrellabs.dev/" target="_blank"> + <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">src/components/LandingPage/noFees.tsx (2)
22-26: Harden parsing of amount query param to avoid NaN/negative valuesCurrent parsing can propagate NaN and allows negatives. Clamp and default safely.
- const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10 + const amountParam = searchParams.get('amount') + const parsedAmount = amountParam !== null ? Number(amountParam) : 10 + const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
116-127: Fix exchange rate API integration, add loading/error handling, and use numeric ratesEndpoint and shape differ from the Bridge route introduced in this PR. Add basic error handling and loading state; use numeric rate consistently.
+ const [isRateLoading, setIsRateLoading] = useState(false) @@ - useEffect(() => { - const fetchExchangeRate = async () => { - const _sourceCurrency = sourceCurrency - const _destinationCurrency = destinationCurrency - const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`) - const data = await response.json() - setCurrentExchangeRate(data.rate) - setDestinationAmount(debouncedSourceAmount * data.rate) - } - - fetchExchangeRate() - }, [sourceCurrency, destinationCurrency, debouncedSourceAmount]) + useEffect(() => { + const fetchExchangeRate = async () => { + setIsRateLoading(true) + try { + const from = sourceCurrency.toLowerCase() + const to = destinationCurrency.toLowerCase() + // Align with Bridge route added in this PR + const response = await fetch(`/api/bridge/exchange-rate?from=${from}&to=${to}`) + if (!response.ok) { + console.error('Failed to fetch exchange rate', response.status) + return + } + const data = await response.json() + // Bridge route returns numeric rates (parseFloat applied server-side) + const rate = data?.rates?.buy ?? data?.rate + if (typeof rate === 'number' && Number.isFinite(rate)) { + setCurrentExchangeRate(rate) + setDestinationAmount(debouncedSourceAmount * rate) + } else { + console.error('Unexpected exchange rate payload', data) + } + } catch (err) { + console.error('Error fetching exchange rate', err) + } finally { + setIsRateLoading(false) + } + } + fetchExchangeRate() + }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])If you prefer keeping
/api/exchange-rate, align its response to match the Bridge route shape or adjust here accordingly. I can take either path—say the word.
🧹 Nitpick comments (5)
src/components/LandingPage/Footer.tsx (1)
76-78: Fix a11y and consistency for decorative hand icons
- Use the imported SVG module directly (not .src).
- Decorative images should have empty alt and aria-hidden.
- <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} /> - <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} /> - <Image src={handWaving.src} alt="Hand waving" width={25} height={25} /> + <Image src={handThumbsUp} alt="" aria-hidden="true" width={20} height={20} /> + <Image src={handMiddleFinger} alt="" aria-hidden="true" width={20} height={20} /> + <Image src={handWaving} alt="" aria-hidden="true" width={25} height={25} />src/components/LandingPage/CurrencySelect.tsx (1)
139-148: Good: external flag image error-handling presentHiding the broken image on error avoids broken UI. Consider adding a local fallback later, but this is acceptable for now.
src/components/LandingPage/noFees.tsx (3)
279-283: Include a loading hint for rate fetchTo address “interaction feels slow,” show a lightweight loading state while fetching.
- {destinationAmount > 0 && ( + {destinationAmount > 0 && ( <div className="rounded-full bg-grey-4 px-2 py-[2px] text-xs font-bold text-gray-1"> - 1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency} + {isRateLoading + ? 'Updating rate...' + : <>1 {sourceCurrency} = {currentExchangeRate} {destinationCurrency}</>} </div> )}
219-228: Input UX: allow empty value and constrain decimalsCurrent handler forces 0 on empty input and doesn’t constrain decimals. Improve UX and numeric safety.
- <input + <input min={0} value={sourceAmount} onChange={(e) => { - const value = parseFloat(e.target.value) - setSourceAmount(isNaN(value) || value < 0 ? 0 : value) + const raw = e.target.value + if (raw === '') { + setSourceAmount(0) + return + } + const num = Number(raw) + if (Number.isFinite(num) && num >= 0) { + // limit to 2 decimals for currency display + const rounded = Math.round(num * 100) / 100 + setSourceAmount(rounded) + } }} type="number" - className="w-full bg-transparent outline-none" + step="0.01" + placeholder="0.00" + className="w-full bg-transparent outline-none" />
299-307: Pass context when navigating from “Send Money”Consider preserving the chosen values via query params to reduce friction on the next screen.
- <Button - onClick={() => router.push('/setup')} + <Button + onClick={() => + router.push(`/setup?from=${sourceCurrency}&to=${destinationCurrency}&amount=${sourceAmount}`) + } icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold" > Send Money
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/Footer.tsx(1 hunks)src/components/LandingPage/hero.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/constants/countryCurrencyMapping.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (12)
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/CurrencySelect.tsxsrc/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.
Applied to files:
src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (3)
src/components/LandingPage/hero.tsx (1)
src/components/LandingPage/imageAssets.tsx (2)
CloudImages(51-116)HeroImages(118-137)
src/components/LandingPage/CurrencySelect.tsx (2)
src/constants/countryCurrencyMapping.ts (1)
countryCurrencyMappings(8-30)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)
src/components/LandingPage/noFees.tsx (4)
src/hooks/useDebounce.ts (1)
useDebounce(9-23)src/constants/countryCurrencyMapping.ts (1)
countryCurrencyMappings(8-30)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)src/components/0_Bruddle/Button.tsx (1)
Button(75-266)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
⏰ 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/constants/countryCurrencyMapping.ts (1)
8-30: LGTM: concise dataset and interface fit the use-caseThe mapping is well-scoped for the current selector and exports both named and default forms, which matches usage across components.
There was a problem hiding this comment.
Actionable comments posted: 0
🔭 Outside diff range comments (5)
src/components/0_Bruddle/Button.tsx (5)
98-101: Fix forwardRef handling; current approach breaks with callback refsCasting
reftoReact.RefObjectand using it directly will break if a consumer passes a function ref (no.current), leading to runtime errors at Lines 108–111 and when assigningrefat Line 239. Merge refs via a stable callback and use a local ref internally.Apply this diff:
- const localRef = useRef<HTMLButtonElement>(null) - const buttonRef = (ref as React.RefObject<HTMLButtonElement>) || localRef + const localRef = useRef<HTMLButtonElement>(null) + const setRefs = useCallback( + (node: HTMLButtonElement | null) => { + localRef.current = node + if (typeof ref === 'function') { + ref(node) + } else if (ref) { + ;(ref as React.MutableRefObject<HTMLButtonElement | null>).current = node + } + }, + [ref] + ) @@ - if (!buttonRef.current) return - buttonRef.current.setAttribute('translate', 'no') - buttonRef.current.classList.add('notranslate') + if (!localRef.current) return + localRef.current.setAttribute('translate', 'no') + localRef.current.classList.add('notranslate') }, []) @@ - ref={buttonRef} + ref={setRefs}Also applies to: 107-111, 239-239
186-195: Prevent interactions while loading and add aria-busy/disabled for UX/a11yClicks should be ignored while loading to prevent double submits; reflect state in DOM for accessibility.
Apply this diff:
const handleClick = useCallback( (e: React.MouseEvent<HTMLButtonElement>) => { + if (loading) { + return + } if (longPress && !isLongPressed) { // If long press is enabled but not completed, don't trigger onClick return } onClick?.(e) }, - [longPress, isLongPressed, onClick] + [loading, longPress, isLongPressed, onClick] )And on the element:
<button className={twMerge(buttonClasses, 'notranslate', longPress && 'relative overflow-hidden')} - ref={buttonRef} + ref={setRefs} translate="no" + aria-busy={loading || undefined} + disabled={loading || props.disabled} onClick={handleClick}Note: The ref prop reflects the earlier ref-fix comment.
Also applies to: 237-249
237-249: Default button type should be "button" to avoid accidental form submitsHTML default is type="submit". UI buttons often unintentionally submit forms.
Apply this diff:
<button + type={props.type ?? 'button'} className={twMerge(buttonClasses, 'notranslate', longPress && 'relative overflow-hidden')}
103-106: Use ReturnType<typeof setTimeout/setInterval> for timers to avoid DOM/Node type mismatchesTyping timers as NodeJS.Timeout can cause TypeScript errors in browser builds; use ReturnType / ReturnType to be environment-agnostic.
- File: src/components/0_Bruddle/Button.tsx (lines ~103–106)
Apply this diff:
- const [pressTimer, setPressTimer] = useState<NodeJS.Timeout | null>(null) + const [pressTimer, setPressTimer] = useState<ReturnType<typeof setTimeout> | null>(null) @@ - const [progressInterval, setProgressInterval] = useState<NodeJS.Timeout | null>(null) + const [progressInterval, setProgressInterval] = useState<ReturnType<typeof setInterval> | null>(null)
209-218: Fix: Tailwind JIT will purge the dynamicactive:translate-y-[${shadowSize}px]— use a static map or safelistTailwind won't see the interpolated arbitrary class in src/components/0_Bruddle/Button.tsx and I didn't find a safelist/matchUtilities for these translate-y values in tailwind.config.js. Replace the dynamic interpolation with a small static map (or add an explicit safelist in tailwind.config.js).
Files to update
- src/components/0_Bruddle/Button.tsx — replace the dynamic arbitrary class
- tailwind.config.js — (optional) add a safelist if you prefer that approach
Suggested change (apply one of these):
- Static map (recommended)
+const activeTranslateByShadow: Record<ShadowSize, string> = { + '4': 'active:translate-y-[4px]', + '6': 'active:translate-y-[6px]', + '8': 'active:translate-y-[8px]', +}Then update the classes:
- const buttonClasses = twMerge( - `btn w-full flex items-center gap-2 transition-all duration-100 active:translate-x-[3px] active:translate-y-[${shadowSize}px] active:shadow-none notranslate`, + const buttonClasses = twMerge( + `btn w-full flex items-center gap-2 transition-all duration-100 active:translate-x-[3px] active:shadow-none notranslate`, buttonVariants[variant], @@ - shadowSize && buttonShadows[shadowType || 'primary'][shadowSize], + shadowSize && buttonShadows[shadowType || 'primary'][shadowSize], + shadowSize && activeTranslateByShadow[shadowSize],
- Alternative: safelist the three classes in tailwind.config.js (if you prefer):
- safelist: ['active:translate-y-[4px]', 'active:translate-y-[6px]', 'active:translate-y-[8px]'] (or equivalent regex)
♻️ Duplicate comments (4)
src/components/LandingPage/Footer.tsx (2)
18-20: Add missing security attribute to external link.The Squirrel Labs link is missing
rel="noopener noreferrer"which creates a security vulnerability.- <a className="underline" href="https://squirrellabs.dev/" target="_blank"> + <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
97-99: Fix inconsistent image source usage for hand icons.The last three hand icons use
.srcproperty instead of passing the imported SVG directly like the handPeace icon does. Also, if these are decorative, they should have empty alt text with aria-hidden.- <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} /> - <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} /> - <Image src={handWaving.src} alt="Hand waving" width={25} height={25} /> + <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={handMiddleFinger} alt="" width={20} height={20} aria-hidden="true" /> + <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />src/components/LandingPage/noFees.tsx (2)
125-135: Add error handling for exchange rate API calls.The API call lacks error handling, which could cause the component to display incorrect data if the request fails. Based on the learning from your conversation with Manu about error states, please ensure this follows the team's consistent error handling pattern.
useEffect(() => { const fetchExchangeRate = async () => { + try { const _sourceCurrency = sourceCurrency const _destinationCurrency = destinationCurrency const response = await fetch(`/api/exchange-rate?from=${_sourceCurrency}&to=${_destinationCurrency}`) + if (!response.ok) { + console.error('Failed to fetch exchange rate') + return + } const data = await response.json() setCurrentExchangeRate(data.rate) setDestinationAmount(debouncedSourceAmount * data.rate) + } catch (error) { + console.error('Error fetching exchange rate:', error) + // Follow team's error handling pattern as discussed with Manu + } } fetchExchangeRate() }, [sourceCurrency, destinationCurrency, debouncedSourceAmount])
23-25: Strengthen parsing of the amount query parameter.The current parsing could allow NaN or negative values to flow into the state. Consider validating and clamping the parsed value.
- const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10 + const amountParam = searchParams.get('amount') + const parsedAmount = amountParam ? parseFloat(amountParam) : 10 + const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
🧹 Nitpick comments (2)
src/components/0_Bruddle/Button.tsx (1)
242-247: Unify input handling with Pointer events and add keyboard support for long-pressHandling both mouse and touch separately is error-prone and lacks keyboard accessibility. Prefer Pointer events and support Space/Enter.
Apply this diff for the element handlers:
- onMouseDown={longPress ? handlePressStart : undefined} - onMouseUp={longPress ? handlePressEnd : undefined} - onMouseLeave={longPress ? handlePressCancel : undefined} - onTouchStart={longPress ? handlePressStart : undefined} - onTouchEnd={longPress ? handlePressEnd : undefined} - onTouchCancel={longPress ? handlePressCancel : undefined} + onPointerDown={longPress ? handlePressStart : undefined} + onPointerUp={longPress ? handlePressEnd : undefined} + onPointerCancel={longPress ? handlePressCancel : undefined} + onKeyDown={ + longPress + ? (e) => { + if (e.key === ' ' || e.key === 'Enter') handlePressStart() + } + : undefined + } + onKeyUp={ + longPress + ? (e) => { + if (e.key === ' ' || e.key === 'Enter') handlePressEnd() + } + : undefined + }I can provide a small helper to normalize keys and avoid inline lambdas if you prefer.
src/components/LandingPage/noFees.tsx (1)
228-236: Improve amount input validation and UX.The current input handling could be improved to handle edge cases better and provide a smoother user experience. Consider allowing empty input states and limiting decimal places for currency amounts.
<input min={0} value={sourceAmount} onChange={(e) => { - const value = parseFloat(e.target.value) - setSourceAmount(isNaN(value) || value < 0 ? 0 : value) + const value = e.target.value + // Allow empty string for better UX + if (value === '') { + setSourceAmount(0) + return + } + const numValue = parseFloat(value) + if (!isNaN(numValue) && numValue >= 0) { + // Limit to 2 decimal places for currency + setSourceAmount(Math.round(numValue * 100) / 100) + } }} type="number" + step="0.01" + placeholder="0.00" className="w-full bg-transparent outline-none" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (5)
src/app/exchange/page.tsx(1 hunks)src/components/0_Bruddle/Button.tsx(1 hunks)src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/Footer.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (14)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-23T09:38:27.670Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#469
File: src/app/request/pay/page.tsx:32-64
Timestamp: 2024-10-23T09:38:27.670Z
Learning: In `src/app/request/pay/page.tsx`, if `linkRes` is not OK in the `generateMetadata` function, the desired behavior is to use the standard title and preview image without throwing an error.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.
Applied to files:
src/components/LandingPage/Footer.tsx
🧬 Code Graph Analysis (2)
src/app/exchange/page.tsx (1)
src/components/LandingPage/noFees.tsx (1)
NoFees(16-320)
src/components/LandingPage/noFees.tsx (4)
src/hooks/useDebounce.ts (1)
useDebounce(9-23)src/constants/countryCurrencyMapping.ts (1)
countryCurrencyMappings(8-30)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)src/components/0_Bruddle/Button.tsx (1)
Button(76-267)
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
⏰ 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 (8)
src/components/0_Bruddle/Button.tsx (1)
1-1: Client-ifying Button is correct given hooks and interactivityThis component uses React hooks, timers, and DOM APIs; marking it as a client component is appropriate.
src/app/exchange/page.tsx (1)
1-13: LGTM! Clean page structure with proper imports.The page component is well-structured with appropriate use of the
'use client'directive and proper component composition.src/components/LandingPage/Footer.tsx (2)
27-50: Social media links look good with proper security attributes.All social links now include proper
rel="noopener noreferrer"attributes and semantic aria-labels for accessibility. Good job on addressing the security concerns!
53-60: Update docs link to peanut.me domain.The docs link still points to peanut.to instead of peanut.me, which is a blocking issue mentioned in the PR objectives.
- href="https://docs.peanut.to/" + href="https://docs.peanut.me/"Likely an incorrect or invalid review comment.
src/components/LandingPage/noFees.tsx (4)
260-265: Good implementation of read-only destination amount.The destination amount input is correctly set as read-only, preventing users from directly modifying the calculated value. The fixed decimal formatting ensures consistent display.
307-316: Send Money button correctly navigates to setup page.The button implementation with onClick handler and navigation to '/setup' looks good and addresses the functionality requirement.
69-75: Good implementation of optimistic UI updates.The setSourceAmount callback now includes an optimistic update using the current exchange rate, which addresses the UI sluggishness issue mentioned in the PR objectives. This provides immediate feedback to users while the API call completes.
138-138: Consider Portal implementation in CurrencySelect for mobile popover issues.Based on the PR objectives mentioning that the currency widget gets hidden on mobile, and your previous confirmation that this was fixed using Portal rendering in CurrencySelect, the overflow-hidden here should work correctly with that solution.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/components/LandingPage/noFees.tsx (3)
117-117: Popover clipping: resolved via Portal — keeping overflow-hidden here is fineAcknowledging the chosen fix. Rendering CurrencySelect in a Portal avoids clipping; no need to change container overflow.
23-27: Harden parsing of the amount query param (avoid NaN/negatives)Using parseFloat with a fallback can still leak NaN/negative values. Clamp and validate for a better UX.
Apply this diff:
- const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10 + const amountParam = searchParams.get('amount') + const parsedAmount = amountParam !== null ? Number(amountParam) : 10 + const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
300-307: Make “Send Money” navigate with prefilled query params and reflect loadingPreserve the user’s selection and amount when navigating. Also reflect loading state on the button.
Apply this diff:
- <Button - onClick={() => router.push('/setup')} + <Button + loading={isLoading} + onClick={() => { + const amount = typeof sourceAmount === 'number' ? sourceAmount : 0 + router.push(`/setup?from=${sourceCurrency}&to=${destinationCurrency}&amount=${amount}`) + }} icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold" > Send Money </Button>
🧹 Nitpick comments (4)
src/hooks/useExchangeRate.ts (2)
48-51: Round stored numeric destination amount to match displayed precisionYou round the display string to 2 decimals, but keep the numeric state unrounded. Aligning both avoids subtle drift and ensures consistent downstream usage.
Apply this diff:
- const updateDestinationFromCalculation = (calculatedAmount: number) => { - setDestinationAmount(calculatedAmount) - setDestinationInputValue(calculatedAmount.toFixed(2)) - } + const updateDestinationFromCalculation = (calculatedAmount: number) => { + const rounded = parseFloat(calculatedAmount.toFixed(2)) + setDestinationAmount(rounded) + setDestinationInputValue(rounded.toFixed(2)) + }
65-75: Small cleanup: simplify destination display logicThe final else on Line 74 can only be a number or empty string; the String() fallback is redundant. Tighten the logic.
Apply this diff:
- const getDestinationDisplayValue = useCallback(() => { + const getDestinationDisplayValue = useCallback(() => { if (lastEditedField === 'destination') { return destinationInputValue } if (destinationAmount === '') { return '' } - return typeof destinationAmount === 'number' ? destinationAmount.toFixed(2) : String(destinationAmount) + return (destinationAmount as number).toFixed(2) }, [lastEditedField, destinationInputValue, destinationAmount])src/components/LandingPage/noFees.tsx (2)
45-57: Avoid relying on captured searchParams; preserve path when updating URLURL mutation using the captured searchParams snapshot can be stale if concurrent updates occur; also, replacing only the query omits the path in some contexts. Using usePathname and reconstructing is more robust.
Apply this diff and update imports accordingly:
- const updateUrlParams = useCallback( - (params: { from?: string; to?: string; amount?: number }) => { - const newSearchParams = new URLSearchParams(searchParams.toString()) + const updateUrlParams = useCallback( + (params: { from?: string; to?: string; amount?: number }) => { + const newSearchParams = new URLSearchParams(searchParams.toString()) if (params.from) newSearchParams.set('from', params.from) if (params.to) newSearchParams.set('to', params.to) if (params.amount !== undefined) newSearchParams.set('amount', params.amount.toString()) - router.replace(`?${newSearchParams.toString()}`, { scroll: false }) + router.replace(`${window.location.pathname}?${newSearchParams.toString()}`, { scroll: false }) }, [searchParams, router] )Also add the import if you prefer SSR-safe approach:
// Additionally, you can use usePathname for SSR-friendly path retrieval import { useSearchParams, useRouter, usePathname } from 'next/navigation' const pathname = usePathname() // Then replace window.location.pathname with pathname
206-221: Improve numeric input UX (decimal keypad, step, and empty handling)Small tweaks improve input behavior across browsers and mobile.
Apply this diff:
- <input + <input min={0} placeholder="0" value={sourceAmount === '' ? '' : sourceAmount} onChange={(e) => { const inputValue = e.target.value if (inputValue === '') { handleSourceAmountChange('') } else { const value = parseFloat(inputValue) handleSourceAmountChange(isNaN(value) ? '' : value) } }} - type="number" + type="number" + inputMode="decimal" + step="any" className="w-full bg-transparent outline-none" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/hooks/useExchangeRate.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/LandingPage/CurrencySelect.tsx
🧰 Additional context used
🧠 Learnings (12)
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.
Applied to files:
src/components/LandingPage/noFees.tsx
🧬 Code Graph Analysis (2)
src/hooks/useExchangeRate.ts (1)
src/hooks/useDebounce.ts (1)
useDebounce(9-23)
src/components/LandingPage/noFees.tsx (5)
src/hooks/useExchangeRate.ts (1)
useExchangeRate(23-139)src/hooks/useDebounce.ts (1)
useDebounce(9-23)src/constants/countryCurrencyMapping.ts (1)
countryCurrencyMappings(8-30)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)src/components/0_Bruddle/Button.tsx (1)
Button(76-267)
🔇 Additional comments (4)
src/hooks/useExchangeRate.ts (1)
41-41: Should 0 be considered a valid amount?Currently amounts must be > 0. If a user enters 0, you clear computed fields, which can feel jumpy. If 0 should yield 0 on the other side, loosen the guard to allow 0.
If desired, change the predicate:
- const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount > 0 + const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount >= 0Confirm product expectations for zero inputs before applying.
src/components/LandingPage/noFees.tsx (3)
223-238: Guard against missing flag codes; provide fallback UIIf the mapping lacks a flagCode (or it's 'eu' and CDN fails), the image 404s. Render a fallback icon to avoid broken images.
Apply this diff:
- <CurrencySelect + <CurrencySelect selectedCurrency={sourceCurrency} setSelectedCurrency={setSourceCurrency} excludeCurrencies={[destinationCurrency]} trigger={ - <button className="flex w-32 items-center gap-2"> - <Image - src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`} - alt={`${sourceCurrencyFlag} flag`} - width={160} - height={160} - className="size-4 rounded-full object-cover" - /> - {sourceCurrency} <Icon name="chevron-down" className="text-gray-1" size={10} /> - </button> + <button className="flex w-32 items-center gap-2"> + {sourceCurrencyFlag ? ( + <Image + src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`} + alt={`${sourceCurrencyFlag} flag`} + width={160} + height={160} + className="size-4 rounded-full object-cover" + /> + ) : ( + <Icon name="globe" size={16} className="text-gray-1" /> + )} + {sourceCurrency} <Icon name="chevron-down" className="text-gray-1" size={10} /> + </button> } />Repeat similarly for Lines 266-277 (destination).
246-260: Confirm product decision: allow manual editing of destination amountEarlier comments suggested making destination read-only to avoid breaking calculations. This UI permits two-way editing and the hook supports it. Align with product: do we want two-way conversion, or source-only input?
If one-way is desired, remove onChange and add readOnly. If two-way is intended, keep as is; the hook logic looks sound.
15-15: Import is valid — no change requiredVerified: src/constants/countryCurrencyMapping.ts declares a named export (export const countryCurrencyMappings at line 8) and also re-exports it as the default (export default countryCurrencyMappings at line 32). The default import in src/components/LandingPage/noFees.tsx (line 15) is therefore correct and will not break the build.
Files/locations:
- src/constants/countryCurrencyMapping.ts — named export at line 8; default export at line 32
- src/components/LandingPage/noFees.tsx — default import at line 15
Optional: consider removing the duplicate export (pick either named or default) for clarity, but no fix required for this PR.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 7
🔭 Outside diff range comments (1)
src/components/LandingPage/noFees.tsx (1)
1-347: Blocking — mixed docs domains (docs.peanut.to vs docs.peanut.me); please standardize before mergeRepo search shows mixed references to the docs domain. Please pick the canonical docs host and update the occurrences below:
- redirects.json — "destination": "https://docs.peanut.to"
- src/components/Global/Footer/consts.ts — url: 'https://docs.peanut.to' (entries)
- src/components/Global/WalletNavigation/index.tsx — href: 'https://docs.peanut.to/'
- src/components/Payment/Views/Error.validation.view.tsx — href: 'https://docs.peanut.to/how-to-use-peanut-links/request-peanut-links'
- src/components/Setup/Views/SetupPasskey.tsx — href: 'https://docs.peanut.to/passkeys'
- src/components/Profile/index.tsx — href: 'https://docs.peanut.to/how-to-use-peanut-links/referrals' and 'https://docs.peanut.to/fees'
- src/constants/tooltips.ts — links to 'https://docs.peanut.to/...'
- .env.example — comment: "See in docs.peanut.to"
- src/components/LandingPage/Footer.tsx — href: "https://docs.peanut.me/" (inconsistent with above)
Also note there are other domain variants in the repo (peanut.me, peanut.to, legacy.peanut.to, api.peanut.me, did:web:peanut.to). After you confirm the canonical domain for docs, update the listed files (and redirects/configs if needed) so all docs links are consistent.
♻️ Duplicate comments (8)
src/app/api/exchange-rate/route.ts (5)
22-29: Add currency-code validation for robustness and to prevent unnecessary external callsValidate ISO 4217 codes early. This avoids calling upstream APIs with invalid params and aligns with prior guidance.
Apply this diff:
// Validate required parameters if (!from || !to) { return NextResponse.json({ error: 'Missing required parameters: from and to' }, { status: 400 }) } - const fromUc = from.toUpperCase() - const toUc = to.toUpperCase() + const fromUc = from.toUpperCase() + const toUc = to.toUpperCase() + + // Basic ISO-4217 validation (3 uppercase letters) + const ISO4217 = /^[A-Z]{3}$/ + if (!ISO4217.test(fromUc) || !ISO4217.test(toUc)) { + return NextResponse.json({ error: 'Invalid currency code format' }, { status: 400 }) + }
135-143: Add timeout to Frankfurter fetch and keep existing SWR cachingSame rationale as Bridge: prevent hanging requests.
Apply this diff:
const options: RequestInit & { next?: { revalidate?: number } } = { method: 'GET', next: { revalidate: 300 }, // Cache for 5 minutes + signal: AbortSignal.timeout(5000), }
69-72: Differentiate timeouts from generic server errors (return 504 on timeout)Surface a proper 504 for timeouts. Helps client-side UX and diagnostics.
Apply this diff:
} catch (error) { - console.error('Exchange rate API error:', error) - return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) + if ((error as any)?.name === 'AbortError') { + console.error('Exchange rate API timeout') + return NextResponse.json({ error: 'Request timeout' }, { status: 504 }) + } + console.error('Exchange rate API error:', error) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) }
149-153: Validate Frankfurter response shape and numeric rate before using itAvoid runtime errors when API schema changes or unsupported pairs are requested.
Apply this diff:
- const data = await response.json() - - const exchangeRate: ExchangeRateResponse = { - rate: data.rates[to] * 0.995, // Subtract 50bps - } + const data = await response.json() + const raw = data?.rates?.[to] + if (typeof raw !== 'number' || !Number.isFinite(raw) || raw <= 0) { + console.error('Invalid Frankfurter response structure or rate value', data) + return NextResponse.json({ error: 'Invalid exchange rate data' }, { status: 502 }) + } + const exchangeRate: ExchangeRateResponse = { + rate: raw * 0.995, // Subtract 50bps + }
75-96: Add request timeout to Bridge fetch to avoid hanging requestsWrap Bridge requests with a timeout to fail fast and improve perceived reliability. Prefer 5s.
Apply this diff:
try { const url = `https://api.bridge.xyz/v0/exchange_rates?from=${from.toLowerCase()}&to=${to.toLowerCase()}` const options: RequestInit & { next?: { revalidate?: number } } = { method: 'GET', // Bridge expects header name 'Api-Key' headers: { 'Api-Key': bridgeAPIKey }, next: { revalidate: 300 }, // Cache for 5 minutes + // Fail fast if upstream is slow + signal: AbortSignal.timeout(5000), }src/components/LandingPage/noFees.tsx (2)
23-27: Harden parsing of amount query param to avoid NaN/negative values seeping into stateCurrent logic falls back to 10 on NaN but allows negative values. Clamp and ensure finite.
Apply this diff:
- const urlSourceAmount = parseFloat(searchParams.get('amount') || '10') || 10 + const amountParam = searchParams.get('amount') + const parsedAmount = amountParam !== null ? Number(amountParam) : 10 + const urlSourceAmount = Number.isFinite(parsedAmount) && parsedAmount >= 0 ? parsedAmount : 10
327-335: Preserve user selections when navigating: pass query params to /setupCarry over currencies and amount to improve flow continuity and address UX feedback.
Apply this diff:
- <Button - onClick={() => router.push('/setup')} + <Button + onClick={() => + router.push( + `/setup?from=${encodeURIComponent(sourceCurrency)}&to=${encodeURIComponent( + destinationCurrency + )}&amount=${encodeURIComponent(String(sourceAmount ?? ''))}` + ) + } icon="arrow-up-right" iconSize={13} shadowSize="4" className="w-full text-base font-bold" >src/hooks/useExchangeRate.ts (1)
83-93: Harden the fetch: URI-encode params, validate payload, and avoid unnecessary requests
- Encode user-selected currencies to prevent malformed URLs.
- Validate payload and return a typed object to avoid downstream NaNs.
- Skip the network call when currencies are identical via enabled.
- Add a reasonable retry policy.
Apply this diff:
- } = useQuery<{ rate: number }>({ - queryKey: ['exchangeRate', sourceCurrency, destinationCurrency], - queryFn: async () => { - const res = await fetch(`/api/exchange-rate?from=${sourceCurrency}&to=${destinationCurrency}`) - if (!res.ok) throw new Error('Failed to fetch exchange rate') - return res.json() - }, - staleTime: 2 * 60 * 1000, // 2 minutes - gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes - refetchOnWindowFocus: false, - }) + } = useQuery<{ rate: number }>({ + queryKey: ['exchangeRate', sourceCurrency, destinationCurrency], + queryFn: async () => { + const from = encodeURIComponent(sourceCurrency) + const to = encodeURIComponent(destinationCurrency) + const res = await fetch(`/api/exchange-rate?from=${from}&to=${to}`) + if (!res.ok) throw new Error(`Failed to fetch exchange rate (${res.status})`) + const data = await res.json() + const rate = Number(data?.rate) + if (!Number.isFinite(rate) || rate <= 0) { + throw new Error('Invalid exchange rate payload') + } + return { rate } + }, + enabled: Boolean(sourceCurrency && destinationCurrency && sourceCurrency !== destinationCurrency), + staleTime: 2 * 60 * 1000, // 2 minutes + gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes + refetchOnWindowFocus: false, + retry: 2, + })
🧹 Nitpick comments (15)
src/app/api/exchange-rate/route.ts (1)
13-16: Optional: add basic rate limiting to protect the route in productionA simple in-memory limiter helps prevent abuse. Consider moving to a shared store (KV/Upstash) if deployed across instances.
Apply this diff:
// Currency pairs that should use Bridge API (USD to these currencies only) const BRIDGE_PAIRS = new Set(['USD-EUR', 'USD-MXN', 'USD-BRL']) +// Simple in-memory rate limiting (global). For multi-instance, use a shared store. +const RATE_LIMIT_WINDOW_MS = 60_000 +const RATE_LIMIT_MAX = 60 +const rateHits: Map<string, number[]> = new Map() + export async function GET(request: NextRequest) { try { + // naive IP detection; refine as needed + const ip = + request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || + request.headers.get('x-real-ip') || + 'global' + const now = Date.now() + const hits = (rateHits.get(ip) || []).filter((t) => now - t < RATE_LIMIT_WINDOW_MS) + if (hits.length >= RATE_LIMIT_MAX) { + return NextResponse.json({ error: 'Too Many Requests' }, { status: 429 }) + } + hits.push(now) + rateHits.set(ip, hits)scripts/compare-rates.mjs (3)
67-75: Add timeouts to Bridge fetch to avoid hanging script runsFail fast when Bridge is slow or unresponsive.
Apply this diff:
- const res = await fetch(url, { + const res = await fetch(url, { method: 'GET', headers: { 'Api-Key': BRIDGE_API_KEY }, - }) + signal: AbortSignal.timeout(5000), + })
80-89: Add timeout to Frankfurter fetchConsistency with Bridge; improves UX on network hiccups.
Apply this diff:
- const res = await fetch(url, { method: 'GET' }) + const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(5000) })
91-104: Use timeout and encode query params when querying local APISafer and more resilient on slow/invalid endpoints.
Apply this diff:
- const url = `${apiArg}?from=${from}&to=${to}` - const res = await fetch(url, { method: 'GET' }) + const url = `${apiArg}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}` + const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(5000) })src/constants/countryCurrencyMapping.ts (1)
32-35: LGTM. Dataset is up-to-date (no HRK), and comingSoon flags are explicitThe mapping avoids HRK and uses EUR for Croatia implicitly via Eurozone. CHF appears once (Switzerland) which is fine; if we need to surface Liechtenstein later, we can document the duplicate usage of CHF in the consuming UI.
Optional: add a comment noting CHF is also used by Liechtenstein for clarity in downstream UIs.
src/components/LandingPage/CurrencySelect.tsx (2)
115-130: Apply the same stable key in the All currencies sectionConsistency with the Popular section.
Apply this diff:
- <CurrencyBox - key={`${currency.countryCode}-${currency.country}-${index}`} + <CurrencyBox + key={currency.currency} countryCode={currency.countryCode} country={currency.country} currency={currency.currency} currencyName={currency.currencyName} comingSoon={currency.comingSoon} selected={currency.currency === selectedCurrency}
83-112: Consider rendering a single unified list when searching to reduce jitterWhen searchTerm is set, you render both “popular” and “all” groups (without headers), which may appear as a split list. Consider showing a single filtered list when searching.
src/components/LandingPage/noFees.tsx (4)
242-251: Add error handling/fallback for external flag images (source currency)If the mapping doesn’t resolve or flagcdn fails, the UI shows a broken image. Hide the image on error or render a placeholder.
Apply this diff:
<Image - src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`} - alt={`${sourceCurrencyFlag} flag`} + src={`https://flagcdn.com/w320/${sourceCurrencyFlag}.png`} + alt={`${sourceCurrencyFlag ?? 'flag'} flag`} width={160} height={160} className="size-4 rounded-full object-cover" + onError={(e) => { + e.currentTarget.style.display = 'none' + }} />
286-295: Add error handling/fallback for external flag images (destination currency)Same rationale as for source currency image.
Apply this diff:
<Image - src={`https://flagcdn.com/w320/${destinationCurrencyFlag}.png`} - alt={`${destinationCurrencyFlag} flag`} + src={`https://flagcdn.com/w320/${destinationCurrencyFlag}.png`} + alt={`${destinationCurrencyFlag ?? 'flag'} flag`} width={160} height={160} className="size-4 rounded-full object-cover" + onError={(e) => { + e.currentTarget.style.display = 'none' + }} />
210-236: Visible loading state added: good step; consider optimistic updates for snappier UXSkeletons help, but the selector still “waits for the rate” before updating. If your hook doesn’t already do it, optimistically compute destination amount using the last known rate on input change to reduce perceived latency.
If you want, I can propose a small refactor to
useExchangeRatethat maintains a ref of the last confirmed rate and uses it for immediate conversions while fetch is in-flight.Also applies to: 256-281, 301-311
124-136: Potential clipping of overlays due to overflow-hidden (clouds/popovers)You fixed currency popover clipping by using Portal (good). Note that overflow-hidden here can still cause background animation elements (clouds) to “despawn” early on some viewports. Consider overflow-visible on the outer section or increase cloud widths/speeds to minimize early exits.
src/hooks/useExchangeRate.ts (4)
41-41: Treat non-finite numbers as invalid amountsPrevents NaN or Infinity from slipping through as “valid.” This is defensive and avoids edge-case glitches.
Apply this diff:
- const isValidAmount = (amount: InputValue): amount is number => typeof amount === 'number' && amount > 0 + const isValidAmount = (amount: InputValue): amount is number => + typeof amount === 'number' && Number.isFinite(amount) && amount > 0
94-96: Improve UX: handle same-currency pairs and refine loading semantics
- Use a deterministic rate of 1 when source === destination.
- Don’t show a loading state during background refetches after the first rate is loaded.
Apply this diff:
- const exchangeRate = rateData?.rate ?? 0 - const isLoading = isFetching + const exchangeRate = sourceCurrency === destinationCurrency ? 1 : (rateData?.rate ?? 0) + const isLoading = !rateData && isFetching
131-134: Prop-driven initial amount changes may not recompute if destination was last editedIf initialSourceAmount changes while lastEditedField === 'destination', the destination won’t be recomputed from the new source. Resetting lastEditedField ensures the “initial load” path runs.
Apply this diff:
useEffect(() => { setSourceAmount(initialSourceAmount) + setLastEditedField(null) }, [initialSourceAmount])Can you confirm whether initialSourceAmount changes dynamically (e.g., when switching presets or currencies)? If so, this change will make the behavior consistent.
83-93: Optional: keep previous rate while fetching a new pair to avoid layout jankKeeping the previous data until the new rate arrives prevents reflows/repositioning in the currency widget. Pair with an isFetching-based subtle loader to signal updating.
If you are on react-query v5, add placeholderData to keep previous data:
} = useQuery<{ rate: number }>({ queryKey: ['exchangeRate', sourceCurrency, destinationCurrency], queryFn: async () => { // ... }, + placeholderData: (prev) => prev, enabled: Boolean(sourceCurrency && destinationCurrency && sourceCurrency !== destinationCurrency), staleTime: 2 * 60 * 1000, // 2 minutes gcTime: 10 * 60 * 1000, // garbage collect after 10 minutes refetchOnWindowFocus: false, retry: 2, })If on v4, an equivalent is keepPreviousData: true.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (7)
scripts/compare-rates.mjs(1 hunks)src/app/api/exchange-rate/route.ts(1 hunks)src/components/LandingPage/CurrencySelect.tsx(1 hunks)src/components/LandingPage/RegulatedRails.tsx(1 hunks)src/components/LandingPage/noFees.tsx(4 hunks)src/constants/countryCurrencyMapping.ts(1 hunks)src/hooks/useExchangeRate.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/LandingPage/RegulatedRails.tsx
🧰 Additional context used
🧠 Learnings (15)
📚 Learning: 2025-08-12T17:47:28.362Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/app/api/bridge/exchange-rate/route.ts:4-19
Timestamp: 2025-08-12T17:47:28.362Z
Learning: In the Bridge exchange rate API route (src/app/api/bridge/exchange-rate/route.ts), the ExchangeRateResponse interface uses numeric types for rates because the route converts string values from the Bridge API to floats using parseFloat() before returning the response.
Applied to files:
scripts/compare-rates.mjssrc/components/LandingPage/noFees.tsxsrc/app/api/exchange-rate/route.ts
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-11-18T21:36:11.486Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Applied to files:
src/components/LandingPage/noFees.tsxsrc/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:113-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `PayRequestLink` component (`src/components/Request/Pay/Pay.tsx`), when resolving ENS names, handle errors by displaying an appropriate error message to the user if the ENS cannot be resolved.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-12-11T10:13:22.806Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#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/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T08:56:06.734Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:0-0
Timestamp: 2025-08-14T08:56:06.734Z
Learning: In the peanut-ui project, when implementing error states for API calls, the team consults with Manu to ensure consistent error handling patterns across components.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T09:19:43.965Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:130-130
Timestamp: 2025-08-14T09:19:43.965Z
Learning: When CurrencySelect popover gets clipped by parent containers with overflow-hidden, the preferred solution is to implement Portal rendering in the CurrencySelect component rather than removing overflow rules from parent containers. Portal rendering ensures overlays are rendered outside the normal DOM tree at the document root level.
Applied to files:
src/components/LandingPage/noFees.tsxsrc/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2025-08-14T09:20:37.207Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/hero.tsx:0-0
Timestamp: 2025-08-14T09:20:37.207Z
Learning: In the hero component at src/components/LandingPage/hero.tsx, the height was intentionally reduced from min-h-[100vh] to h-[90vh] to improve scrollability discoverability - so users can see there's more content below to scroll. The overflow-y-hidden is acceptable when other elements are adjusted to prevent clipping.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2025-08-14T12:46:10.663Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1089
File: src/components/LandingPage/noFees.tsx:282-285
Timestamp: 2025-08-14T12:46:10.663Z
Learning: In the peanutprotocol/peanut-ui project, loading states are implemented only after they have been properly designed by the design team to ensure consistency across the application.
Applied to files:
src/components/LandingPage/noFees.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Applied to files:
src/components/LandingPage/CurrencySelect.tsx
📚 Learning: 2025-08-14T14:42:54.399Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.399Z
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/constants/countryCurrencyMapping.ts
🧬 Code Graph Analysis (3)
src/hooks/useExchangeRate.ts (1)
src/hooks/useDebounce.ts (1)
useDebounce(9-23)
src/components/LandingPage/CurrencySelect.tsx (2)
src/constants/countryCurrencyMapping.ts (1)
countryCurrencyMappings(9-35)src/components/Global/Icons/Icon.tsx (1)
Icon(186-195)
src/app/api/exchange-rate/route.ts (2)
src/app/actions/exchange-rate.ts (1)
ExchangeRateResponse(8-15)scripts/compare-rates.mjs (4)
url(67-67)url(81-81)url(94-94)rate(87-87)
⏰ 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 (3)
src/components/LandingPage/CurrencySelect.tsx (1)
59-68: Good fix: rendering Popover content in a Portal avoids mobile clippingThis addresses the widget being hidden due to overflow-hidden ancestors.
src/hooks/useExchangeRate.ts (2)
25-35: Good separation of concerns; query-cached fetch + compute-only effect is the right directionSwapping to react-query for fetch/caching and keeping a pure compute effect is a solid improvement over the prior single-effect approach. This directly addresses earlier race and over-fetching concerns.
89-91: No change needed — repo uses @tanstack/react-query v5 (gcTime is valid)package.json shows @tanstack/react-query@5.8.4, and the hook uses gcTime — that's correct for v5, so no change required.
- File: src/hooks/useExchangeRate.ts — lines ~89–91 (staleTime, gcTime, refetchOnWindowFocus)
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/components/LandingPage/Footer.tsx (2)
18-20: Add missing rel="noopener noreferrer" to external link (security).This external link opens a new tab but lacks rel attributes, exposing reverse-tabnabbing risk and tripping the linter. This was flagged earlier and appears unresolved here.
Apply this diff:
- <a className="underline" href="https://squirrellabs.dev/" target="_blank"> + <a className="underline" href="https://squirrellabs.dev/" target="_blank" rel="noopener noreferrer">
97-107: Fix a11y for decorative icons and remove unnecessary .src usage on SVGs.
- Decorative images should use alt="" and aria-hidden="true".
- For the linked middle-finger icon, rely on the parent anchor’s aria-label and set the image alt="".
- Pass the imported SVG modules directly to Next/Image (src={module}), not .src, for consistency.
- Mirrors prior guidance and aligns with your established pattern (see retrieved learnings).
Apply this diff:
- <Image src={handThumbsUp.src} alt="Hand thumbs up" width={20} height={20} /> + <Image src={handThumbsUp} alt="" width={20} height={20} aria-hidden="true" /> @@ - <Image src={handMiddleFinger.src} alt="Hand Middle finger" width={20} height={20} /> + <Image src={handMiddleFinger} alt="" width={20} height={20} /> @@ - <Image src={handWaving.src} alt="Hand waving" width={25} height={25} /> + <Image src={handWaving} alt="" width={25} height={25} aria-hidden="true" />
🧹 Nitpick comments (2)
src/components/LandingPage/Footer.tsx (2)
24-50: Optional: centralize social URLs and improve label wording.
- Consider importing canonical social URLs (e.g., from src/components/Global/Footer/consts.ts) to avoid duplication and drift across components.
- Minor a11y/copy tweak: “X (Twitter)” is clearer for users.
Example changes:
- aria-label="Follow us on Twitter" + aria-label="Follow us on X (Twitter)"And optionally replace hardcoded hrefs with imported constants:
// At the top of the file // import { DISCORD_URL, X_URL, GITHUB_URL } from '@/components/Global/Footer/consts' <a href={DISCORD_URL} ...>...</a> <a href={X_URL} ...>...</a> <a href={GITHUB_URL} ...>...</a>
14-15: Nit: consider making the brand logo a home link.Common footer/header pattern is to link the logo to “/” for discoverability. Purely optional.
Example:
- <div className="flex"> - <Image src={PEANUT_LOGO} alt="Peanut Logo" width={110} height={40} /> - </div> + <a href="/" className="flex" aria-label="Peanut home"> + <Image src={PEANUT_LOGO} alt="Peanut" width={110} height={40} /> + </a>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/components/LandingPage/Footer.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-24T10:57:15.315Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1000
File: src/components/og/ProfileCardOG.tsx:0-0
Timestamp: 2025-07-24T10:57:15.315Z
Learning: In `src/components/og/ProfileCardOG.tsx`, the scribble image should have an empty alt attribute (alt="") to prevent layout issues if the image fails to load. Since it's a decorative element positioned absolutely over the username text, showing alt text would interfere with the layout and username display.
Applied to files:
src/components/LandingPage/Footer.tsx
🪛 Biome (2.1.2)
src/components/LandingPage/Footer.tsx
[error] 18-18: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".
Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.
(lint/security/noBlankTarget)
⏰ 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/LandingPage/Footer.tsx (1)
52-85: Docs domain fix and link hardening look good.
- Docs now points to docs.peanut.me (resolves the blocking issue).
- All these external links correctly include rel="noopener noreferrer".
* feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com>
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com>
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com>
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> * fix: bank claim flow runtime error (#1138) * hotfix: make iban non optional (#1139) * fix: bank claim flow runtime error * fix: dont have iban as optional * fix: merge conflicts * fix: merge external account with bank details (#1140) * fix: add id to external account (#1142) * added tg footer (#1144) * Hotfix : add missing countries - claim as guest flow (#1146) * fix: add missing countries * remove duplicate comment * fix: show error on dynamic bank account form (#1147) * fix: Invalid IBAN for UK (#1151) --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> Co-authored-by: Hugo Montenegro <hugo@peanut.to>
* HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> * fix: bank claim flow runtime error (#1138) * hotfix: make iban non optional (#1139) * fix: bank claim flow runtime error * fix: dont have iban as optional * fix: merge conflicts * fix: merge external account with bank details (#1140) * fix: add id to external account (#1142) * added tg footer (#1144) * Hotfix : add missing countries - claim as guest flow (#1146) * fix: add missing countries * remove duplicate comment * fix: show error on dynamic bank account form (#1147) * fix: Invalid IBAN for UK (#1151) --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> Co-authored-by: Hugo Montenegro <hugo@peanut.to>
* feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * Prod to staging (#1124) * HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> * fix: dates in receipts (#1105) * [TASK-13865] fix: add tx info on receipt (#1109) * fix: add tx info on receipt * feat: use address explorer url for depositor address * fix(history): check befroe creating address explorer url * Fix: logged in users have to re-login after installing PWA (#1103) * store `LOCAL_STORAGE_WEB_AUTHN_KEY` in cookies * ensure backward compatibility * refactor: move syncLocalStorageToCookie call into useEffect for better lifecycle management * feat: links v2.1 req fulfilment flows (#1085) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * feat: req fulfillment exchange flow * fix: header title * feat: req fulfillment using connected external wallet * fix: navigation and ui * fix: file name * feat: abstract reusbale components from onramp flow for bank fulfilment * feat: handle onramp creation for request fulfilment * feat: reusable verification component * feat: handle bank req fulfilment for peanut users * fix: show all supported countries in req/claim bank flow * feat: show google-pay/apple-pay based on users device * fix: resolve pr review comments * fix: exhange rate hook fallback value * fix: resolve pr comments * Feat: Collect tg username (#1110) * feat: collect tg username * update animations * fix api route * add thinking peanut gif * fix typescript errors * fix typo and reset telegramHandle field on logout * fix: spacing and describe regex rules * add missing export * feat: add sound in success views (#1127) * feat: handle history ui changes for links v2.1 (#1106) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * feat: req fulfillment exchange flow * fix: header title * feat: req fulfillment using connected external wallet * fix: navigation and ui * fix: file name * feat: abstract reusbale components from onramp flow for bank fulfilment * feat: handle onramp creation for request fulfilment * feat: reusable verification component * feat: handle bank req fulfilment for peanut users * fix: show all supported countries in req/claim bank flow * feat: show google-pay/apple-pay based on users device * feat: handle bank send link claim hisotry for peanut users * feat: handle history ui changes for request fulfillment using bank accounts * fix: resolve pr review comments * fix: exhange rate hook fallback value * fix: resolve pr comments * fix: review comments * feat: badges updates (#1119) * feat: badges updates and hook to check for interactions * feat: handle badges for receipts and drawer header * feat: handle badges on request and send flow * feat: tooltip for badges * fix: tooltip positioning * fix: associate a external wallet claim to user if logged in (#1126) * fix: associate a external wallet claim to user if logged in * chore: fix comments * [TASK-14113] fix: handle rpc outage when creating sendlinks (#1120) * HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * fix: handle rpc outage when creating sendlinks * fix: formatting * fix: parallelize geting deposit index --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> * Integrate Daimo Pay (#1104) * add daimo pay * minor improvements * cleanup and add success state * resolve dependency issues * fix: formatting * fix: recent methods redirection * add functions for daimo payment in request fulfilment flow * Integrate daimo in request fulfilment flow * remove hardcoded address * add separate arbitrum usdc flow for deposits * Add risk modal * fix overlay blur * Enhance loading state indication in payment process * Add payer's address in deposit history entry * Add validation * add error handling * remove action and move logic to API route * fix errors * fix: request flow * fix: validation * fixes * add daimo flow in country specific method * fix: slider not working on first attempt * filter supported networks * create reusable daimo button * remove space * remove route.ts file and move logic to server actions * fix: infinite loading edge case * update api and remove delay * fix: layout shift * fix: shadow * update function name * fix: success receipt (#1129) * fix: roboto font not working (#1130) * fix: allow cancel link from the claim page * fix: allow canceling links from the shared receipt (#1134) * fix: send flow cta (#1133) * fix: send flow ctas * fix: success sound on send flow * fix: disabled btn on req pay flow * Fix Daimo bugs (#1132) * fix: bugs * fix cross chain deposit details not correct * fix: request screen UI * add loading state * remove old daimo button * fix: missing dependencies and dead code * add try catch finally block * remove clear Daimo errors inside the balance-check effect * fix copy * minor fixes * move ACTION_METHODS to constants file to remove circular dependency * fix: circular dependency * fix ts error * update daimo version * [TASK-14095] feat: add fallback transport to viem clients (#1131) * feat: add fallback transport to viem clients Use viem fallback transport to handle RPC errors and fallback to other providers. * style: Apply prettier formatting * test: add fallback to viem mock * fix: external claim links history ui + badges fix (#1136) * fix: external claim links history ui + badges fix * fix: resolve codderrabbit suggestions * fix: coderrabbit comment on state stale * Fix: disable add money button on default state + disable sound on IOS (#1145) * fix: add money success screen shows usernmae * disable add money button in default state * disable sound on IOS * Fix: daimo bugs part2 (#1149) * fix: black screen on IOS * fix: sucess screen showed without paying - add money flow * fix currency and double $ in txn history * fix: x-chan token size and add API to get missing token icons * fix: req fulfilment * add default value to tokenData * fix: move useeffect above transaction null check * format amount * fix: space between currency and amount (#1135) * Lock token to USDC arb for peanut ens username (#1128) * Lock token to USDC arb for peanut ens username * add comment * revert variable declaration for sanitizedValue in GeneralRecipientInput component * fix add regex to strip only from the end * [TASK-13900] Feat/kyc modal changes (#1137) * fix: pointer events * fix: modal btns not working on mobile * add missing dependency * remove close button * Chore/prod to dev 106 (#1152) * HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> * fix: bank claim flow runtime error (#1138) * hotfix: make iban non optional (#1139) * fix: bank claim flow runtime error * fix: dont have iban as optional * fix: merge conflicts * fix: merge external account with bank details (#1140) * fix: add id to external account (#1142) * added tg footer (#1144) * Hotfix : add missing countries - claim as guest flow (#1146) * fix: add missing countries * remove duplicate comment * fix: show error on dynamic bank account form (#1147) * fix: Invalid IBAN for UK (#1151) --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> Co-authored-by: Hugo Montenegro <hugo@peanut.to> * [TASK-13950] Fix: incorrect token amount on second withdraw (#1150) * fix: incorrect token amount on second withdraw * move `resetTokenContextProvider()` to unmount callback * fix: transaction explorer url for deposits * fix: history skeleton copy * save token and chain details for cross chain req-fulfilments (#1154) * fix: send links history ui for senders pov when claimed to bank accounts (#1156) * fix: sort action list methods * fix: send links claimed to bank accounts history ui for senders pov * fix: issues for request link paying with bank (#1158) - Specify recipient when creating onramp for request fulfillment - Use correct amount depending on currency * fix: stop cleaning error by bic field (#1159) We now always clear before starting submission and also bic field will always show, so that logic is not needed anymore. * fix: claim country currency and amount, fallback to $ (#1164) * feat: show local bank currency incase of bank claims * fix: activity rows for sender's send link history * fix: verification modal when claiming * fix: state issue when new user tries to claim to bank * fix: request pay copy (#1165) * fix: close kyc modal btn (#1166) * Fix testing github action (#1167) * chore: remove prettier action When commiting it clashes with signature verification rules * chore: update test action setup version * fix: install first * fix: actually make sure that cancelledDate is a Date (#1170) * fix: icon and margin (#1171) * Hide pay with wallet button in Daimo component (#1172) * hide pay with wallet button * improve targeted css approach * Fix: Daimo bug and activity receipt bug (#1175) * sligify chain name * fix: stale state issue * hide row if tokenData is not present * Fix/conflicts (#1177) * HOTFIX - IBAN country detection and incorrect bank acc details (#1094) * Fix: Iban country detection and incorrect bank acc details * Fix: update IBAN country validation to use correct locale string comparison * add validations for US and mexican bank accounts * fix typo * fix claim flow and create a reusable function for getting 3 letter code * fix country code mismatch * fix: show error below input field * remove unnecessary checks * remove unnecessary CLABE check * Prod LP v2.1 (#1098) * feat: lpv2.1 * fix: gigaclouds, font and exchange widget * fixes and improvements * remove duplicate export * remove unused component * Fix: Landing page hero section responsiveness issue (#1107) * fix: hero section responsiveness issue * fix: stars position * fix height on desktop * remove unused code * fix margins (#1113) * [TASK-14052] Prod release 105 (#1122) * feat: handle send link claims to bank account for peanut users (#1078) * reafactor: create reusable country list component and use it for all the flows * feat: reusable user accounts components * feat: handle different cases based on kyc status for bank claim * fix: account creation * chore: add docstring to hooks * chore: better comments for bank flow manager * fix: kyc modal closing after tos acceptance issue * fix: remove bank acc caching from withdraw flow * fix: update confirm claim modal copy * fix: remove bank acc caching from claim flow * fix: navheader title * remove duplicate debounce code and use `useDebounce` hook instead (#1079) * Landing page v2.1 (#1089) * lpv2.1 part 1 * Add exchange widget * add and integrate exchange API * add yourMoney component bg * update landing countries svg * integrate frankfurter API * fixes and improvements * decrease hero section height * allow max 2 decimal places * Add `/exchange` route * fix: overlay * make destination amount editable and bugg fixes * some fixes & currency improvements * crucial commit * fix checkmark, font size and weight --------- Co-authored-by: Hugo Montenegro <h@hugo0.com> * [TASK-13186] refactor: use networkName instead of axelarChainName (#1095) * refactor: use networkName instead of axelarChainName * fix: types * fix: onramp currency (#1096) * fix: stretched favicon (#1099) * [TASK-13971] fix: scientific notation in eip681 parsing (#1097) * fix: scientific notation in eip681 parsing * fix: qr handling tests * fix: peanut sdk mock * pull iban hotfix (#1100) * fix: claim flow bugs (#1102) * fix: cross chain claim * fix: full name issue on confirm bank claim view * fix: back navigation on desktop views * Fix back button not working on /profile (#1101) * Fix back button not working * fix public profile page * extract internal navigation logic to utility function * fix: send link claims to us bank accounts (#1108) * fix: usa bank account claims * fix: show bank account details in confirm claim view * Sync Landing page changes (#1111) * reduce clouds size and update font * fix: hero section responsiveness issue * fix: formatting errors * add currency animation * fix: us bank claims after kyc for logged in users (#1112) * fix: trim account form inputs for spaces (#1114) * [TASK-14107] fix: don't allow claiming on xChain if route is not found (#1115) * fix: don't allow claiming on xChain if route is not found * fix(claim): use correct decimals for min receive amount * feat: handle redirect uri when on unsupported browsers (#1117) * feat: handle redirect uri when on unsupported browsers * fix: confirm bank claim ui rows for iban guest claim * remove animation (#1118) * fix: formatting --------- Co-authored-by: Kushagra Sarathe <76868364+kushagrasarathe@users.noreply.github.com> Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> * fix: bank claim flow runtime error (#1138) * hotfix: make iban non optional (#1139) * fix: bank claim flow runtime error * fix: dont have iban as optional * fix: merge conflicts * fix: merge external account with bank details (#1140) * fix: add id to external account (#1142) * added tg footer (#1144) * Hotfix : add missing countries - claim as guest flow (#1146) * fix: add missing countries * remove duplicate comment * fix: show error on dynamic bank account form (#1147) * fix: Invalid IBAN for UK (#1151) --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> Co-authored-by: Hugo Montenegro <hugo@peanut.to> --------- Co-authored-by: Mohd Zishan <72738005+Zishan-7@users.noreply.github.com> Co-authored-by: Hugo Montenegro <h@hugo0.com> Co-authored-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com> Co-authored-by: Juan José Ramírez <artjjrn@gmail.com> Co-authored-by: Hugo Montenegro <hugo@peanut.to>


Contributes to TASK-13599 and TASK-13479