[TASK-15066] feat: implement UI changes for Points#1266
[TASK-15066] feat: implement UI changes for Points#1266Zishan-7 merged 11 commits intofeat/pointsfrom
Conversation
Zishan-7
commented
Oct 1, 2025
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded@Zishan-7 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 7 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughAdds a points API and types, a confetti utility, and integrates tier/points fetching across invites, mobile points, withdraw, recipient, claim, and payment success UIs; updates invites service to return points-shaped responses and adds canvas-confetti dependency. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/app/(mobile-ui)/points/page.tsx(3 hunks)src/services/points.ts(1 hunks)src/services/services.types.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/app/(mobile-ui)/points/page.tsx (5)
src/services/points.ts (1)
pointsApi(3-24)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)src/components/Global/Icons/Icon.tsx (1)
Icon(198-207)src/components/Global/Card/index.tsx (1)
getCardPosition(14-19)src/components/UserHeader/index.tsx (1)
VerifiedUserLabel(34-81)
src/services/points.ts (1)
src/services/services.types.ts (1)
TierInfo(327-336)
🔇 Additional comments (1)
src/services/services.types.ts (1)
327-335: TierInfo contract aligns with the new UI usage.
Field names and number types match the downstream consumption in the points page and the mock API.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
src/app/(mobile-ui)/points/page.tsx (2)
64-69: Clamp the tier progress width and handle zero thresholds.Line 67 divides by
tierInfo.data.nextTierThresholdwithout guarding against zero or undefined values. When a user reaches the maximum tier (threshold = 0 or undefined), this producesInfinity%orNaN%. IftotalPointsexceeds the threshold, the progress bar will overflow past 100%.Apply this diff to add validation and clamping:
<div className="flex w-full items-center gap-2"> {tierInfo?.data.currentTier} <div className="w-full"> <div className="relative h-1.5 w-full overflow-hidden rounded-full bg-grey-2"> - <div - className="h-full rounded-full bg-primary-1 transition-all duration-300" - style={{ - width: `${(tierInfo.data.totalPoints / tierInfo.data.nextTierThreshold) * 100}%`, - }} - /> + {(() => { + const threshold = tierInfo.data.nextTierThreshold || 1 + const rawPercent = (tierInfo.data.totalPoints / threshold) * 100 + const progress = Math.min(100, Math.max(0, rawPercent)) + return ( + <div + className="h-full rounded-full bg-primary-1 transition-all duration-300" + style={{ width: `${progress}%` }} + /> + ) + })()} </div> </div> {tierInfo?.data.currentTier + 1} </div>
63-69: Clamp the tier progress width and handle zero thresholds.Line 67 divides by
tierInfo.data.nextTierThresholdwith no guard. When a user hits the top tier (threshold = 0 or undefined) you'll renderInfinity%/NaN%, and iftotalPointsever overshoots the threshold the bar will overflow past 100%. Please derive the percentage with a zero check and clamp to[0, 100].Apply this diff to fix the calculation:
- <div - className="h-full rounded-full bg-primary-1 transition-all duration-300" - style={{ - width: `${(tierInfo.data.totalPoints / tierInfo.data.nextTierThreshold) * 100}%`, - }} - /> + {(() => { + const threshold = tierInfo.data.nextTierThreshold || 0 + const rawPercent = threshold > 0 ? (tierInfo.data.totalPoints / threshold) * 100 : 100 + const progress = Math.min(100, Math.max(0, rawPercent)) + return ( + <div + className="h-full rounded-full bg-primary-1 transition-all duration-300" + style={{ width: `${progress}%` }} + /> + ) + })()}
🧹 Nitpick comments (1)
src/services/points.ts (1)
7-26: Add explicit guard for missing JWT token.The method retrieves the JWT token but does not explicitly check if it's undefined before making the request. If the token is missing, the Authorization header will be
"Bearer undefined", which the backend will reject. While the error is caught by theresponse.okcheck or the catch block, adding an explicit guard improves clarity and aligns with the updatedinvitesApi.getInvites(which now throws on missing token in lines 31-33 of invites.ts).Apply this diff to add an explicit guard:
getTierInfo: async (): Promise<{ success: boolean; data: TierInfo | null }> => { try { const jwtToken = Cookies.get('jwt-token') + if (!jwtToken) { + return { success: false, data: null } + } const response = await fetchWithSentry(`${PEANUT_API_URL}/points`, {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/app/(mobile-ui)/points/page.tsx(5 hunks)src/services/invites.ts(2 hunks)src/services/points.ts(1 hunks)src/services/services.types.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/services/services.types.ts (1)
src/utils/bridge-accounts.utils.ts (1)
BridgeKycStatus(34-34)
src/services/points.ts (3)
src/services/services.types.ts (1)
TierInfo(327-336)src/utils/sentry.utils.ts (1)
fetchWithSentry(26-104)src/constants/general.consts.ts (1)
PEANUT_API_URL(43-47)
src/services/invites.ts (3)
src/services/services.types.ts (1)
PointsInvitesResponse(352-362)src/utils/sentry.utils.ts (1)
fetchWithSentry(26-104)src/constants/general.consts.ts (1)
PEANUT_API_URL(43-47)
src/app/(mobile-ui)/points/page.tsx (6)
src/services/points.ts (1)
pointsApi(6-27)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)src/components/Global/Icons/Icon.tsx (1)
Icon(198-207)src/services/services.types.ts (1)
PointsInvite(338-350)src/components/Global/Card/index.tsx (1)
getCardPosition(14-19)src/components/UserHeader/index.tsx (1)
VerifiedUserLabel(34-81)
🔇 Additional comments (11)
src/services/services.types.ts (4)
327-362: LGTM! Type definitions are well-structured.The new interfaces (TierInfo, PointsInvite, PointsInvitesResponse) are clearly defined with appropriate nullable fields and align correctly with their usage across the services and UI components.
327-336: LGTM!The
TierInfointerface is well-structured and correctly typed for tier-related data. All fields are appropriately named and typed.
338-350: LGTM!The
PointsInviteinterface is well-structured with appropriate nullable fields (fullName,kycStatus) and correctly typed for detailed invite data.
352-362: LGTM!The
PointsInvitesResponseinterface is well-structured with a nested summary object and correctly typed array of invitees.src/app/(mobile-ui)/points/page.tsx (6)
39-45: LGTM! Loading guards are properly implemented.The dual loading check (Line 39) and the data guard (Line 43-45) correctly prevent rendering the UI until both tier info and invites data are available, avoiding potential runtime errors.
97-138: LGTM! Invites rendering correctly uses the new data shape.The code properly maps over
invites.invitees(Line 111), uses the PointsInvite type fields (username, fullName, kycStatus, totalPoints), and implements the fullName fallback for avatar initials (Line 120).
30-45: LGTM!The tierInfo query setup and loading guards are correct. The component waits for both queries to complete and guards against missing data before rendering.
80-86: LGTM!The informational block is well-placed and provides helpful context about the points system.
97-138: LGTM!The invites rendering correctly uses the new
PointsInvitedata shape, with appropriate fallback forfullNamein avatar initials and correct display oftotalPoints.
140-161: LGTM!The empty invites state correctly checks
invites.invitees.lengthand provides a helpful call-to-action.src/services/invites.ts (1)
31-33: LGTM!The explicit error handling (throwing on missing token and non-OK responses) improves error visibility and aligns with typical React Query patterns. This allows consumers to handle failures explicitly via error states.
Also applies to: 42-44, 48-50
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/services/points.ts (1)
7-26: Add JWT validation and error logging.Lines 9-10 send
Authorization: Bearer undefinedif the JWT token is missing, which may cause unexpected server behavior. Additionally, line 23 silently swallows all errors without logging, making debugging difficult.Apply this diff to add validation and logging:
getTierInfo: async (): Promise<{ success: boolean; data: TierInfo | null }> => { try { const jwtToken = Cookies.get('jwt-token') + if (!jwtToken) { + console.warn('No JWT token found for getTierInfo') + return { success: false, data: null } + } const response = await fetchWithSentry(`${PEANUT_API_URL}/points`, { method: 'GET', headers: { Authorization: `Bearer ${jwtToken}`, 'Content-Type': 'application/json', }, }) if (!response.ok) { return { success: false, data: null } } const pointsInfo: TierInfo = await response.json() return { success: true, data: pointsInfo } - } catch { + } catch (error) { + console.error('Failed to fetch tier info:', error) return { success: false, data: null } } },
🧹 Nitpick comments (2)
src/services/points.ts (1)
65-66: Add error logging to calculatePoints.The catch block at lines 65-66 swallows the original error and throws a generic message, losing valuable debugging information about what actually failed (network error, JSON parse error, auth failure, etc.).
Apply this diff to preserve error context:
- } catch { - throw new Error('Failed to calculate points') + } catch (error) { + console.error('Failed to calculate points:', error) + throw error instanceof Error ? error : new Error('Failed to calculate points') }src/utils/confetti.ts (1)
24-47: Add reduced-motion support to respect user preferences.The confetti implementation should respect users'
prefers-reduced-motionsetting. Based on learnings, canvas-confetti@1.9.x provides adisableForReducedMotionoption that should be enabled.Apply this diff to add reduced-motion support:
const defaultConfettiConfig = { spread: 360, ticks: 80, gravity: 0.3, decay: 0.96, startVelocity: 15, colors: ['#FFE400', '#FFBD00', '#E89400', '#FFCA6C', '#FDFFB8'], + disableForReducedMotion: true, }Based on learnings.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
package.json(2 hunks)src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx(3 hunks)src/app/[...recipient]/client.tsx(3 hunks)src/components/Claim/Link/Onchain/Success.view.tsx(6 hunks)src/components/Payment/Views/Status.payment.view.tsx(5 hunks)src/services/points.ts(1 hunks)src/services/services.types.ts(1 hunks)src/utils/confetti.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.
Applied to files:
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
🧬 Code graph analysis (6)
src/components/Payment/Views/Status.payment.view.tsx (1)
src/utils/confetti.ts (1)
shootDoubleStarConfetti(49-67)
src/services/services.types.ts (1)
src/utils/bridge-accounts.utils.ts (1)
BridgeKycStatus(34-34)
src/components/Claim/Link/Onchain/Success.view.tsx (4)
src/services/points.ts (1)
pointsApi(6-69)src/utils/general.utils.ts (1)
formatTokenAmount(444-465)src/utils/confetti.ts (1)
shootDoubleStarConfetti(49-67)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)
src/app/[...recipient]/client.tsx (1)
src/services/points.ts (1)
pointsApi(6-69)
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx (1)
src/services/points.ts (1)
pointsApi(6-69)
src/services/points.ts (3)
src/services/services.types.ts (2)
TierInfo(327-336)CalculatePointsRequest(373-377)src/utils/sentry.utils.ts (1)
fetchWithSentry(26-104)src/constants/general.consts.ts (1)
PEANUT_API_URL(43-47)
🔇 Additional comments (3)
package.json (1)
54-54: LGTM!The canvas-confetti dependency and its types are properly added with current stable versions (^1.9.3 runtime, ^1.9.0 types). Based on learnings, version 1.9.x includes stability fixes and reduced-motion support.
Also applies to: 96-96
src/components/Payment/Views/Status.payment.view.tsx (1)
42-42: LGTM!The points integration is well-implemented:
- Optional prop with proper typing
- Confetti triggered only when points are present
- Clean conditional rendering of the points UI
Also applies to: 170-174, 244-249
src/services/services.types.ts (1)
327-377: LGTM!The new points-related types are well-structured:
- Clear interface definitions with appropriate field types
- Proper reuse of existing types (BridgeKycStatus)
- Descriptive enum values for PointsAction
- Good separation of concerns between request/response types
1 similar comment
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/services/points.ts (1)
7-30: Add error logging in the catch block.Line 27 silently swallows all errors (JSON parse failures, network errors not caught by
fetchWithSentry, etc.), making debugging difficult in production. WhilefetchWithSentryhandles network errors, the catch block here can catch JSON parsing errors and other unexpected failures.Apply this diff:
- } catch { + } catch (error) { + console.error('Failed to fetch tier info:', error) return { success: false, data: null } }src/components/Claim/Link/Onchain/Success.view.tsx (1)
39-56: Fix the unstablequeryKeythat defeats caching.Line 39 uses
crypto.randomUUID()inside theuseMemodependency array, which generates a new UUID on every render. This causes the query key to change constantly, defeating React Query's caching and triggering unnecessary refetches.Apply this diff to use a stable queryKey:
- const queryKey = useMemo(() => ['calculate-points'], [crypto.randomUUID()]) + const queryKey = useMemo( + () => ['calculate-points', 'claim-success', claimLinkData.link, claimLinkData.senderAddress], + [claimLinkData.link, claimLinkData.senderAddress] + )
🧹 Nitpick comments (1)
src/services/points.ts (1)
32-72: Preserve error details in the catch block.Line 69-71 catches all errors but throws a generic "Failed to calculate points" message, losing the original error context (network failures, JSON parse errors, etc.). This makes debugging production issues difficult.
Apply this diff to log the original error:
- } catch { - throw new Error('Failed to calculate points') + } catch (error) { + console.error('Failed to calculate points:', error) + throw error instanceof Error ? error : new Error('Failed to calculate points') }Alternatively, if you want to always throw the same error message for security reasons, at least log the original error:
- } catch { - throw new Error('Failed to calculate points') + } catch (error) { + console.error('Failed to calculate points:', error) + throw new Error('Failed to calculate points') }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/app/[...recipient]/client.tsx(3 hunks)src/components/Claim/Link/Onchain/Success.view.tsx(6 hunks)src/services/invites.ts(2 hunks)src/services/points.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-02T15:23:01.405Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.405Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.
Applied to files:
src/components/Claim/Link/Onchain/Success.view.tsxsrc/app/[...recipient]/client.tsx
🧬 Code graph analysis (4)
src/components/Claim/Link/Onchain/Success.view.tsx (4)
src/services/points.ts (1)
pointsApi(6-73)src/utils/general.utils.ts (1)
formatTokenAmount(444-465)src/utils/confetti.ts (1)
shootDoubleStarConfetti(49-67)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)
src/services/points.ts (3)
src/services/services.types.ts (2)
TierInfo(327-336)CalculatePointsRequest(373-377)src/utils/sentry.utils.ts (1)
fetchWithSentry(26-104)src/constants/general.consts.ts (1)
PEANUT_API_URL(43-47)
src/services/invites.ts (3)
src/services/services.types.ts (1)
PointsInvitesResponse(352-362)src/utils/sentry.utils.ts (1)
fetchWithSentry(26-104)src/constants/general.consts.ts (1)
PEANUT_API_URL(43-47)
src/app/[...recipient]/client.tsx (1)
src/services/points.ts (1)
pointsApi(6-73)
🔇 Additional comments (5)
src/services/invites.ts (2)
5-5: LGTM!The import change correctly reflects the new return type structure for
getInvites.
28-50: Past review concerns addressed.The unsafe
.catch(() => ({}))fallback that was flagged in previous reviews has been removed. The method now properly propagates JSON parse errors to the outer catch block, preventing runtime crashes from invalid empty objects. The throwing behavior is appropriate for an API method, and React Query (used by callers) will handle these errors correctly.src/app/[...recipient]/client.tsx (1)
36-38: LGTM: Import additions are correct.The imports for
useQuery,pointsApi, andPointsActionare necessary and properly used in the points calculation query below.src/components/Claim/Link/Onchain/Success.view.tsx (2)
1-1: LGTM: Import additions are correct.The new imports (
useQuery,STAR_STRAIGHT_ICON,pointsApi,PointsAction,PeanutLoading, andshootDoubleStarConfetti) are all necessary for the points calculation and display features added below.Also applies to: 12-12, 19-24
144-148: LGTM: Confetti trigger is correctly conditional.The confetti effect now correctly triggers only when
pointsData?.estimatedPointsis available, addressing the previous concern about unconditional triggering.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/app/(mobile-ui)/points/page.tsx (1)
63-69: Division by zero and overflow issue remains unaddressed.The progress bar calculation still divides by
nextTierThresholdwithout guards. This is a duplicate of a previous review comment that has not been resolved.When
nextTierThresholdis 0 or undefined (e.g., at max tier), the width will beInfinity%orNaN%. IftotalPointsexceedsnextTierThreshold, the bar will overflow past 100%.Apply this diff to fix:
- <div - className="h-full rounded-full bg-primary-1 transition-all duration-300" - style={{ - width: `${(tierInfo.data.totalPoints / tierInfo.data.nextTierThreshold) * 100}%`, - }} - /> + {(() => { + const threshold = tierInfo.data.nextTierThreshold || 1 + const rawPercent = (tierInfo.data.totalPoints / threshold) * 100 + const progress = Math.min(100, Math.max(0, rawPercent)) + return ( + <div + className="h-full rounded-full bg-primary-1 transition-all duration-300" + style={{ width: `${progress}%` }} + /> + ) + })()}
🧹 Nitpick comments (1)
src/app/(mobile-ui)/points/page.tsx (1)
52-78: Consider max tier edge case.The UI displays
currentTier + 1as the next tier (line 72) and shows "points needed for the next tier" (line 76). When users reach the maximum tier, these elements may display misleading information.Consider conditionally rendering the progress bar and next tier message only when a next tier exists, or display alternative content for max tier users.
Example:
{tierInfo.data.nextTierThreshold > 0 ? ( // Current progress bar and next tier UI ) : ( <p className="text-sm text-grey-1">You've reached the maximum tier!</p> )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/(mobile-ui)/points/page.tsx(5 hunks)src/app/[...recipient]/client.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/[...recipient]/client.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(mobile-ui)/points/page.tsx (5)
src/services/points.ts (1)
pointsApi(6-73)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)src/components/Global/Icons/Icon.tsx (1)
Icon(198-207)src/services/services.types.ts (1)
PointsInvite(338-350)src/components/Global/Card/index.tsx (1)
getCardPosition(14-19)
🔇 Additional comments (5)
src/app/(mobile-ui)/points/page.tsx (5)
14-14: LGTM!The new imports are properly utilized throughout the component.
Also applies to: 17-19
30-34: LGTM!The tier info query follows best practices with proper key structure and enablement condition.
39-45: LGTM!The loading state handling is thorough. The additional
tierInfo?.datacheck (lines 43-45) is technically redundant after the loading check but provides extra safety for data availability.
80-86: LGTM!The informational section provides clear guidance to users about the points system.
97-161: LGTM!The invites rendering correctly uses the new
PointsInvitetype structure with appropriate optional chaining, proper key usage (inviteeId), and correct derivation of verification status fromkycStatus.
…w for better cache management
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx(3 hunks)src/components/Claim/Link/Onchain/Success.view.tsx(6 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.
Applied to files:
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsxsrc/components/Claim/Link/Onchain/Success.view.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
PR: peanutprotocol/peanut-ui#869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
PR: peanutprotocol/peanut-ui#1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.
Applied to files:
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx
🧬 Code graph analysis (2)
src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx (1)
src/services/points.ts (1)
pointsApi(6-73)
src/components/Claim/Link/Onchain/Success.view.tsx (4)
src/services/points.ts (1)
pointsApi(6-73)src/utils/general.utils.ts (1)
formatTokenAmount(444-465)src/utils/confetti.ts (1)
shootDoubleStarConfetti(49-67)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/app/(mobile-ui)/points/page.tsx (1)
80-85: Clamp the tier progress width and handle zero thresholds.Line 83 divides by
tierInfo.data.nextTierThresholdwith no guard. When a user hits the top tier (threshold = 0 or undefined) you'll renderInfinity%/NaN%, and iftotalPointsever overshoots the threshold the bar will overflow past 100%.Apply this diff to fix the calculation:
- <div - className="h-full rounded-full bg-primary-1 transition-all duration-300" - style={{ - width: `${(tierInfo.data.totalPoints / tierInfo.data.nextTierThreshold) * 100}%`, - }} - /> + {(() => { + const threshold = tierInfo.data.nextTierThreshold || 0 + const rawPercent = threshold > 0 ? (tierInfo.data.totalPoints / threshold) * 100 : 100 + const progress = Math.min(100, Math.max(0, rawPercent)) + return ( + <div + className="h-full rounded-full bg-primary-1 transition-all duration-300" + style={{ width: `${progress}%` }} + /> + ) + })()}
🧹 Nitpick comments (1)
src/app/(mobile-ui)/points/page.tsx (1)
114-114: Simplify the redundant conditional check.The condition
invites?.invitees?.length && invites.invitees.length > 0is redundant. Ifinvites?.invitees?.lengthis truthy, it already implies> 0.Apply this diff to simplify:
- {invites?.invitees?.length && invites.invitees.length > 0 && ( + {invites?.invitees && invites.invitees.length > 0 && (
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/app/(mobile-ui)/points/page.tsx(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(mobile-ui)/points/page.tsx (9)
src/context/authContext.tsx (1)
useAuth(182-188)src/services/invites.ts (1)
invitesApi(7-89)src/services/points.ts (1)
pointsApi(6-73)src/components/Global/PeanutLoading/index.tsx (1)
PeanutLoading(4-19)src/components/Global/EmptyStates/EmptyState.tsx (1)
EmptyState(13-28)src/components/Global/Icons/Icon.tsx (1)
Icon(198-207)src/services/services.types.ts (1)
PointsInvite(338-350)src/components/Global/Card/index.tsx (1)
getCardPosition(14-19)src/components/UserHeader/index.tsx (1)
VerifiedUserLabel(34-81)
🔇 Additional comments (2)
src/app/(mobile-ui)/points/page.tsx (2)
48-50: LGTM!The loading state correctly guards against both queries and returns the appropriate loading component.
97-103: LGTM!The info block clearly explains the points system to users.