Skip to content

Feat/request pots#1332

Merged
Zishan-7 merged 32 commits intopeanut-wallet-devfrom
feat/request-pots
Oct 24, 2025
Merged

Feat/request pots#1332
Zishan-7 merged 32 commits intopeanut-wallet-devfrom
feat/request-pots

Conversation

@Zishan-7
Copy link
Contributor

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

Contributes to
TASK-15548
TASK-15663


1. Request Creation

Request Creation

2. Request Receipts

Pending

Request Receipt - Pending

Closed

Request Receipt - Closed

Cancelled

Request Receipt - Cancelled

3. Request Fulfillment

Request Fulfillment 1 Request Fulfillment 2

@vercel
Copy link

vercel bot commented Oct 16, 2025

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

Project Deployment Preview Comments Updated (UTC)
peanut-wallet Ready Ready Preview Comment Oct 24, 2025 1:42pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

Walkthrough

Adds request-pot (payment link) features: contributors and progress UI, request close API, ProgressBar/Slider/Contributors components, request-pay wiring through payment flows/hooks, multiple type and API changes, and UI adjustments across avatar/status/transaction components.

Changes

Cohort / File(s) Summary
Initial payment view
src/app/[...recipient]/client.tsx
Passes showRequestPotInitialView to InitialPaymentView (derived from requestId) and hides ActionList when requestId is present.
Icons
src/assets/icons/index.ts
Added exports: SOCIALS_ICON, COIN_ICON.
New UI: Progress & Contributors
src/components/Global/ProgressBar/index.tsx, src/components/Global/Contributors/*
Adds ProgressBar component (props: goal, progress, isClosed), Contributors list and ContributorCard component/type; renders contributors and positions via getCardPosition.
Slider & Token input
src/components/Global/Slider/index.tsx, src/components/Global/TokenAmountInput/index.tsx
Adds Radix-based Slider with snap points; TokenAmountInput gains showInfoText, infoText, showSlider, maxAmount and slider integration.
QRCode & small UI tweaks
src/components/Global/QRCodeWrapper/index.tsx, src/components/Global/PeanutActionCard/index.tsx, src/components/Kyc/KycStatusItem.tsx
QR wrapper max width reduced (192→160px); PeanutActionCard spacing/copy and socials icon row added; KYC status badge size reduced.
Avatar / Status UI
src/components/Profile/AvatarWithBadge.tsx, src/components/Global/Badges/StatusBadge.tsx, src/components/Global/StatusPill/index.tsx, src/components/TransactionDetails/TransactionAvatarBadge.tsx
Removed showStatusPill/statusPillStatus props from AvatarWithBadge and callers; added closed status mapping/variant; adjusted icon/size mappings.
Transaction components & transformer
src/components/TransactionDetails/transactionTransformer.ts, src/components/TransactionDetails/transaction-details.utils.ts, src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx, src/components/TransactionDetails/TransactionDetailsReceipt.tsx, src/components/TransactionDetails/TransactionCard.tsx
Adds isRequestPotLink, requestPotPayments, totalAmountCollected to transaction model; maps CLOSEDclosed; header supports ProgressBar; receipt renders Contributors, close request flow, and new row key 'closed'; amount display tweaks.
User & Payment flow
src/components/User/UserCard.tsx, src/components/Payment/PaymentForm/index.tsx, src/hooks/usePaymentInitiator.ts
UserCard adds request_pay type and amount props; PaymentForm gains showRequestPotInitialView?: boolean and request-pot UI/flow gating; usePaymentInitiator adds returnAfterChargeCreation?: boolean and uses router for URL updates.
Request link & requests API
src/components/Request/link/views/Create.request.link.view.tsx, src/services/requests.ts, src/services/services.types.ts
Request link view simplified (URL params support); requests API switched to JSON + JWT auth, added requestsApi.close(uuid); removed attachment/mimeType/filename from CreateRequestRequest; added totalCollectedAmount to response type.
Request routing & pages
src/app/(mobile-ui)/request/page.tsx, src/app/(mobile-ui)/request/create/page.tsx, src/components/Request/views/RequestRouter.view.tsx
Mobile request page now renders CreateRequestLinkView; deleted mobile create page and removed RequestRouterView component.
Utilities & history types
src/utils/general.utils.ts, src/utils/history.utils.ts
formatCurrency signature extended with maxDecimals/minDecimals; added getContributorsFromCharge; EHistoryStatus adds CLOSED; HistoryEntry gains isRequestLink, charges, totalAmountCollected.
QR-pay payloads
src/app/(mobile-ui)/qr-pay/page.tsx
Adds totalAmountCollected: Number(usdAmount) to transaction payloads when opening transaction details.
Misc / config / icons
tailwind.config.js, src/components/Global/Icons/*
Added colors.error.4 token; added split icon component and extended IconName mapping.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Hugo0

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Feat/request pots" directly and clearly describes the primary feature being added in this changeset. The raw summary shows extensive changes implementing request pot functionality including request creation flows, transaction receipt handling with multiple states (pending, closed, cancelled), contributor tracking, progress bars for tracking request goals, and request closing logic. This aligns perfectly with the title and demonstrates a coherent feature addition. The title is concise, specific, and avoids vague terminology.
Description Check ✅ Passed The PR description is clearly related to the changeset and provides meaningful context about the feature being implemented. It references specific task identifiers (TASK-15548, TASK-15663), describes the main feature components (Request Creation, Request Receipts in three states, and Request Fulfillment), and includes visual mockups demonstrating the functionality. This aligns with the extensive code changes throughout the changeset that implement these exact features including new components, state management, and UI flows.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/request-pots

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (2)
src/components/Request/link/views/Create.request.link.view.tsx (1)

121-126: AbortController is not wired: abort() won’t cancel in-flight requests

You create an AbortController but never pass its signal to requestsApi/chargesApi. Aborting here won’t stop network calls.

Apply a pattern like this (adjust to your API shape):

- createLinkAbortRef.current = new AbortController()
+ createLinkAbortRef.current = new AbortController()
  ...
- const requestDetails = await requestsApi.create(requestData)
+ const requestDetails = await requestsApi.create(requestData, { signal: createLinkAbortRef.current.signal })
  ...
- const charge = await chargesApi.create({
+ const charge = await chargesApi.create({
     pricing_type: 'fixed_price',
     local_price: {
       amount: requestDetails.tokenAmount,
       currency: 'USD',
     },
     baseUrl: BASE_URL,
     requestId: requestDetails.uuid,
     transactionType: 'REQUEST',
- })
+ }, { signal: createLinkAbortRef.current.signal })

If your API wrappers don’t accept AbortSignals, consider adding it or removing the unused AbortController.

Also applies to: 168-181

src/components/User/UserCard.tsx (1)

46-59: Stale closure risk: missing deps in useCallback

getTitle uses fullName/username but depends only on type. If names change, title won’t update.

- }, [type])
+ }, [type, fullName, username])
🧹 Nitpick comments (17)
src/components/Kyc/KycStatusItem.tsx (1)

95-95: Confirm size token and keep sizing consistent

Verify AvatarWithBadge supports size="extra-small" and that it matches sizing tokens used elsewhere (e.g., badges/pills) to avoid visual drift. If not universally supported, prefer a shared enum/const.

src/app/[...recipient]/client.tsx (1)

509-512: Remove or track commented prop

Either remove the commented isInviteLink prop or add a TODO with owner/date. Dead comments add noise.

src/components/Request/link/views/Create.request.link.view.tsx (2)

369-375: Don’t use useMemo for side effects; switch to useEffect

Setting defaults via setSelectedChainID/setSelectedTokenAddress in useMemo is a side-effect and can be skipped by React. Use useEffect.

- useMemo(() => {
+ useEffect(() => {
     if (isConnected && address) {
       setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
       setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
     }
- }, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
+ }, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])

77-81: Remove unused derived values (minor cleanup)

usdValue and isValidRecipient are computed but not used.

- const usdValue = useMemo(() => {
-   if (!selectedTokenPrice || !tokenValue) return ''
-   return (parseFloat(tokenValue) * selectedTokenPrice).toString()
- }, [tokenValue, selectedTokenPrice])
...
- const isValidRecipient = useMemo(() => {
-   return isConnected && !!address
- }, [isConnected, address])

Also applies to: 87-90

src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (1)

223-225: Avoid hardcoded ProgressBar values; make it data‑driven or remove

ProgressBar(goal=100, progress=20, isClosed=false) is static and misleading in production.

Expose props (goal/progress/isClosed) or compute from transaction data; otherwise omit until real values are available.

src/components/Payment/Views/RequestFulfillmentViews/RequestFulfillmentFlow.tsx (1)

18-18: Hook up back navigation

onPrev={() => {}} is a no-op. Use router.back() or a provided callback to support navigation.

+ import { useRouter } from 'next/navigation'
  ...
- <NavHeader onPrev={() => {}} title="Pay" />
+ const router = useRouter()
+ <NavHeader onPrev={() => router.back()} title="Pay" />
src/components/Global/TokenAmountInput/index.tsx (1)

53-55: Guard window usage to avoid SSR pitfalls

If this component is ever rendered without a client boundary, window will be undefined. Either add 'use client' at top of this file or guard.

- const inputType = useMemo(() => (window.innerWidth < 640 ? 'text' : 'number'), [])
+ const inputType = useMemo(() => {
+   if (typeof window === 'undefined') return 'number'
+   return window.innerWidth < 640 ? 'text' : 'number'
+ }, [])
src/components/Global/Slider/index.tsx (7)

10-16: Adopt a true controllable/uncontrollable pattern and surface min/max/step explicitly.

Avoid two sources of truth by deriving a single displayedValue and only setting state when uncontrolled. Also expose min/max/step in the signature so the labels can reflect overrides.

-function Slider({
-    className,
-    defaultValue = [100],
-    value: controlledValue,
-    onValueChange,
-    ...props
-}: React.ComponentProps<typeof SliderPrimitive.Root>) {
+function Slider({
+    className,
+    defaultValue = [100],
+    value: controlledValue,
+    onValueChange,
+    min = 0,
+    max = 120,
+    step = 1,
+    ...props
+}: React.ComponentProps<typeof SliderPrimitive.Root>) {
     // Use internal state for the slider value to enable magnetic snapping
-    const [internalValue, setInternalValue] = React.useState<number[]>(controlledValue || defaultValue)
+    const isControlled = controlledValue !== undefined
+    const [internalValue, setInternalValue] = React.useState<number[]>(defaultValue)
+    const displayedValue = isControlled ? (controlledValue as number[]) : internalValue

20-26: Remove sync effect; derive display from props instead.

The effect keeps internal state in lockstep with controlled prop, creating two sources of truth. With displayedValue, this effect is unnecessary.

-    // Sync with controlled value if it changes externally
-    React.useEffect(() => {
-        if (controlledValue) {
-            setInternalValue(controlledValue)
-        }
-    }, [controlledValue])
+    // No effect needed; displayedValue reflects controlledValue directly when provided

28-31: Compute snap indicator from displayedValue.

Ensures correct behavior in controlled mode.

-    const activeSnapPoint = React.useMemo(() => {
-        return SNAP_POINTS.find((snapPoint) => Math.abs(internalValue[0] - snapPoint) < 0.5)
-    }, [internalValue])
+    const activeSnapPoint = React.useMemo(() => {
+        return SNAP_POINTS.find((snapPoint) => Math.abs(displayedValue[0] - snapPoint) < 0.5)
+    }, [displayedValue])

32-55: Only set state when uncontrolled; compare against displayedValue.

Prevents controlled+internal state drift and unnecessary re-renders.

-    const handleValueChange = React.useCallback(
+    const handleValueChange = React.useCallback(
         (newValue: number[]) => {
             const rawValue = newValue[0]
             let finalValue = rawValue
@@
             const finalArray = [finalValue]
 
             // Only update if the value actually changed
-            if (internalValue[0] !== finalValue) {
-                setInternalValue(finalArray)
-                onValueChange?.(finalArray)
-            }
+            if (displayedValue[0] !== finalValue) {
+                if (isControlled) {
+                    onValueChange?.(finalArray)
+                } else {
+                    setInternalValue(finalArray)
+                    onValueChange?.(finalArray)
+                }
+            }
         },
-        [onValueChange, internalValue]
+        [onValueChange, displayedValue, isControlled]
     )

65-69: Bind Root value to displayedValue and pass explicit min/max/step.

Keeps the visual value in sync in both modes and prevents {...props} from overriding unintentionally.

-                value={internalValue}
+                value={displayedValue}
                 onValueChange={handleValueChange}
-                min={0}
-                max={120}
-                step={1}
+                min={min}
+                max={max}
+                step={step}

101-103: Render label from displayedValue.

So labels reflect controlled values too.

-                        {internalValue[0] % 1 === 0 ? internalValue[0].toFixed(0) : internalValue[0].toFixed(2)}%
+                        {displayedValue[0] % 1 === 0 ? displayedValue[0].toFixed(0) : displayedValue[0].toFixed(2)}%

7-9: Optional: Make snap config customizable via props.

Consider exposing snapPoints and snapThreshold with sensible defaults to reuse Slider in other contexts.

src/components/Global/ProgressBar/index.tsx (3)

19-20: Unify currency formatting across the app.

A local $${value.toFixed(2)} may diverge from src/utils/general.utils.ts formatting (grouping, locales). Prefer the shared util or rename this helper to avoid confusion.

Would you like me to patch this to use the shared formatter (and add the “$” prefix if needed)?


76-86: Minor: remove unused parameter in renderMarker.

isPercentage is unused and can be dropped, along with the extra argument at call sites.


104-133: Optional hardening: clamp widths and positions at render-time.

Even with guards, defensive clamping in styles prevents visual glitches from unexpected inputs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between afad7e6 and 15bc994.

⛔ Files ignored due to path filters (1)
  • src/assets/icons/socials.svg is excluded by !**/*.svg
📒 Files selected for processing (18)
  • src/app/[...recipient]/client.tsx (3 hunks)
  • src/assets/icons/index.ts (1 hunks)
  • src/components/Global/PeanutActionCard/index.tsx (1 hunks)
  • src/components/Global/ProgressBar/index.tsx (1 hunks)
  • src/components/Global/QRCodeWrapper/index.tsx (1 hunks)
  • src/components/Global/Slider/index.tsx (1 hunks)
  • src/components/Global/StatusPill/index.tsx (1 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (7 hunks)
  • src/components/Kyc/KycStatusItem.tsx (1 hunks)
  • src/components/Payment/Views/RequestFulfillmentViews/RequestFulfillmentFlow.tsx (1 hunks)
  • src/components/Profile/AvatarWithBadge.tsx (0 hunks)
  • src/components/Request/link/views/Create.request.link.view.tsx (2 hunks)
  • src/components/TransactionDetails/TransactionAvatarBadge.tsx (0 hunks)
  • src/components/TransactionDetails/TransactionCard.tsx (3 hunks)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (4 hunks)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (3 hunks)
  • src/components/User/UserCard.tsx (5 hunks)
  • tailwind.config.js (1 hunks)
💤 Files with no reviewable changes (2)
  • src/components/TransactionDetails/TransactionAvatarBadge.tsx
  • src/components/Profile/AvatarWithBadge.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay` components, the `tokenPrice` property in the `IPayScreenProps` interface is only relevant to these views. Other components using `IPayScreenProps` do not need to handle `tokenPriceData` when it's updated in these components.

Applied to files:

  • src/components/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Global/TokenAmountInput/index.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/Global/TokenAmountInput/index.tsx
🧬 Code graph analysis (5)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/User/UserCard.tsx (2)
src/utils/color.utils.ts (2)
  • getColorForUsername (51-69)
  • AVATAR_TEXT_DARK (44-44)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-109)
src/components/Global/PeanutActionCard/index.tsx (1)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (206-215)
src/components/Global/ProgressBar/index.tsx (1)
src/utils/general.utils.ts (1)
  • formatCurrency (382-384)
src/components/Payment/Views/RequestFulfillmentViews/RequestFulfillmentFlow.tsx (1)
src/components/0_Bruddle/Button.tsx (1)
  • Button (76-267)
🔇 Additional comments (8)
tailwind.config.js (1)

115-115: Add documentation for error.3 vs error.4 semantics

The new error.4 shade (#FC5555) is used in src/components/Global/ProgressBar/index.tsx for failure states but is very close to error.3 (#FF4A4A). Document in tailwind.config.js or your design guidelines when to use each token to avoid confusion.

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

24-24: SOCIALS_ICON export looks good

Naming matches existing pattern. Ensure the consuming Image/inline SVG provides meaningful alt text or aria-hidden as appropriate.

src/components/Global/QRCodeWrapper/index.tsx (1)

15-15: Reduced QR size: verify scan reliability

At max 160px, scanning can be device-sensitive. Please QA on mid/low-end Android and iOS camera/WA scanners to ensure reliable detection at typical viewing distances. If flaky, consider 176–192px min at small breakpoints.

src/app/[...recipient]/client.tsx (1)

36-36: No action needed

Import is straightforward and scoped to client component.

src/components/Global/StatusPill/index.tsx (1)

31-37: Overlay concern not applicable StatusPill renders as an inline flex container with no absolute positioning and only a single inline usage in TransactionCard; no overlay consumers detected.

Likely an incorrect or invalid review comment.

src/components/Request/link/views/Create.request.link.view.tsx (1)

392-394: TokenAmountInput info text and ShareButton label logic look good

The new props are wired correctly; dynamic label handles empty/zero amounts.

Also applies to: 410-415

src/components/TransactionDetails/TransactionCard.tsx (1)

121-122: LGTM: status presentation and spacing improvements

StatusPill moved to the action row reads cleaner; icon sizing and badge size changes are consistent.

Also applies to: 138-142, 171-207

src/components/Global/PeanutActionCard/index.tsx (1)

1-1: SOCIALS_ICON import path is correctsrc/assets/index.ts re-exports from ./icons, so import { SOCIALS_ICON } from '@/assets' is valid.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/app/[...recipient]/client.tsx (1)

478-481: Past review concern remains unresolved: effects and loading still run for 'request_pay' flow.

The early return for flow === 'request_pay' doesn't prevent the effects above (lines 168-446) from executing, causing unnecessary API calls, state updates, and potential bugs. The isLoading calculation (lines 457-458) also runs regardless of flow, which could show a spinner that masks RequestFulfillmentFlow.

Gate the effects and loading state when flow === 'request_pay':

  1. Update isLoading calculation to exclude 'request_pay':
 const isLoading =
+    flow !== 'request_pay' &&
     (!isUrlParsed || (chargeId && !chargeDetails) || isRequestDetailsFetching || isDaimoPaymentProcessing)
  1. Add early returns at the top of each effect body that shouldn't run for 'request_pay'. For example:
 useEffect(() => {
+    if (flow === 'request_pay') return
     let isMounted = true
     const fetchParsedURL = async () => {
         // existing logic...
     }
     // ...
 }, [recipient, user, isUrlParsed, dispatch, isDirectPay, chargeId, requestId])

Apply similar guards to all effects that perform data fetching or state mutations when they're not needed for RequestFulfillmentFlow.

If RequestFulfillmentFlow manages its own data lifecycle, consider moving shared logic into that component or a dedicated provider used only when flow === 'request_pay'.

Based on past review comments.

src/components/Global/TokenAmountInput/index.tsx (1)

139-149: Slider unit mismatch and forced USD mode remain unaddressed.

The hardcoded true parameter forces USD mode regardless of current displayMode or isInputUsd state, causing incorrect conversions for non-USD currencies or token-denominated inputs. Additionally, the slider position calculation at line 311 divides tokenValue by maxAmount, which produces meaningless ratios if these are in different units (e.g., tokens vs USD).

This issue was previously flagged with a detailed fix suggestion. Please refer to the earlier review comment for the recommended refactor.

🧹 Nitpick comments (1)
src/components/Global/TokenAmountInput/index.tsx (1)

7-7: Unnecessary import alias.

Icon is already imported at line 5 and can be used directly at line 303 instead of creating an alias IconComponent.

Apply this diff to remove the alias:

-import { Icon as IconComponent } from '@/components/Global/Icons/Icon'

Then update line 303:

-                    <IconComponent name="info" size={12} className="text-grey-1" />
+                    <Icon name="info" size={12} className="text-grey-1" />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15bc994 and 122c9c9.

📒 Files selected for processing (8)
  • src/app/(mobile-ui)/request/page.tsx (2 hunks)
  • src/app/[...recipient]/client.tsx (3 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (7 hunks)
  • src/components/Request/link/views/Create.request.link.view.tsx (3 hunks)
  • src/components/Request/views/RequestRouter.view.tsx (0 hunks)
  • src/components/TransactionDetails/TransactionCard.tsx (3 hunks)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (3 hunks)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (3 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Request/views/RequestRouter.view.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx
  • src/components/Request/link/views/Create.request.link.view.tsx
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 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/Global/TokenAmountInput/index.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/Global/TokenAmountInput/index.tsx
🧬 Code graph analysis (1)
src/app/(mobile-ui)/request/page.tsx (1)
src/components/Request/link/views/Create.request.link.view.tsx (1)
  • CreateRequestLinkView (29-425)
⏰ 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/TransactionDetails/TransactionCard.tsx (3)

111-130: LGTM! Avatar rendering restructured correctly.

The avatar rendering logic has been properly refactored to remove the StatusPill from the avatar area. The conditional rendering based on avatarUrl presence is sound, and the fallback to TransactionAvatarBadge without a status prop aligns with the coordinated changes in related components.


145-149: LGTM! Status pill successfully relocated to action row.

The StatusPill has been appropriately moved to the action row with proper conditional rendering and improved spacing (gap-2 instead of gap-1). This placement provides better context by grouping the status with the action text, improving both readability and accessibility.


189-189: Verify icon size reduction is intentional.

The default iconSize has been changed from 8 to 7, which now applies to directional arrows (up-right, down-left) for 'send', 'request', 'receive', and 'bank_request_fulfillment' transaction types. Vertical arrows (up/down) explicitly retain iconSize = 8.

Confirm that this visual differentiation between arrow icon types is intentional and doesn't introduce inconsistencies in the UI.

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

13-19: Verify navigation and routing behavior.

The page now renders CreateRequestLinkView directly instead of RequestRouterView. This architectural change may impact how users navigate between different request states (e.g., create, view, edit). Please verify:

  1. User flows for creating, viewing, and managing requests still work as expected.
  2. Any routing logic previously handled by RequestRouterView has been appropriately migrated.
  3. There are no broken navigation paths or missing views.

The component replacement looks syntactically correct, but functional testing is recommended to ensure the request flow works end-to-end.


3-3: Import path is valid and migration is complete.

The new import path @/components/Request/link/views/Create.request.link.view is correct, and RequestRouterView has been fully removed from the codebase. No breaking changes or incomplete migrations detected.

src/components/Global/TokenAmountInput/index.tsx (3)

27-30: LGTM!

The new props are well-typed, optional, and correctly destructured with sensible defaults.

Also applies to: 46-49


225-225: LGTM!

The increased padding improves spacing and visual balance.


301-306: LGTM!

The conditional rendering and styling for the info text pill are well-implemented.

[TASK-15664] refactor: change request api to use jwt token
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (3)
src/components/Request/link/views/Create.request.link.view.tsx (2)

258-266: Validation conflicts with "open request" feature.

The validation at line 260 prevents auto-creating requests when attachments are added but tokenValue is empty or zero. This conflicts with the "open request" feature where users can share requests without specifying an amount (as indicated by the "Share open request" button text at line 389).

Impact: Users who add a message or file before clicking share won't get auto-save functionality if tokenValue is empty, creating inconsistent behavior between the auto-save flow and the manual share flow (which works via generateLink).

Consider removing or adjusting this validation:

-        if (!requestId && (debouncedAttachmentOptions.rawFile || debouncedAttachmentOptions.message)) {
-            if (!tokenValue || parseFloat(tokenValue) <= 0) return
-
-            const link = await createRequestLink(debouncedAttachmentOptions)
+        if (!requestId && (debouncedAttachmentOptions.rawFile || debouncedAttachmentOptions.message)) {
+            const link = await createRequestLink(debouncedAttachmentOptions)
             if (link) {
                 setGeneratedLink(link)
             }
         }

317-332: Validation may prevent legitimate workflows for open requests.

The early return at line 318 prevents any action when tokenValue is empty or zero. While this function primarily handles updates to existing requests (line 323), this validation could prevent legitimate workflows where users want to update attachment options on an open request.

If open requests (empty tokenValue) are supported, consider allowing updates even when tokenValue is empty:

 const handleTokenAmountSubmit = useCallback(async () => {
-    if (!tokenValue || parseFloat(tokenValue) <= 0) return
     if (isCreatingLink || isUpdatingRequest) return // Prevent duplicate calls
 
     if (hasUnsavedChanges) {
         // PATCH: Update existing request
         await updateRequestLink(debouncedAttachmentOptions)
     }
 }, [
     tokenValue,
     debouncedAttachmentOptions,
     hasUnsavedChanges,
     updateRequestLink,
     isCreatingLink,
     isUpdatingRequest,
 ])
src/hooks/usePaymentInitiator.ts (1)

374-387: Reduce sensitive logging in payment creation path.

These logs print full payloads/responses (attachments, recipient addresses, IDs). Risky for PII/compliance and noisy in production.

Apply this pattern:

-                console.log('Creating charge with payload:', createChargeRequestPayload)
+                if (process.env.NODE_ENV === 'development') {
+                    console.debug('Creating charge (sanitized payload)', {
+                        pricing_type: createChargeRequestPayload.pricing_type,
+                        local_price: createChargeRequestPayload.local_price,
+                        requestId: createChargeRequestPayload.requestId,
+                        transactionType: createChargeRequestPayload.transactionType,
+                    })
+                }
-                console.log('Charge created response:', charge)
+                if (process.env.NODE_ENV === 'development') console.debug('Charge created')

Repeat similar sanitization for other console.log in this file that dump entire objects (payments, receipts, routes).

♻️ Duplicate comments (1)
src/components/User/UserCard.tsx (1)

112-114: Zero-friendly guards fixed—looks good

The ProgressBar now renders for 0/positive values correctly using explicit undefined checks. Nice.

🧹 Nitpick comments (12)
src/components/Global/Contributors/ContributorCard.tsx (1)

30-39: Tighten UX semantics.

  • Setting haveSentMoneyToUser={true} may over-signal double‑check; consider wiring real signal or defaulting false.
  • cursor-pointer without an onClick can confuse.
-        <Card position={position} className="cursor-pointer">
+        <Card position={position}>
@@
-                    <VerifiedUserLabel
+                    <VerifiedUserLabel
                         username={contributor.username ?? ''}
                         name={contributor.username ?? ''}
                         isVerified={contributor.isUserVerified}
-                        haveSentMoneyToUser={true}
+                        haveSentMoneyToUser={false}
                     />
src/utils/history.utils.ts (1)

225-235: Harden request‑link URL construction.

Guard against edge cases (e.g., spaces, unusual symbols) and ensure stable formatting.

-                const tokenCurrency = entry.tokenSymbol
-                const tokenAmount = entry.amount
-                link = `${BASE_URL}/${entry.recipientAccount.username || entry.recipientAccount.identifier}/${tokenAmount}${tokenCurrency}?id=${entry.uuid}`
+                const tokenCurrency = (entry.tokenSymbol || '').toUpperCase().replace(/[^A-Z0-9]/g, '')
+                const tokenAmount = String(entry.amount).trim()
+                const recipient = encodeURIComponent(entry.recipientAccount.username || entry.recipientAccount.identifier)
+                link = `${BASE_URL}/${recipient}/${encodeURIComponent(`${tokenAmount}${tokenCurrency}`)}?id=${encodeURIComponent(entry.uuid)}`
src/components/TransactionDetails/TransactionCard.tsx (1)

120-130: Next/Image sizing mismatches the container; avoid blurry scaling

Container is 48×48 (h-12 w-12) but Image is 30×30. Align to container and use object-cover.

-    <div className={'relative flex h-12 w-12 items-center justify-center rounded-full'}>
-        <Image
-            src={avatarUrl}
-            alt="Icon"
-            className="size-12 object-contain"
-            width={30}
-            height={30}
-        />
-    </div>
+    <div className="relative flex h-12 w-12 items-center justify-center rounded-full">
+        <Image
+            src={avatarUrl}
+            alt="Avatar"
+            className="h-12 w-12 rounded-full object-cover"
+            width={48}
+            height={48}
+        />
+    </div>
src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (3)

44-48: Prop name typo: showProgessBar → showProgressBar

Fix spelling here and update all call sites for consistency.

-    showProgessBar?: boolean
+    showProgressBar?: boolean
@@
-    showProgessBar = false,
+    showProgressBar = false,
@@
-            {!isNoGoalSet && showProgessBar && goal !== undefined && progress !== undefined && (
+            {!isNoGoalSet && showProgressBar && goal !== undefined && progress !== undefined && (

Then replace all usages in other files (e.g., TransactionDetailsReceipt) accordingly.

Also applies to: 180-184, 255-259


197-198: 'No goal' detection misses undefined/NaN; handle all non-positive/absent cases

Current check only matches exactly 0. Use a broader guard.

-const isNoGoalSet = isRequestPotTransaction && goal === 0
+const isNoGoalSet =
+    !!isRequestPotTransaction && (goal === 0 || goal == null || Number.isNaN(goal))

205-211: Align Image intrinsic size with 64×64 container

Prevents unnecessary scaling.

-<Image
+<Image
     src={avatarUrl}
     alt="Icon"
-    className="size-full rounded-full object-cover"
-    width={160}
-    height={160}
+    className="size-full rounded-full object-cover"
+    width={64}
+    height={64}
 />
src/components/User/UserCard.tsx (2)

61-61: Stale closure risk: add dependencies to useCallback

Title uses fullName/username; include them in deps.

-}, [type])
+}, [type, fullName, username])

63-69: Format dollar amount in address/title; show “Pay what you want” for 0/undefined

Use the shared formatter; keep username fallback.

-const getAddressLinkTitle = () => {
-    if (isRequestPot && amount && amount > 0) return `$${amount}` // If goal is set.
-    if (!amount && isRequestPot) return `Pay what you want` // If no goal is set.
-    return username
-}
+const getAddressLinkTitle = () => {
+    if (isRequestPot && typeof amount === 'number' && amount > 0) {
+        return `$${formatCurrency(String(amount), 2, 0)}`
+    }
+    if ((amount == null || amount === 0) && isRequestPot) return 'Pay what you want'
+    return username
+}

Add missing import:

+import { formatCurrency } from '@/utils'
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (2)

51-53: Remove unused import

TransactionCard isn’t used here.

-import TransactionCard from './TransactionCard'
 import ContributorCard from '../Global/Contributors/ContributorCard'

293-295: Token details effect uses transaction but has an empty deps array

If the component re-renders with a different transaction, token details won’t refresh.

-}, [])
+}, [transaction?.tokenDisplayDetails?.chainName, transaction?.tokenAddress])
src/components/Payment/PaymentForm/index.tsx (2)

320-323: Remove redundant condition in return statement.

Line 323 redundantly checks && isRequestPotPayment when it's already verified in the condition on line 321.

Apply this diff:

 // If its requestPotPayment, we only need to check if the recipient exists, amount is set, and token is selected
 if (isRequestPotPayment) {
-    return recipientExists && amountIsSet && tokenSelected && isRequestPotPayment
+    return recipientExists && amountIsSet && tokenSelected
 }

630-635: Memoize contributors derivation for performance.

Line 630 recalculates contributors on every render. Since totalAmountCollected depends on contributors and is memoized, contributors should also be memoized to prevent unnecessary recalculations.

Apply this diff:

-    const contributors = getContributorsFromCharge(requestDetails?.charges || [])
-
-    const totalAmountCollected = useMemo(() => {
+    const contributors = useMemo(() => {
+        return getContributorsFromCharge(requestDetails?.charges || [])
+    }, [requestDetails?.charges])
+
+    const totalAmountCollected = useMemo(() => {
         if (!requestDetails?.charges || !requestDetails?.charges.length) return 0
         return contributors.reduce((acc, curr) => acc + Number(curr.amount), 0)
     }, [contributors])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5e7953 and b3451dc.

📒 Files selected for processing (15)
  • src/app/[...recipient]/client.tsx (1 hunks)
  • src/components/Global/Contributors/ContributorCard.tsx (1 hunks)
  • src/components/Global/Contributors/index.tsx (1 hunks)
  • src/components/Global/ProgressBar/index.tsx (1 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (7 hunks)
  • src/components/Payment/PaymentForm/index.tsx (17 hunks)
  • src/components/Request/link/views/Create.request.link.view.tsx (3 hunks)
  • src/components/TransactionDetails/TransactionCard.tsx (6 hunks)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (6 hunks)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (8 hunks)
  • src/components/TransactionDetails/transactionTransformer.ts (3 hunks)
  • src/components/User/UserCard.tsx (5 hunks)
  • src/hooks/usePaymentInitiator.ts (9 hunks)
  • src/utils/general.utils.ts (3 hunks)
  • src/utils/history.utils.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/[...recipient]/client.tsx
  • src/components/Global/ProgressBar/index.tsx
  • src/components/Global/TokenAmountInput/index.tsx
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 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/User/UserCard.tsx
📚 Learning: 2024-10-04T13:10:49.199Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-04T13:10:49.199Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/User/UserCard.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/Request/link/views/Create.request.link.view.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/TransactionDetails/TransactionDetailsReceipt.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/TransactionDetails/TransactionDetailsReceipt.tsx
🧬 Code graph analysis (10)
src/components/Global/Contributors/ContributorCard.tsx (5)
src/services/services.types.ts (1)
  • Payment (126-148)
src/components/Global/Card/index.tsx (1)
  • CardPosition (4-4)
src/utils/color.utils.ts (1)
  • getColorForUsername (51-69)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-123)
src/utils/general.utils.ts (1)
  • formatTokenAmount (448-469)
src/components/Global/Contributors/index.tsx (3)
src/services/services.types.ts (1)
  • ChargeEntry (50-65)
src/utils/general.utils.ts (1)
  • getContributorsFromCharge (1361-1374)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/hooks/usePaymentInitiator.ts (2)
src/utils/general.utils.ts (1)
  • areEvmAddressesEqual (515-522)
src/constants/zerodev.consts.ts (2)
  • PEANUT_WALLET_TOKEN (20-20)
  • PEANUT_WALLET_CHAIN (18-18)
src/utils/history.utils.ts (3)
src/services/services.types.ts (1)
  • ChargeEntry (50-65)
src/components/Global/DirectSendQR/utils.ts (1)
  • BASE_URL (106-106)
src/constants/general.consts.ts (1)
  • BASE_URL (52-52)
src/components/TransactionDetails/TransactionCard.tsx (1)
src/utils/general.utils.ts (1)
  • formatCurrency (383-385)
src/components/TransactionDetails/transactionTransformer.ts (1)
src/services/services.types.ts (1)
  • ChargeEntry (50-65)
src/components/User/UserCard.tsx (2)
src/utils/color.utils.ts (2)
  • getColorForUsername (51-69)
  • AVATAR_TEXT_DARK (44-44)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-123)
src/utils/general.utils.ts (1)
src/services/services.types.ts (1)
  • ChargeEntry (50-65)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (2)
src/utils/general.utils.ts (2)
  • getContributorsFromCharge (1361-1374)
  • formatCurrency (383-385)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/Payment/PaymentForm/index.tsx (3)
src/utils/general.utils.ts (2)
  • areEvmAddressesEqual (515-522)
  • getContributorsFromCharge (1361-1374)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN (20-20)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
🔇 Additional comments (16)
src/components/Request/link/views/Create.request.link.view.tsx (2)

356-356: LGTM: Navigation update aligns with routing changes.

The navigation target change from '/request' to '/home' is consistent with the broader routing adjustments mentioned in the PR context.


369-370: Excellent UX addition: Info text guides users on open requests.

The addition of showInfoText and infoText props clearly communicates to users that they can leave the amount empty for open requests. This aligns well with the new "request pots" feature where multiple users can contribute.

src/components/TransactionDetails/transactionTransformer.ts (1)

520-523: LGTM; new request‑pot fields mapped with safe defaults.

Non‑breaking addition; totalAmountCollected defaults to 0. No further action.

src/components/Global/Contributors/index.tsx (1)

7-21: Barrel export verified; consider memoizing derived contributors for large lists.

The getContributorsFromCharge function is properly re-exported from @/utils via the wildcard export pattern in src/utils/index.ts, and the component imports correctly from the barrel.

For performance with large contributor lists, consider wrapping the derivation in useMemo:

-const Contributors = ({ charges }: { charges: ChargeEntry[] }) => {
-    const contributors = getContributorsFromCharge(charges)
+const Contributors = ({ charges }: { charges: ChargeEntry[] }) => {
+    const contributors = React.useMemo(() => getContributorsFromCharge(charges), [charges])
src/hooks/usePaymentInitiator.ts (1)

61-62: All callers correctly handle isRequestPotPayment; no actionable concerns found.

The verification confirms proper flag usage across all call sites:

  • PaymentForm (line 440): Deliberately sets isRequestPotPayment = !!requestId and threads it through payload ✓
  • Confirm view (line 269): Intentionally omits the flag because request-pot flows bypass this view entirely (PaymentForm lines 457-459 confirm this)
  • ActionListDaimoPayButton: Uses initiateDaimoPayment, a separate Daimo-specific flow unrelated to request-pot
  • withdraw/crypto page (line 212): Separate withdrawal flow with different payload; request-pot flag not applicable

The short-circuit logic at line 621 (if (payload.isRequestPotPayment || ...)) correctly gates to 'Charge Created' only when the flag is present, and it's only set deliberately in the request-pot flow via PaymentForm. No accidental gating occurs.

src/utils/general.utils.ts (1)

383-385: Defaulting minDecimals to 2 will force trailing zeros across the app—verify this is intentional

The verification confirms your concern is valid. Currently, 14 call sites rely on the default minDecimals value without explicitly specifying it:

  • ProgressBar (3 calls): progress amounts
  • TokenAmountInput, PaymentForm, MantecaDepositShareDetails (5 calls): display values
  • TransactionDetailsReceipt (4 calls): transaction amounts
  • receipt/[entryId]/page.tsx (2 calls): receipt amounts

Changing the default from 2 to 0 would strip trailing zeros from all these locations. Three call sites explicitly pass minDecimals = 0 (TransactionCard.tsx and TransactionDetailsReceipt.tsx:252), so they wouldn't be affected by the default change.

Before proceeding with the proposed change, verify whether the current behavior (with trailing zeros) is intentional across all these call sites or if they should be updated to display whole numbers.

src/components/Payment/PaymentForm/index.tsx (10)

25-25: LGTM!

The new imports are properly added and all are used within the component for the request pot payment feature.

Also applies to: 38-39


54-55: LGTM!

The new isRequestPotPayment property is properly typed and integrated into the component's props interface.


499-501: LGTM!

The button text appropriately changes to "Pay" for request pot payment flows.


531-537: LGTM!

Button icons are appropriately disabled for request pot payment flows, keeping the UI clean and consistent with the simplified "Pay" button text.


668-680: LGTM!

The UserCard props are correctly updated for request pot payment flows. The Number(amount) conversion on line 677 appropriately transforms the string amount to a numeric value for the component.


693-704: LGTM!

The TokenAmountInput is appropriately configured for request pot payments, allowing users to select contribution amounts with a slider interface when a target amount is set.


741-754: LGTM!

The button rendering condition correctly prioritizes request pot payments, allowing the payment button to display regardless of wallet connection status, which aligns with the request pot payment flow requirements.


785-796: LGTM!

The Contributors section is cleanly implemented with proper conditional rendering, unique keys, and appropriate card positioning for the request pot payment feature.


216-273: Balance check logic is intentional and correct.

The apparent asymmetry between peanut wallet and external wallet balance checks is by design. Request pot payments are handled differently in the payment flow:

  • In usePaymentInitiator.ts (line ~315), request pot payments return early with "Charge Created" status and never execute a transaction
  • Therefore, balance validation is unnecessary and correctly skipped in both peanut and external wallet paths
  • The button enablement logic (line 321) confirms request pot payments don't require wallet connection, only recipient, amount, and token selection

The code is working as intended.


454-464: Verify payment flow continuation for request pot payments.

When initiatePayment returns "Charge Created" status for request pot payments, the CONFIRM view is skipped (line 457). Ensure the payment flow transitions to an appropriate state/view, otherwise the user may be left in an undefined UI state.

Run the following script to verify how usePaymentInitiator handles request pot payments:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
src/components/Payment/PaymentForm/index.tsx (2)

326-334: Add missing dependency to useMemo.

The canInitiatePayment useMemo is missing isRequestPotPayment in its dependency array, which could lead to stale values when this prop changes.

Apply this diff:

     return recipientExists && amountIsSet && tokenSelected && walletConnected
 }, [
     recipient,
     inputTokenAmount,
     usdValue,
     selectedTokenAddress,
     selectedChainID,
     isConnected,
     isActivePeanutWallet,
+    isRequestPotPayment,
 ])

457-476: Add missing dependency to useCallback.

The handleInitiatePayment callback is missing isRequestPotPayment in its dependency array at line 457-476.

Apply this diff:

     }
 }, [
     fulfillUsingManteca,
     canInitiatePayment,
     isDepositRequest,
     isConnected,
     openReownModal,
     recipient,
     inputTokenAmount,
     requestId,
     initiatePayment,
     chargeDetails,
     isExternalWalletFlow,
     requestDetails,
     selectedTokenAddress,
     selectedChainID,
     inputUsdValue,
     requestedTokenPrice,
     inviteError,
     handleAcceptInvite,
+    isRequestPotPayment,
+    dispatch,
+    router,
+    user,
 ])
♻️ Duplicate comments (3)
src/components/Request/link/views/Create.request.link.view.tsx (1)

387-391: Optional: Consider Number.isFinite for semantic clarity.

While the current implementation works correctly (NaN is falsy, so invalid inputs show "open request"), using Number.isFinite() would make the intent more explicit:

const numValue = Number(tokenValue)
{!tokenValue || !Number.isFinite(numValue) || numValue === 0
    ? 'Share open request'
    : `Share $${tokenValue} request`}

This avoids multiple parseFloat calls and clearly expresses "only show dollar amount for finite, non-zero numbers."

src/components/Global/Contributors/ContributorCard.tsx (1)

9-16: Add haveSentMoneyToUser to Contributor type and wire through.

The Contributor type is missing the haveSentMoneyToUser field, and VerifiedUserLabel at line 33-37 doesn't pass this prop, defaulting all contributors to false. This prevents the double-check badge from appearing for verified users the viewer has sent money to previously.

To fix:

  1. Add haveSentMoneyToUser: boolean to the Contributor type (lines 9-16)
  2. Update getContributorsFromCharge in src/utils/general.utils.ts to include this field (requires access to user interactions data)
  3. Pass haveSentMoneyToUser={contributor.haveSentMoneyToUser} at line 37

If interaction data isn't available in the contributors context, consider whether this badge is necessary for pot contributors, or fetch the data where contributors are rendered.

Also applies to: 33-37

src/utils/general.utils.ts (1)

1350-1367: Critical: Filter charges by completed fulfillment.

The function maps all charges without checking if fulfillmentPayment exists. Charges without completed fulfillment should not be included as contributors since the payment hasn't actually been processed.

Apply this diff to filter valid contributors:

 export const getContributorsFromCharge = (charges: ChargeEntry[]) => {
-    return charges.map((charge) => {
+    return charges
+        .filter((charge) => !!charge.fulfillmentPayment)
+        .map((charge) => {
         const successfulPayment = charge.payments.at(-1)
         let username = successfulPayment?.payerAccount?.user?.username
         if (successfulPayment?.payerAccount?.type === 'evm-address') {
             username = successfulPayment.payerAccount.identifier
         }

         return {
             uuid: charge.uuid,
             payments: charge.payments,
             amount: charge.tokenAmount,
             username,
             fulfillmentPayment: charge.fulfillmentPayment,
             isUserVerified: successfulPayment?.payerAccount?.user?.bridgeKycStatus === 'approved',
         }
     })
 }

This ensures only charges with completed fulfillment payments are treated as contributions.

🧹 Nitpick comments (3)
src/components/User/UserCard.tsx (1)

63-68: Optional: More explicit checks in getAddressLinkTitle.

While functionally correct, the logic could be more explicit:

const getAddressLinkTitle = () => {
    if (isRequestPot && typeof amount === 'number' && amount > 0) return `$${amount}`
    if (isRequestPot && (amount === undefined || amount === 0)) return `Pay what you want`
    return username
}

This makes it clearer that both 0 and undefined mean "open request" (no goal), though the current implementation works correctly.

src/components/Payment/PaymentForm/index.tsx (2)

320-323: Remove redundant condition check.

The return statement includes && isRequestPotPayment which is redundant since it's already verified by the if statement on line 321.

Apply this diff:

     // If its requestPotPayment, we only need to check if the recipient exists, amount is set, and token is selected
     if (isRequestPotPayment) {
-        return recipientExists && amountIsSet && tokenSelected && isRequestPotPayment
+        return recipientExists && amountIsSet && tokenSelected
     }

622-624: Memoize contributor data derivation.

The contributors array and totalAmountCollected are recalculated on every render. Consider memoizing these values for better performance.

Apply this diff:

-    const contributors = getContributorsFromCharge(requestDetails?.charges || [])
-
-    const totalAmountCollected = requestDetails?.totalCollectedAmount ?? 0
+    const contributors = useMemo(
+        () => getContributorsFromCharge(requestDetails?.charges || []),
+        [requestDetails?.charges]
+    )
+
+    const totalAmountCollected = useMemo(
+        () => requestDetails?.totalCollectedAmount ?? 0,
+        [requestDetails?.totalCollectedAmount]
+    )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92facb2 and 6468c92.

📒 Files selected for processing (12)
  • src/app/[...recipient]/client.tsx (1 hunks)
  • src/components/Global/Contributors/ContributorCard.tsx (1 hunks)
  • src/components/Global/ProgressBar/index.tsx (1 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (8 hunks)
  • src/components/Payment/PaymentForm/index.tsx (18 hunks)
  • src/components/Request/link/views/Create.request.link.view.tsx (3 hunks)
  • src/components/TransactionDetails/transactionTransformer.ts (4 hunks)
  • src/components/User/UserCard.tsx (5 hunks)
  • src/hooks/usePaymentInitiator.ts (9 hunks)
  • src/services/requests.ts (3 hunks)
  • src/services/services.types.ts (1 hunks)
  • src/utils/general.utils.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/services/services.types.ts
  • src/components/Global/TokenAmountInput/index.tsx
  • src/components/TransactionDetails/transactionTransformer.ts
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2024-10-04T13:10:49.199Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-04T13:10:49.199Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.tsx
  • src/components/User/UserCard.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/Request/link/views/Create.request.link.view.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay` components, the `tokenPrice` property in the `IPayScreenProps` interface is only relevant to these views. Other components using `IPayScreenProps` do not need to handle `tokenPriceData` when it's updated in these components.

Applied to files:

  • src/app/[...recipient]/client.tsx
🧬 Code graph analysis (7)
src/services/requests.ts (4)
src/services/services.types.ts (2)
  • CreateRequestRequest (12-22)
  • TRequestResponse (24-49)
src/utils/sentry.utils.ts (1)
  • fetchWithSentry (36-150)
src/constants/general.consts.ts (1)
  • PEANUT_API_URL (43-47)
src/utils/general.utils.ts (1)
  • jsonStringify (74-84)
src/utils/general.utils.ts (1)
src/services/services.types.ts (1)
  • ChargeEntry (51-66)
src/hooks/usePaymentInitiator.ts (2)
src/utils/general.utils.ts (1)
  • areEvmAddressesEqual (515-522)
src/constants/zerodev.consts.ts (2)
  • PEANUT_WALLET_TOKEN (20-20)
  • PEANUT_WALLET_CHAIN (18-18)
src/components/Global/Contributors/ContributorCard.tsx (5)
src/services/services.types.ts (1)
  • Payment (127-149)
src/components/Global/Card/index.tsx (1)
  • CardPosition (4-4)
src/utils/color.utils.ts (1)
  • getColorForUsername (51-69)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-123)
src/utils/general.utils.ts (1)
  • formatTokenAmount (448-469)
src/components/Global/ProgressBar/index.tsx (1)
src/utils/general.utils.ts (1)
  • formatCurrency (383-385)
src/components/User/UserCard.tsx (2)
src/utils/color.utils.ts (2)
  • getColorForUsername (51-69)
  • AVATAR_TEXT_DARK (44-44)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-123)
src/components/Payment/PaymentForm/index.tsx (2)
src/utils/general.utils.ts (2)
  • areEvmAddressesEqual (515-522)
  • getContributorsFromCharge (1350-1367)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
⏰ 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 (20)
src/components/Request/link/views/Create.request.link.view.tsx (2)

356-356: LGTM: Navigation update aligns with request-pots workflow.

The change from /request to /home is appropriate for the new request-pots feature flow.


369-370: Good UX: Informational text guides users on open requests.

The new props clearly communicate that leaving the amount empty creates an open request where payers can choose their own amount.

src/components/Global/ProgressBar/index.tsx (3)

12-20: LGTM: Safe percentage calculations with proper guards.

The calculations correctly guard against division by zero and clamp all percentages to valid ranges [0, 100]. This resolves the past critical review concern.


22-22: Note: Local formatCurrency is fit for purpose.

While there's an enhanced formatCurrency in utils, this simpler local version with fixed 2 decimals is appropriate for the component's needs and avoids unnecessary coupling.


49-51: LGTM: Remaining amount correctly clamped at zero.

The Math.max(goal - progress, 0) prevents displaying negative currency when progress exceeds the goal, as noted in the addressed past review.

src/app/[...recipient]/client.tsx (2)

524-524: LGTM: isRequestPotPayment flag correctly derived.

The boolean flag !!requestId appropriately signals request-pot payment flows to InitialPaymentView, enabling the component to adjust its behavior accordingly.


527-536: LGTM: ActionList correctly hidden for request-pot flows.

Suppressing the ActionList when requestId is present makes sense—the payment is targeted to a specific request pot, so generic actions are not applicable.

src/utils/general.utils.ts (1)

383-385: LGTM: formatCurrency signature enhanced with decimal controls.

Adding maxDecimals and minDecimals parameters with sensible defaults (2, 2) provides flexibility while maintaining backward compatibility.

src/hooks/usePaymentInitiator.ts (3)

28-28: LGTM: useRouter correctly used for param updates.

As discussed in the past review, router.replace is appropriate here because components in client.tsx consume params via useSearchParams() in useEffect dependencies and need reactive updates.

Based on learnings

Also applies to: 82-82


61-61: LGTM: isRequestPotPayment flag added to payload.

The new optional field enables the hook to distinguish and handle request-pot payment flows appropriately.


625-631: LGTM: Request pot payment short-circuits correctly.

The condition at line 625 appropriately returns after charge creation for request-pot payments, allowing the UI to collect contributions before payment execution. The logic flow is sound.

src/services/requests.ts (3)

8-11: LGTM: Authentication validation added to create() and update().

Both methods now validate the JWT token presence before making API calls, addressing the past review concerns. The consistent error message "Authentication token not found. Please log in again." provides clear user feedback.

Also applies to: 41-44


12-18: LGTM: Cleaner JSON payloads replace FormData.

The switch to JSON with Content-Type: application/json and jsonStringify (which handles BigInt serialization) is cleaner and more consistent with modern REST API patterns.

Also applies to: 45-51


90-105: LGTM: close() method properly implemented.

The new close() method follows the established pattern with:

  • Auth token validation
  • DELETE request to the correct endpoint
  • Proper error handling
  • Typed return value

This enables the request-pot closing functionality in the UI.

src/components/User/UserCard.tsx (3)

23-25: LGTM: New props support request-pot features.

The optional amount, amountCollected, and isRequestPot props appropriately extend the component for request-pot payment tracking.


101-107: LGTM: username prop bug fixed.

The component now correctly passes username={username} (the variable) instead of the literal string 'username', resolving the past critical issue.


112-114: LGTM: ProgressBar rendering with proper guards.

The explicit !== undefined checks and amount > 0 guard ensure:

  1. Progress bar only renders for request-pot payments with defined amounts
  2. Zero amounts (open requests) don't show progress
  3. Type-safe props passed to ProgressBar after guards

This correctly addresses the past review concern about falsy checks.

src/components/Payment/PaymentForm/index.tsx (3)

730-743: Verify button visibility logic for request pot payments.

The button is shown for request pot payments regardless of wallet connection status (isRequestPotPayment ||). This differs from the normal payment flow which requires wallet connection. Confirm this is the intended UX behavior.

If wallet connection is required later in the flow, consider updating the button text or adding a visual indicator that connection will be needed. Otherwise, approve this design decision.


774-785: LGTM! Contributors section implementation is clean.

The Contributors section correctly uses the getCardPosition utility and maps through contributors with proper key management. The conditional rendering ensures it only appears for request pot payments.


216-220: Balance check bypass for request pot is intentional and correct; no issue found.

For request pot payments, the balance check is appropriately skipped because request pot flows do not execute transactions on behalf of the initiator. The flow only creates a charge record and returns immediately without transitioning to payment confirmation or execution. Actual contributors will be balance-checked when they later choose to contribute to the pot through the normal payment flow.

@Zishan-7 Zishan-7 marked this pull request as ready for review October 19, 2025 15:54
@notion-workspace
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

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

307-334: Add isRequestPotPayment to dependency array.

The canInitiatePayment computation uses isRequestPotPayment at lines 321-322 but it's missing from the dependency array at line 334. This causes the memoized value to use a stale isRequestPotPayment when the prop changes.

Apply this diff:

     }, [
         recipient,
         inputTokenAmount,
         usdValue,
         selectedTokenAddress,
         selectedChainID,
         isConnected,
         isActivePeanutWallet,
+        isRequestPotPayment,
     ])
🧹 Nitpick comments (2)
src/components/Payment/PaymentForm/index.tsx (2)

623-623: Memoize contributors calculation.

getContributorsFromCharge is called on every render, which maps over the charges array. Wrap this in useMemo to avoid unnecessary recalculations.

Apply this diff:

-    const contributors = getContributorsFromCharge(requestDetails?.charges || [])
+    const contributors = useMemo(
+        () => getContributorsFromCharge(requestDetails?.charges || []),
+        [requestDetails?.charges]
+    )

775-786: Consider hiding contributors section when empty.

The contributors section renders even when the array is empty, displaying "Contributors (0)" with no cards. Consider hiding the entire section when contributors.length === 0 for a cleaner UI.

Apply this diff:

-            {isRequestPotPayment && (
+            {isRequestPotPayment && contributors.length > 0 && (
                 <div>
                     <h2 className="mb-4 text-base font-bold text-black">Contributors ({contributors.length})</h2>
                     {contributors.map((contributor, index) => (
                         <ContributorCard
                             position={getCardPosition(index, contributors.length)}
                             key={contributor.uuid}
                             contributor={contributor}
                         />
                     ))}
                 </div>
             )}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6468c92 and 07149b8.

📒 Files selected for processing (1)
  • src/components/Payment/PaymentForm/index.tsx (19 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 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/Payment/PaymentForm/index.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/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (1)
src/components/Payment/PaymentForm/index.tsx (3)
src/utils/general.utils.ts (2)
  • areEvmAddressesEqual (515-522)
  • getContributorsFromCharge (1350-1367)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN (20-20)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
⏰ 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

@notion-workspace
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/components/Payment/PaymentForm/index.tsx (1)

320-324: Fix minor typo in comment.

"It's" (contraction) instead of "its".

-        // If its requestPotPayment, we only need to check if the recipient exists, amount is set, and token is selected
+        // If it's requestPotPayment, we only need to check if the recipient exists, amount is set, and token is selected
🧹 Nitpick comments (2)
src/hooks/usePaymentInitiator.ts (1)

164-167: Use router.replace for same-path query updates to avoid history bloat.

router.push adds a history entry each time chargeId toggles, impacting back-button UX. router.replace updates search params reactively without polluting history.

Apply:

- router.push(newUrl.pathname + newUrl.search)
+ router.replace(newUrl.pathname + newUrl.search)

Repeat at all three locations. No other logic changes required. -->

Also applies to: 399-402, 680-683

src/components/Payment/PaymentForm/index.tsx (1)

688-692: Parse amount once to avoid repeated Number(...) and potential NaN.

Small cleanup: compute a sanitized numeric once and reuse for disabled/showSlider/maxAmount.

- disabled={
-   !showRequestPotInitialView &&
-   !isExternalWalletFlow &&
-   (!!requestDetails?.tokenAmount || !!chargeDetails?.tokenAmount)
- }
+ // Precompute above render:
+ // const reqAmountNum = showRequestPotInitialView && amount ? Number(amount) : undefined
+ disabled={
+   !showRequestPotInitialView &&
+   !isExternalWalletFlow &&
+   (!!requestDetails?.tokenAmount || !!chargeDetails?.tokenAmount)
+ }
...
- showSlider={showRequestPotInitialView && amount ? Number(amount) > 0 : false}
- maxAmount={showRequestPotInitialView && amount ? Number(amount) : undefined}
+ showSlider={showRequestPotInitialView && (reqAmountNum ?? 0) > 0}
+ maxAmount={showRequestPotInitialView ? reqAmountNum : undefined}

Also applies to: 697-699

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07149b8 and f9dd883.

📒 Files selected for processing (3)
  • src/app/[...recipient]/client.tsx (1 hunks)
  • src/components/Payment/PaymentForm/index.tsx (19 hunks)
  • src/hooks/usePaymentInitiator.ts (9 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is used instead of `router.replace` in URL manipulation functions when immediate/synchronous URL updates are required for subsequent actions to read the updated URL parameters.

Applied to files:

  • src/hooks/usePaymentInitiator.ts
📚 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/Payment/PaymentForm/index.tsx
📚 Learning: 2024-10-03T09:57:43.885Z
Learnt from: jjramirezn
PR: peanutprotocol/peanut-ui#404
File: src/context/tokenSelector.context.tsx:121-121
Timestamp: 2024-10-03T09:57:43.885Z
Learning: In `TokenContextProvider` within `tokenSelector.context.tsx`, when token data is loaded from preferences, it's acceptable to set `isTokenPriceFetchingComplete` to `true` because the token data is already available.

Applied to files:

  • src/components/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (2)
src/hooks/usePaymentInitiator.ts (2)
src/utils/general.utils.ts (1)
  • areEvmAddressesEqual (515-522)
src/constants/zerodev.consts.ts (2)
  • PEANUT_WALLET_TOKEN (20-20)
  • PEANUT_WALLET_CHAIN (18-18)
src/components/Payment/PaymentForm/index.tsx (3)
src/utils/general.utils.ts (2)
  • areEvmAddressesEqual (515-522)
  • getContributorsFromCharge (1350-1367)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN (20-20)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
⏰ 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 (11)
src/app/[...recipient]/client.tsx (2)

524-525: Prop plumbed correctly for request-pot initial view.

Passing showRequestPotInitialView={!!requestId} is clear and stable.


527-536: Good: hide ActionList when a requestId is present.

Prevents conflicting actions in request-context routes.

src/hooks/usePaymentInitiator.ts (1)

61-62: New payload flag is clear and narrowly scoped.

returnAfterChargeCreation?: boolean reads well for the pot initial view short‑circuit. Consider documenting its caller expectations in the JSDoc.

src/components/Payment/PaymentForm/index.tsx (8)

25-25: Contributors plumbing looks good.

Imports, memo-less derivation from requestDetails?.charges || [], and render block are straightforward and null-safe.

Also applies to: 38-40, 626-629, 781-793


54-55: API surface addition is consistent.

showRequestPotInitialView flows correctly from parent into PaymentForm props.

Also applies to: 71-72


216-220: Correctly skip balance checks during request‑pot initial view.

Prevents false “insufficient” errors before wallet wiring. Dependency list updated accordingly.

Also applies to: 272-273


367-386: Invite/connect/sign-in gates correctly bypassed for pot initial view.

Keeps the flow focused on charge creation first; good UX.


442-443: Charge creation short‑circuit is aligned end‑to‑end.

Payload sets returnAfterChargeCreation; view only advances to CONFIRM when not in pot initial view.

Also applies to: 452-455


495-498: Button label/icon adaptations are sane for pot initial view.

Clear “Pay” CTA and suppression of wallet/balance icons when not relevant.

Also applies to: 527-534


664-675: Verify UserCard type/props for 'request_pay'.

Ensure UserCard accepts type="request_pay" and optional amount/amountCollected/isRequestPot without layout regressions.


636-639: Hiding NavHeader in pot initial view is sensible.

Reduces chrome to emphasize the pot context.

@Zishan-7 Zishan-7 requested a review from Hugo0 October 19, 2025 18:27
Copy link
Contributor

@jjramirezn jjramirezn left a comment

Choose a reason for hiding this comment

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

Lets goooo

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
src/components/TransactionDetails/TransactionCard.tsx (1)

87-94: Fix bigint crash in amount formatting

usdAmount can be bigint; Math.abs(bigint) throws. Convert safely and avoid toString on undefined totals.

-    let usdAmount = amount
-    if (!isStableCoin(transaction.tokenSymbol ?? 'USDC')) {
-        usdAmount = Number(transaction.currency?.amount ?? amount)
-    }
-
-    const formattedAmount = formatCurrency(Math.abs(usdAmount).toString(), 2, 0)
-    const formattedTotalAmountCollected = formatCurrency(transaction.totalAmountCollected.toString(), 2, 0)
+    let usdAmount = amount
+    if (!isStableCoin(transaction.tokenSymbol ?? 'USDC')) {
+        usdAmount = Number(transaction.currency?.amount ?? amount)
+    }
+    const numericUsd = typeof usdAmount === 'bigint' ? Number(usdAmount) : (usdAmount as number)
+    const formattedAmount = formatCurrency(Math.abs(numericUsd).toString(), 2, 0)
+    const formattedTotalAmountCollected = formatCurrency(String(transaction.totalAmountCollected ?? 0), 2, 0)
src/components/Payment/PaymentForm/index.tsx (1)

341-349: Add showRequestPotInitialView to dependency array.

The canInitiatePayment memoized value uses showRequestPotInitialView at line 336 but it's missing from the dependency array (lines 341-349). This can cause the computed value to reference a stale value when showRequestPotInitialView changes.

Apply this diff:

     }, [
         recipient,
         inputTokenAmount,
         usdValue,
         selectedTokenAddress,
         selectedChainID,
         isConnected,
         isActivePeanutWallet,
+        showRequestPotInitialView,
     ])
♻️ Duplicate comments (3)
src/app/[...recipient]/client.tsx (1)

527-536: Guard downstream UI when requestId is present

The ActionList gating looks right. Ensure other effects and loaders above also short-circuit when a request pot is driving the view to avoid unnecessary churn. This was raised previously for this file; re-check after these changes.

src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

504-509: Pass raw numbers to ProgressBar and fix prop name

formattedTotalAmountCollected is a formatted string; Number('1,234.56') → NaN. Also the header prop is misspelled.

-                showProgessBar={transaction.isRequestPotLink}
-                goal={Number(transaction.amount)}
-                progress={Number(formattedTotalAmountCollected)}
+                showProgressBar={transaction.isRequestPotLink}
+                goal={Number(transaction.amount)}
+                progress={Number(transaction.totalAmountCollected ?? 0)}
src/components/Payment/PaymentForm/index.tsx (1)

335-335: Fix typo in comment.

"its" should be "it's" (contraction of "it is").

🧹 Nitpick comments (8)
src/services/services.types.ts (1)

48-48: Confirm API shape for totalCollectedAmount and document units

Every other amount here is a string; this is a number. Ensure backend returns a number (not a string) and clarify whether it’s USD or token units. If API returns a string, align the type and convert at the transformer boundary.

src/app/[...recipient]/client.tsx (1)

524-525: Small readability tweak

Extract a const to avoid repeating coercions:

-                        showRequestPotInitialView={!!requestId}
+                        {(() => { const hasRequestId = Boolean(requestId); return (
+                          <InitialPaymentView
+                            ...
+                            showRequestPotInitialView={hasRequestId}
+                          />
+                        )})()}
src/components/TransactionDetails/TransactionCard.tsx (1)

116-133: Avoid duplicate StatusPill rendering

StatusPill is shown beside the avatar and again in the action row; choose one location to prevent double badges.

-                                {status && <StatusPill status={status} />}
...
-                                {status && <StatusPill status={status} />}
+                                {/* keep only one StatusPill (e.g., in action row) */}

Also applies to: 160-163

src/utils/history.utils.ts (1)

228-235: Harden request link construction

Encode path segments to avoid malformed URLs and standardize symbol case if the parser expects uppercase.

-                link = `${BASE_URL}/${entry.recipientAccount.username || entry.recipientAccount.identifier}/${tokenAmount}${tokenCurrency}?id=${entry.uuid}`
+                const name = encodeURIComponent(entry.recipientAccount.username || entry.recipientAccount.identifier)
+                const symbol = (entry.tokenSymbol || '').toUpperCase()
+                link = `${BASE_URL}/${name}/${tokenAmount}${symbol}?id=${encodeURIComponent(entry.uuid)}`
src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (2)

43-48: Rename prop to showProgressBar (spelling) and align usages

Typo leaks into the public API. Rename now to avoid spreading the misspelling.

-    showProgessBar?: boolean
+    showProgressBar?: boolean
...
-    showProgessBar = false,
+    showProgressBar = false,
...
-            {!isNoGoalSet && showProgessBar && goal !== undefined && progress !== undefined && (
+            {!isNoGoalSet && showProgressBar && goal !== undefined && progress !== undefined && (

Follow up: update all call sites (e.g., TransactionDetailsReceipt).

Also applies to: 179-184, 258-262


47-48: Make isTransactionClosed optional with a default

Avoid breaking future call sites; default to false.

-    isTransactionClosed: boolean
+    isTransactionClosed?: boolean

And use: const closed = Boolean(isTransactionClosed) when passing to ProgressBar.

src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

1407-1420: Contributors list: guard large lists

If contributors can be large, consider simple windowing (autoSizer + virtual list) or a stricter max-height with “View all” to avoid tall drawers and layout jank.

src/components/Payment/PaymentForm/index.tsx (1)

336-338: Remove redundant condition in return statement.

The condition && showRequestPotInitialView at line 337 is redundant since it's already inside the if (showRequestPotInitialView) block. This creates unnecessary verbosity.

Apply this diff:

         if (showRequestPotInitialView) {
-            return recipientExists && amountIsSet && tokenSelected && showRequestPotInitialView
+            return recipientExists && amountIsSet && tokenSelected
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9dd883 and fcfefbb.

📒 Files selected for processing (12)
  • src/app/(mobile-ui)/qr-pay/page.tsx (2 hunks)
  • src/app/[...recipient]/client.tsx (1 hunks)
  • src/components/Global/TokenAmountInput/index.tsx (7 hunks)
  • src/components/Kyc/KycStatusItem.tsx (1 hunks)
  • src/components/Payment/PaymentForm/index.tsx (19 hunks)
  • src/components/TransactionDetails/TransactionCard.tsx (4 hunks)
  • src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (5 hunks)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (12 hunks)
  • src/components/TransactionDetails/transactionTransformer.ts (4 hunks)
  • src/services/services.types.ts (1 hunks)
  • src/utils/general.utils.ts (3 hunks)
  • src/utils/history.utils.ts (5 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/Kyc/KycStatusItem.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/general.utils.ts
🧰 Additional context used
🧠 Learnings (6)
📚 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/Global/TokenAmountInput/index.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/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/components/Global/TokenAmountInput/index.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/TransactionDetails/TransactionDetailsReceipt.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/TransactionDetails/TransactionDetailsReceipt.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/Payment/PaymentForm/index.tsx
🧬 Code graph analysis (7)
src/utils/history.utils.ts (2)
src/services/services.types.ts (1)
  • ChargeEntry (51-66)
src/constants/general.consts.ts (1)
  • BASE_URL (52-52)
src/components/Global/TokenAmountInput/index.tsx (1)
src/components/Global/Slider/index.tsx (1)
  • Slider (110-110)
src/components/TransactionDetails/TransactionCard.tsx (2)
src/utils/general.utils.ts (1)
  • formatCurrency (381-383)
src/components/TransactionDetails/PerkIcon.tsx (1)
  • PerkIcon (30-40)
src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx (2)
src/components/Global/Icons/Icon.tsx (1)
  • Icon (206-215)
src/components/UserHeader/index.tsx (1)
  • VerifiedUserLabel (45-123)
src/components/TransactionDetails/transactionTransformer.ts (1)
src/services/services.types.ts (1)
  • ChargeEntry (51-66)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (6)
src/utils/general.utils.ts (3)
  • getContributorsFromCharge (1362-1379)
  • formatCurrency (381-383)
  • formatDate (933-945)
src/services/requests.ts (1)
  • requestsApi (6-106)
src/services/charges.ts (1)
  • chargesApi (11-108)
src/constants/query.consts.ts (1)
  • TRANSACTIONS (2-2)
src/components/Payment/PaymentInfoRow.tsx (1)
  • PaymentInfoRow (18-87)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
src/components/Payment/PaymentForm/index.tsx (3)
src/utils/general.utils.ts (2)
  • areEvmAddressesEqual (513-520)
  • getContributorsFromCharge (1362-1379)
src/constants/zerodev.consts.ts (1)
  • PEANUT_WALLET_TOKEN (20-20)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
⏰ 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 (11)
src/components/Global/TokenAmountInput/index.tsx (2)

218-223: LGTM — Slider position calculation correctly converts to USD.

The sliderValue calculation properly converts tokenValue to USD before computing the percentage, ensuring the slider position is accurate across different display modes and input units. This addresses the slider position concern raised in the previous review.


313-323: LGTM — Info text and slider UI integrated cleanly.

The conditional rendering for both the info text pill and the slider component is properly guarded, and the wiring to onSliderValueChange and sliderValue follows the expected pattern. The UI elements are well-structured.

src/utils/history.utils.ts (1)

75-87: Ensure UI supports ‘closed’ status end-to-end

You added CLOSED and treat it final. Confirm StatusBadge/StatusPill understands 'closed' and that any filters/search rely on FINAL_STATES include it.

src/components/TransactionDetails/transactionTransformer.ts (2)

417-421: Status mapping for CLOSED looks good; verify badge support

UI shows 'closed' unless collected is 0 → 'cancelled'. Confirm StatusBadge supports 'closed' so header/card render correctly. If not, map to 'cancelled' until badge is extended.


133-136: Nice: pot fields plumbed through

isRequestPotLink, requestPotPayments, and totalAmountCollected are correctly exposed with a safe default.

Also applies to: 549-552

src/components/Payment/PaymentForm/index.tsx (6)

25-25: LGTM! New imports and prop properly defined.

The new imports (getContributorsFromCharge, ContributorCard, getCardPosition) and the showRequestPotInitialView prop are correctly defined and used throughout the component to support the request pot initial view feature.

Also applies to: 38-39, 54-54, 71-71


229-233: LGTM! Balance check bypass for initial view is appropriate.

The balance check bypass when showRequestPotInitialView is true makes sense, as users in the initial view haven't connected their wallets yet. The dependency is correctly included in the useEffect array.

Also applies to: 285-285


382-403: LGTM! Request pot initial view flow handling is consistent.

The modifications to handleInitiatePayment properly bypass wallet connection, sign-in prompts, and invite handling when showRequestPotInitialView is true. The returnAfterChargeCreation payload flag and view navigation logic are correctly adjusted. The dependency is properly included in the useCallback array.

Also applies to: 457-457, 467-469, 494-494


510-512: LGTM! Button text and icon logic properly adapted for initial view.

The button text ('Pay') and icon logic correctly handle the request pot initial view case, providing appropriate UI feedback without wallet-specific indicators.

Also applies to: 542-548


641-643: LGTM! Safe data extraction with proper fallbacks.

The contributors and total amount collected are safely extracted with appropriate fallback values ([] and 0).


651-653: LGTM! UI rendering properly adapted for request pot initial view.

The UI changes are well-integrated:

  • NavHeader correctly hidden in initial view
  • UserCard type switches to request_pay with appropriate props
  • Amount conversions use safe checks (amount ? Number(amount) : ...) to prevent NaN
  • Token input and slider logic properly configured for the initial view flow
  • Contributors section renders with correct card positioning

Also applies to: 679-679, 687-689, 703-707, 712-713, 751-752, 796-807

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
src/components/Request/link/views/Create.request.link.view.tsx (2)

303-318: Refine validation to handle NaN explicitly.

The guard at line 304 uses parseFloat(tokenValue) <= 0, which doesn't catch NaN (since NaN <= 0 is false). Invalid numeric input would bypass the early return and proceed to the update logic.

Apply this diff for explicit NaN handling:

 const handleTokenAmountSubmit = useCallback(async () => {
-    if (!tokenValue || parseFloat(tokenValue) <= 0) return
+    const amount = parseFloat(tokenValue)
+    if (!tokenValue || !Number.isFinite(amount) || amount <= 0) return
     if (isCreatingLink || isUpdatingRequest) return // Prevent duplicate calls

333-338: Replace useMemo with useEffect for side effects.

Using useMemo to perform side effects (setting state) violates React's rules of hooks. useMemo is for computing derived values and should return a value, not execute side effects.

Apply this diff:

 // Set wallet defaults when connected
-useMemo(() => {
+useEffect(() => {
     if (isConnected && address) {
         setSelectedChainID(PEANUT_WALLET_CHAIN.id.toString())
         setSelectedTokenAddress(PEANUT_WALLET_TOKEN)
     }
 }, [isConnected, address, setSelectedChainID, setSelectedTokenAddress])
♻️ Duplicate comments (1)
src/components/Request/link/views/Create.request.link.view.tsx (1)

373-377: LGTM: Safe dynamic button text with proper NaN handling.

The button text logic correctly implements the suggestion from the previous review. The triple check (!tokenValue || !parseFloat(tokenValue) || parseFloat(tokenValue) === 0) safely handles empty strings, NaN, and zero values.

🧹 Nitpick comments (1)
src/components/Request/link/views/Create.request.link.view.tsx (1)

320-330: Remove unused dependencies from generateLink.

The qrCodeLink and tokenValue variables appear in the dependency array but are not used within the generateLink function body. This causes unnecessary callback re-creation when these values change.

Apply this diff:

     return link || ''
-}, [generatedLink, qrCodeLink, tokenValue, attachmentOptions, createRequestLink, isCreatingLink, isUpdatingRequest])
+}, [generatedLink, attachmentOptions, createRequestLink, isCreatingLink, isUpdatingRequest])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fcfefbb and 5ccac6e.

📒 Files selected for processing (1)
  • src/components/Request/link/views/Create.request.link.view.tsx (7 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.tsx
🧬 Code graph analysis (1)
src/components/Request/link/views/Create.request.link.view.tsx (2)
src/redux/slices/send-flow-slice.ts (2)
  • setTokenValue (47-49)
  • setAttachmentOptions (86-88)
src/redux/types/send-flow.types.ts (1)
  • IAttachmentOptions (9-13)
⏰ 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/Request/link/views/Create.request.link.view.tsx (3)

259-273: LGTM: Simplified debounced update flow.

The removal of the "create-on-change" path correctly centralizes request creation in the generateLink function (triggered by the Share button), while this handler now focuses solely on updating existing requests when attachments change.


342-342: LGTM: Navigation updated to home route.

The back button now navigates to /home instead of /request, which aligns with the updated user flow.


355-356: LGTM: Helpful info text for open requests.

Always showing the info text "Leave empty to let payers choose amounts." effectively communicates the open request feature to users.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

510-515: Fix ProgressBar props: prop typo + NaN risk.

  • Rename prop to showProgressBar (if that’s the HeaderCard API).
  • Pass raw numeric collected amount, not a formatted string.
-                showProgessBar={transaction.isRequestPotLink}
-                goal={Number(transaction.amount)}
-                progress={Number(formattedTotalAmountCollected)}
+                showProgressBar={transaction.isRequestPotLink}
+                goal={Number(transaction.amount ?? 0)}
+                progress={Number(transaction.totalAmountCollected ?? 0)}
                 isRequestPotTransaction={transaction.isRequestPotLink}
                 isTransactionClosed={transaction.status === 'closed'}

Run to verify the expected prop name on TransactionDetailsHeaderCard:

#!/bin/bash
# Verify prop name in header component
rg -n -C2 'showProgre?s?sBar' src/components/TransactionDetails/TransactionDetailsHeaderCard.tsx
🧹 Nitpick comments (3)
src/components/Request/link/views/Create.request.link.view.tsx (2)

374-378: Simplify and optimize the button text logic.

The current condition chains multiple parseFloat calls and includes redundant checks. While functionally acceptable, it's inefficient and harder to read.

Apply this diff to use a single parse with clearer logic:

-                    <ShareButton generateUrl={generateLink}>
-                        {!tokenValue || !parseFloat(tokenValue) || parseFloat(tokenValue) === 0
-                            ? 'Share open request'
-                            : `Share $${tokenValue} request`}
-                    </ShareButton>
+                    <ShareButton generateUrl={generateLink}>
+                        {(() => {
+                            const amount = Number(tokenValue)
+                            return !tokenValue || !isFinite(amount) || amount <= 0
+                                ? 'Share open request'
+                                : `Share $${tokenValue} request`
+                        })()}
+                    </ShareButton>

This parses once, handles all edge cases (NaN, Infinity, negatives, zero), and is more maintainable.


321-331: Remove unused dependencies from generateLink callback.

The qrCodeLink and tokenValue dependencies at line 331 are not referenced in the function body. Since tokenValue affects createRequestLink (which is already a dependency), and qrCodeLink isn't used, including them causes unnecessary re-renders.

Apply this diff to clean up the dependencies:

-    }, [generatedLink, qrCodeLink, tokenValue, attachmentOptions, createRequestLink, isCreatingLink, isUpdatingRequest])
+    }, [generatedLink, attachmentOptions, createRequestLink, isCreatingLink, isUpdatingRequest])
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)

1195-1205: URL‑encode query params when navigating to /request.

Prevent malformed URLs for names with spaces/special chars.

-                <Button
-                    onClick={() => {
-                        router.push(`/request?amount=${transaction.amount}&merchant=${transaction.userName}`)
-                    }}
+                <Button
+                    onClick={() => {
+                        const amt = encodeURIComponent(String(transaction.amount ?? 0))
+                        const merch = encodeURIComponent(transaction.userName ?? '')
+                        router.push(`/request?amount=${amt}&merchant=${merch}`)
+                    }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ccac6e and 7bc99ec.

📒 Files selected for processing (4)
  • src/components/Global/Icons/Icon.tsx (3 hunks)
  • src/components/Global/Icons/split.tsx (1 hunks)
  • src/components/Request/link/views/Create.request.link.view.tsx (7 hunks)
  • src/components/TransactionDetails/TransactionDetailsReceipt.tsx (13 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

Applied to files:

  • src/components/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.

Applied to files:

  • src/components/Request/link/views/Create.request.link.view.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/Request/link/views/Create.request.link.view.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/TransactionDetails/TransactionDetailsReceipt.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/TransactionDetails/TransactionDetailsReceipt.tsx
🧬 Code graph analysis (3)
src/components/Request/link/views/Create.request.link.view.tsx (2)
src/redux/slices/send-flow-slice.ts (2)
  • setTokenValue (47-49)
  • setAttachmentOptions (86-88)
src/redux/types/send-flow.types.ts (1)
  • IAttachmentOptions (9-13)
src/components/Global/Icons/Icon.tsx (1)
src/components/Global/Icons/split.tsx (1)
  • SplitIcon (3-12)
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (4)
src/utils/general.utils.ts (2)
  • getContributorsFromCharge (1362-1379)
  • formatCurrency (381-383)
src/services/requests.ts (1)
  • requestsApi (6-106)
src/services/charges.ts (1)
  • chargesApi (11-108)
src/components/Global/Card/index.tsx (1)
  • getCardPosition (14-19)
⏰ 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/Global/Icons/split.tsx (1)

1-12: LGTM! Clean icon component implementation.

The SplitIcon component follows the established pattern for icon components in this codebase. The use of fill="currentColor" enables proper theming, and spreading props after the fixed attributes allows the parent Icon component to override dimensions as needed.

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

66-66: LGTM! Proper integration of the new split icon.

The three changes correctly wire the new SplitIcon into the Icon system:

  • Import added at line 66
  • Type union extended at line 134
  • Component mapping added at line 206

This enables usage of <Icon name="split" /> throughout the codebase while maintaining type safety.

Also applies to: 134-134, 206-206

src/components/Request/link/views/Create.request.link.view.tsx (3)

260-274: LGTM! Simplified debounced update logic.

The refactored handleDebouncedChange now focuses solely on updating existing requests, which is cleaner and more maintainable. The inline change detection avoids unnecessary state dependencies while preserving correctness.


349-358: LGTM! Clear guidance for open request functionality.

The info text effectively communicates that users can leave the amount empty to create flexible "open requests" where payers choose their own amounts. This is a helpful UX improvement.


343-343: Navigation route /home is valid and properly configured.

The /home route exists at src/app/(mobile-ui)/home/page.tsx and the navigation change from /request to /home is supported by the app's routing structure. The /request route remains accessible via the wallet navigation menu.

src/components/TransactionDetails/TransactionDetailsReceipt.tsx (3)

666-677: Closed row rendering looks good.

Visibility and timestamp usage align with the config.


1427-1440: Contributors section LGTM.

Good use of stable keys and card positioning; scroll bounds are sensible.


265-270: Avoid non-null assertion on extraDataForDrawer (runtime TypeError risk).

Use optional chaining so it safely evaluates when drawer data is absent.

-const isQRPayment =
-    transaction &&
-    [EHistoryEntryType.MANTECA_QR_PAYMENT, EHistoryEntryType.SIMPLEFI_QR_PAYMENT].includes(
-        transaction.extraDataForDrawer!.originalType
-    )
+const isQRPayment = [EHistoryEntryType.MANTECA_QR_PAYMENT, EHistoryEntryType.SIMPLEFI_QR_PAYMENT].includes(
+  transaction?.extraDataForDrawer?.originalType as any
+)
⛔ Skipped due to learnings
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.
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.
Learnt from: Hugo0
PR: peanutprotocol/peanut-ui#413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-04T13:10:49.199Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.

@Zishan-7 Zishan-7 merged commit bdbf95e into peanut-wallet-dev Oct 24, 2025
5 checks passed
Copy link
Contributor

Choose a reason for hiding this comment

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

Btw we also have a progress bar in points / tiers. They are quite different, so I don't think justify DRY. But the name of this one shouldnt be "ProgressBar", since that implies its universal

const isGoalAchieved = progress >= goal && !isOverGoal && goal > 0
const totalValue = isOverGoal ? progress : goal

// Guard against division by zero and clamp percentages to valid ranges
Copy link
Contributor

Choose a reason for hiding this comment

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

praise

const QRCodeWrapper = ({ url, isLoading = false, disabled = false }: QRCodeWrapperProps) => {
return (
<div className="relative mx-auto h-auto w-full max-w-[192px]">
<div className="relative mx-auto h-auto w-full max-w-[160px]">
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm always slightly worried on changes to this file lol

})

const response = await fetchWithSentry('/api/proxy/withFormData/requests', {
const token = Cookies.get('jwt-token')
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this should be centralized somewhere

1: '#FFD8D8',
2: '#EA8282',
3: '#FF4A4A',
4: '#FC5555',
Copy link
Contributor

Choose a reason for hiding this comment

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

manu approved? 👁️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants