Conversation
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Juan José Ramírez <70615692+jjramirezn@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds 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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 requestsYou 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 useCallbackgetTitle 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 consistentVerify 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 propEither 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 useEffectSetting 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 removeProgressBar(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 navigationonPrev={() => {}} 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 pitfallsIf 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
⛔ Files ignored due to path filters (1)
src/assets/icons/socials.svgis 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 semanticsThe new
error.4shade (#FC5555) is used insrc/components/Global/ProgressBar/index.tsxfor failure states but is very close toerror.3(#FF4A4A). Document intailwind.config.jsor your design guidelines when to use each token to avoid confusion.src/assets/icons/index.ts (1)
24-24: SOCIALS_ICON export looks goodNaming 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 reliabilityAt 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 neededImport 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 goodThe 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 improvementsStatusPill 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 correct –src/assets/index.tsre-exports from./icons, soimport { SOCIALS_ICON } from '@/assets'is valid.Likely an incorrect or invalid review comment.
src/components/TransactionDetails/TransactionDetailsReceipt.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
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. TheisLoadingcalculation (lines 457-458) also runs regardless of flow, which could show a spinner that masksRequestFulfillmentFlow.Gate the effects and loading state when
flow === 'request_pay':
- Update
isLoadingcalculation to exclude 'request_pay':const isLoading = + flow !== 'request_pay' && (!isUrlParsed || (chargeId && !chargeDetails) || isRequestDetailsFetching || isDaimoPaymentProcessing)
- 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
RequestFulfillmentFlowmanages its own data lifecycle, consider moving shared logic into that component or a dedicated provider used only whenflow === '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
trueparameter forces USD mode regardless of currentdisplayModeorisInputUsdstate, causing incorrect conversions for non-USD currencies or token-denominated inputs. Additionally, the slider position calculation at line 311 dividestokenValuebymaxAmount, 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.
Iconis already imported at line 5 and can be used directly at line 303 instead of creating an aliasIconComponent.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
📒 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
avatarUrlpresence is sound, and the fallback toTransactionAvatarBadgewithout 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-2instead ofgap-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
iconSizehas been changed from8to7, 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 retainiconSize = 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
CreateRequestLinkViewdirectly instead ofRequestRouterView. This architectural change may impact how users navigate between different request states (e.g., create, view, edit). Please verify:
- User flows for creating, viewing, and managing requests still work as expected.
- Any routing logic previously handled by
RequestRouterViewhas been appropriately migrated.- 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.viewis correct, andRequestRouterViewhas 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
There was a problem hiding this comment.
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
tokenValueis 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
tokenValueis empty, creating inconsistent behavior between the auto-save flow and the manual share flow (which works viagenerateLink).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
tokenValueis 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 whentokenValueis 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 goodThe 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 scalingContainer 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 → showProgressBarFix 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 casesCurrent 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 containerPrevents 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 useCallbackTitle 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/undefinedUse 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 importTransactionCard 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 arrayIf 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
&& isRequestPotPaymentwhen 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
contributorson every render. SincetotalAmountCollecteddepends oncontributorsand is memoized,contributorsshould 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
📒 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
showInfoTextandinfoTextprops 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
getContributorsFromChargefunction is properly re-exported from@/utilsvia the wildcard export pattern insrc/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 = !!requestIdand 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 intentionalThe 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
isRequestPotPaymentproperty 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
initiatePaymentreturns "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
usePaymentInitiatorhandles request pot payments:
src/components/TransactionDetails/TransactionDetailsReceipt.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
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
canInitiatePaymentuseMemo is missingisRequestPotPaymentin 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
handleInitiatePaymentcallback is missingisRequestPotPaymentin 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
parseFloatcalls 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
Contributortype is missing thehaveSentMoneyToUserfield, andVerifiedUserLabelat line 33-37 doesn't pass this prop, defaulting all contributors tofalse. This prevents the double-check badge from appearing for verified users the viewer has sent money to previously.To fix:
- Add
haveSentMoneyToUser: booleanto theContributortype (lines 9-16)- Update
getContributorsFromChargeinsrc/utils/general.utils.tsto include this field (requires access to user interactions data)- Pass
haveSentMoneyToUser={contributor.haveSentMoneyToUser}at line 37If 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
fulfillmentPaymentexists. 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
0andundefinedmean "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
&& isRequestPotPaymentwhich is redundant since it's already verified by theifstatement 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
contributorsarray andtotalAmountCollectedare 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
📒 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.tsxsrc/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
/requestto/homeis 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
formatCurrencyin 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
!!requestIdappropriately 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
requestIdis 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
maxDecimalsandminDecimalsparameters 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.replaceis appropriate here because components inclient.tsxconsume params viauseSearchParams()inuseEffectdependencies 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/jsonandjsonStringify(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, andisRequestPotprops 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
!== undefinedchecks andamount > 0guard ensure:
- Progress bar only renders for request-pot payments with defined amounts
- Zero amounts (open requests) don't show progress
- 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
getCardPositionutility 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.
There was a problem hiding this comment.
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: AddisRequestPotPaymentto dependency array.The
canInitiatePaymentcomputation usesisRequestPotPaymentat lines 321-322 but it's missing from the dependency array at line 334. This causes the memoized value to use a staleisRequestPotPaymentwhen 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.
getContributorsFromChargeis called on every render, which maps over the charges array. Wrap this inuseMemoto 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 === 0for 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
📒 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
There was a problem hiding this comment.
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
📒 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.
There was a problem hiding this comment.
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 formattingusdAmount 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: AddshowRequestPotInitialViewto dependency array.The
canInitiatePaymentmemoized value usesshowRequestPotInitialViewat line 336 but it's missing from the dependency array (lines 341-349). This can cause the computed value to reference a stale value whenshowRequestPotInitialViewchanges.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 presentThe 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 nameformattedTotalAmountCollected 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 unitsEvery 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 tweakExtract 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 renderingStatusPill 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 constructionEncode 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 usagesTypo 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 defaultAvoid breaking future call sites; default to false.
- isTransactionClosed: boolean + isTransactionClosed?: booleanAnd use: const closed = Boolean(isTransactionClosed) when passing to ProgressBar.
src/components/TransactionDetails/TransactionDetailsReceipt.tsx (1)
1407-1420: Contributors list: guard large listsIf 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
&& showRequestPotInitialViewat line 337 is redundant since it's already inside theif (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
📒 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
sliderValuecalculation properly convertstokenValueto 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
onSliderValueChangeandsliderValuefollows the expected pattern. The UI elements are well-structured.src/utils/history.utils.ts (1)
75-87: Ensure UI supports ‘closed’ status end-to-endYou 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 supportUI 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 throughisRequestPotLink, 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 theshowRequestPotInitialViewprop 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
showRequestPotInitialViewis 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
handleInitiatePaymentproperly bypass wallet connection, sign-in prompts, and invite handling whenshowRequestPotInitialViewis true. ThereturnAfterChargeCreationpayload 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 (
[]and0).
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_paywith 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
There was a problem hiding this comment.
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 (sinceNaN <= 0is 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
useMemoto perform side effects (setting state) violates React's rules of hooks.useMemois 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
qrCodeLinkandtokenValuevariables appear in the dependency array but are not used within thegenerateLinkfunction 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
📒 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
generateLinkfunction (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
/homeinstead 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.
There was a problem hiding this comment.
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
parseFloatcalls 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
qrCodeLinkandtokenValuedependencies at line 331 are not referenced in the function body. SincetokenValueaffectscreateRequestLink(which is already a dependency), andqrCodeLinkisn'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
📒 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
handleDebouncedChangenow 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/homeis valid and properly configured.The
/homeroute exists atsrc/app/(mobile-ui)/home/page.tsxand the navigation change from/requestto/homeis supported by the app's routing structure. The/requestroute 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.
There was a problem hiding this comment.
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 |
| 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]"> |
There was a problem hiding this comment.
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') |
There was a problem hiding this comment.
nit: this should be centralized somewhere
| 1: '#FFD8D8', | ||
| 2: '#EA8282', | ||
| 3: '#FF4A4A', | ||
| 4: '#FC5555', |
Contributes to
TASK-15548
TASK-15663
1. Request Creation
2. Request Receipts
Pending
Closed
Cancelled
3. Request Fulfillment