Skip to content

Commit e2f3430

Browse files
authored
Merge pull request #1380 from peanutprotocol/fix/request-pots-qa-fixed
Fix Request pots qa fixes
2 parents 07d3b3a + bc9d49d commit e2f3430

7 files changed

Lines changed: 94 additions & 36 deletions

File tree

src/components/Common/ActionList.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,14 @@ export default function ActionList({
188188
break
189189
}
190190
} else if (flow === 'request' && requestLinkData) {
191-
if (method.id === 'bank' && amountInUsd < 1) {
192-
setShowMinAmountError(true)
191+
// @dev TODO: Fix req fulfillment with bank properly post devconnect
192+
if (method.id === 'bank') {
193+
if (user?.user) {
194+
router.push('/add-money')
195+
} else {
196+
const redirectUri = encodeURIComponent('/add-money')
197+
router.push(`/setup?redirect_uri=${redirectUri}`)
198+
}
193199
return
194200
}
195201

src/components/Global/Contributors/ContributorCard.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
'use client'
12
import { type Payment } from '@/services/services.types'
23
import Card, { type CardPosition } from '../Card'
34
import AvatarWithBadge from '@/components/Profile/AvatarWithBadge'
45
import { getColorForUsername } from '@/utils/color.utils'
56
import { VerifiedUserLabel } from '@/components/UserHeader'
67
import { formatTokenAmount } from '@/utils'
78
import { isAddress } from 'viem'
9+
import { useRouter } from 'next/navigation'
10+
import { twMerge } from 'tailwind-merge'
811

912
export type Contributor = {
1013
uuid: string
@@ -13,15 +16,26 @@ export type Contributor = {
1316
username: string | undefined
1417
fulfillmentPayment: Payment | null
1518
isUserVerified: boolean
19+
isPeanutUser: boolean
1620
}
1721

1822
const ContributorCard = ({ contributor, position }: { contributor: Contributor; position: CardPosition }) => {
1923
const colors = getColorForUsername(contributor.username ?? '')
2024
const isEvmAddress = isAddress(contributor.username ?? '')
25+
26+
const router = useRouter()
27+
2128
return (
22-
<Card position={position} className="cursor-pointer">
29+
<Card position={position}>
2330
<div className="flex items-center justify-between">
24-
<div className="flex items-center gap-2">
31+
<div
32+
onClick={() => {
33+
if (contributor.isPeanutUser) {
34+
router.push(`/${contributor.username}`)
35+
}
36+
}}
37+
className={twMerge('flex items-center gap-2', contributor.isPeanutUser && 'cursor-pointer')}
38+
>
2539
<AvatarWithBadge
2640
name={contributor.username ?? ''}
2741
size={'extra-small'}

src/components/Global/ProgressBar/index.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { COIN_ICON } from '@/assets'
22
import Image from 'next/image'
33
import React from 'react'
44
import { twMerge } from 'tailwind-merge'
5+
import { formatExtendedNumber } from '@/utils/general.utils'
56

67
interface ProgressBarProps {
78
goal: number
@@ -12,14 +13,42 @@ interface ProgressBarProps {
1213
const ProgressBar: React.FC<ProgressBarProps> = ({ goal, progress, isClosed }) => {
1314
const isOverGoal = progress > goal && goal > 0
1415
const isGoalAchieved = progress >= goal && !isOverGoal && goal > 0
15-
const totalValue = isOverGoal ? progress : goal
16+
17+
// Calculate actual ratio and enforce minimum visual distance when over goal
18+
const MIN_VISUAL_DISTANCE = 30 // 30% minimum distance between markers
19+
let totalValue = isOverGoal ? progress : goal
20+
let visualGoalPercentage = 0
21+
let visualProgressPercentage = 0
22+
23+
if (isOverGoal && goal > 0) {
24+
const actualRatio = (progress / goal) * 100
25+
if (actualRatio < 100 + MIN_VISUAL_DISTANCE) {
26+
// Progress is too close to goal, enforce minimum distance
27+
// Map goal to ~90% and progress to 100%
28+
visualGoalPercentage = 100 - MIN_VISUAL_DISTANCE
29+
visualProgressPercentage = 100
30+
} else {
31+
// Progress is far enough, show actual ratio
32+
totalValue = progress
33+
visualGoalPercentage = (goal / totalValue) * 100
34+
visualProgressPercentage = 100
35+
}
36+
}
1637

1738
// Guard against division by zero and clamp percentages to valid ranges
18-
const goalPercentage = totalValue > 0 ? Math.min(Math.max((goal / totalValue) * 100, 0), 100) : 0
19-
const progressPercentage = totalValue > 0 ? Math.min(Math.max((progress / totalValue) * 100, 0), 100) : 0
39+
const goalPercentage = isOverGoal
40+
? visualGoalPercentage
41+
: totalValue > 0
42+
? Math.min(Math.max((goal / totalValue) * 100, 0), 100)
43+
: 0
44+
const progressPercentage = isOverGoal
45+
? visualProgressPercentage
46+
: totalValue > 0
47+
? Math.min(Math.max((progress / totalValue) * 100, 0), 100)
48+
: 0
2049
const percentage = goal > 0 ? Math.min(Math.max(Math.round((progress / goal) * 100), 0), 100) : 0
2150

22-
const formatCurrency = (value: number) => `$${value.toFixed(2)}`
51+
const formatCurrency = (value: number) => `$${formatExtendedNumber(value, 4)}`
2352

2453
const getStatusText = () => {
2554
if (isOverGoal) return 'Goal exceeded!'

src/components/Global/TokenAmountInput/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ const TokenAmountInput = ({
154154
if (maxAmount) {
155155
const selectedPercentage = value[0]
156156
const selectedAmount = parseFloat(((selectedPercentage / 100) * maxAmount).toFixed(4)).toString()
157-
onChange(selectedAmount, isInputUsd)
157+
const maxDecimals = displayMode === 'FIAT' || displayMode === 'STABLE' || isInputUsd ? 2 : decimals
158+
const formattedAmount = formatTokenAmount(selectedAmount, maxDecimals, true)
159+
if (formattedAmount) {
160+
onChange(formattedAmount, isInputUsd)
161+
}
158162
}
159163
},
160164
[maxAmount, onChange]

src/components/Payment/PaymentForm/index.tsx

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,13 @@ export const PaymentForm = ({
168168

169169
const isActivePeanutWallet = useMemo(() => !!user && isPeanutWalletConnected, [user, isPeanutWalletConnected])
170170

171+
const isRequestPotLink = !!chargeDetails?.requestLink
172+
171173
useEffect(() => {
172-
if (initialSetupDone || showRequestPotInitialView) return
174+
// skip this step for request pot payments
175+
// Amount is set by the user so we dont need to manually update it
176+
// chain and token are also USDC arb always, for cross-chain we use Daimo
177+
if (initialSetupDone || showRequestPotInitialView || isRequestPotLink) return
173178

174179
if (amount) {
175180
setInputTokenAmount(amount)
@@ -192,7 +197,7 @@ export const PaymentForm = ({
192197
}
193198

194199
setInitialSetupDone(true)
195-
}, [chain, token, amount, initialSetupDone, requestDetails, showRequestPotInitialView])
200+
}, [chain, token, amount, initialSetupDone, requestDetails, showRequestPotInitialView, isRequestPotLink])
196201

197202
// reset error when component mounts or recipient changes
198203
useEffect(() => {
@@ -301,17 +306,27 @@ export const PaymentForm = ({
301306

302307
// Calculate USD value when requested token price is available
303308
useEffect(() => {
304-
if (showRequestPotInitialView || !requestedTokenPriceData?.price || !requestDetails?.tokenAmount) return
309+
// skip this step for request pot payments
310+
// Amount is set by the user so we dont need to manually update it
311+
// No usd conversion needed because amount will always be USDC
312+
if (
313+
showRequestPotInitialView ||
314+
!requestedTokenPriceData?.price ||
315+
!requestDetails?.tokenAmount ||
316+
isRequestPotLink
317+
)
318+
return
305319

306320
const tokenAmount = parseFloat(requestDetails.tokenAmount)
307321
if (isNaN(tokenAmount) || tokenAmount <= 0) return
308322

309323
if (isNaN(requestedTokenPriceData.price) || requestedTokenPriceData.price === 0) return
310324

311325
const usdValue = formatAmount(tokenAmount * requestedTokenPriceData.price)
326+
312327
setInputTokenAmount(usdValue)
313328
setUsdValue(usdValue)
314-
}, [requestedTokenPriceData?.price, requestDetails?.tokenAmount, showRequestPotInitialView])
329+
}, [requestedTokenPriceData?.price, requestDetails?.tokenAmount, showRequestPotInitialView, isRequestPotLink])
315330

316331
const canInitiatePayment = useMemo<boolean>(() => {
317332
let amountIsSet = false
@@ -581,10 +596,12 @@ export const PaymentForm = ({
581596

582597
// Initialize inputTokenAmount
583598
useEffect(() => {
584-
if (amount && !inputTokenAmount && !initialSetupDone) {
599+
// skip this step for request pot payments
600+
// Amount is set by the user so we dont need to manually update it
601+
if (amount && !inputTokenAmount && !initialSetupDone && !showRequestPotInitialView) {
585602
setInputTokenAmount(amount)
586603
}
587-
}, [amount, inputTokenAmount, initialSetupDone])
604+
}, [amount, inputTokenAmount, initialSetupDone, showRequestPotInitialView])
588605

589606
useEffect(() => {
590607
const stepFromURL = searchParams.get('step')
@@ -620,6 +637,7 @@ export const PaymentForm = ({
620637

621638
// ensure inputTokenAmount is a valid positive number before allowing payment
622639
const numericAmount = parseFloat(inputTokenAmount)
640+
623641
if (isNaN(numericAmount) || numericAmount <= 0) {
624642
if (!isExternalWalletFlow) return true
625643
}
@@ -691,24 +709,8 @@ export const PaymentForm = ({
691709

692710
if (contributionAmounts.length === 0) return { percentage: 0, suggestedAmount: 0 }
693711

694-
const avgContribution = contributionAmounts.reduce((sum, amt) => sum + amt, 0) / contributionAmounts.length
695-
696-
// Calculate remaining amount (could be negative if over-contributed)
697-
const remaining = totalAmount - totalCollected
698-
let suggestedAmount: number
699-
700-
// If pot is already full or over-filled, suggest minimum contribution
701-
if (remaining <= 0) {
702-
// Pot is full/overfilled - suggest the smallest previous contribution or 10% of pot
703-
const minContribution = Math.min(...contributionAmounts)
704-
suggestedAmount = Math.min(minContribution, totalAmount * 0.1)
705-
} else if (remaining < avgContribution) {
706-
// If remaining is less than average, suggest the remaining amount
707-
suggestedAmount = remaining
708-
} else {
709-
// Otherwise, suggest the average contribution (most common pattern)
710-
suggestedAmount = avgContribution
711-
}
712+
// suggest the average contribution (most common pattern)
713+
const suggestedAmount = contributionAmounts.reduce((sum, amt) => sum + amt, 0) / contributionAmounts.length
712714

713715
// Convert amount to percentage of total pot
714716
const percentage = (suggestedAmount / totalAmount) * 100

src/components/TransactionDetails/TransactionDetailsReceipt.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1459,7 +1459,7 @@ export const TransactionDetailsReceipt = ({
14591459
{requestPotContributors.length > 0 && (
14601460
<>
14611461
<h2 className="text-base font-bold text-black">Contributors ({requestPotContributors.length})</h2>
1462-
<div className="max-h-36 overflow-y-auto">
1462+
<div className="overflow-y-auto">
14631463
{requestPotContributors.map((contributor, index) => (
14641464
<ContributorCard
14651465
position={getCardPosition(index, requestPotContributors.length)}

src/utils/general.utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ export const getHeaderTitle = (pathname: string) => {
655655
* @param amount - The number or string to format.
656656
* @returns A formatted string with appropriate suffix.
657657
*/
658-
export const formatExtendedNumber = (amount: string | number): string => {
658+
export const formatExtendedNumber = (amount: string | number, minDigitsForFomatting: number = 6): string => {
659659
// Handle null/undefined/invalid inputs
660660
if (!amount && amount !== 0) return '0'
661661

@@ -669,7 +669,7 @@ export const formatExtendedNumber = (amount: string | number): string => {
669669
const totalDigits = amount.toString().replace(/[.-]/g, '').length
670670

671671
// If 6 or fewer digits, just use formatAmount
672-
if (totalDigits <= 6) {
672+
if (totalDigits <= minDigitsForFomatting) {
673673
return formatAmount(num)
674674
}
675675

@@ -902,13 +902,16 @@ export const getContributorsFromCharge = (charges: ChargeEntry[]) => {
902902
username = successfulPayment.payerAccount.identifier
903903
}
904904

905+
const isPeanutUser = successfulPayment?.payerAccount?.type === AccountType.PEANUT_WALLET
906+
905907
return {
906908
uuid: charge.uuid,
907909
payments: charge.payments,
908910
amount: charge.tokenAmount,
909911
username,
910912
fulfillmentPayment: charge.fulfillmentPayment,
911913
isUserVerified: successfulPayment?.payerAccount?.user?.bridgeKycStatus === 'approved',
914+
isPeanutUser,
912915
}
913916
})
914917
}

0 commit comments

Comments
 (0)