Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/apps/review/src/lib/utils/reviewPhaseGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,28 @@ export const shouldIncludeInReviewPhase = (
}

const normalizedCandidateList = Array.from(normalizedCandidates)
const isExcluded = normalizedCandidateList
.some(candidate => (
EXCLUDED_REVIEW_TYPE_FRAGMENTS
.some(fragment => candidate.includes(fragment))
))

const hasExplicitReviewPhase = hasReviewPhase(submission.review, phases)
|| (
Array.isArray(submission.reviews)
&& submission.reviews.some(review => hasReviewPhase(review, phases))
)

const isExcluded = normalizedCandidateList.some(candidate => (
EXCLUDED_REVIEW_TYPE_FRAGMENTS.some(fragment => {
if (!candidate.includes(fragment)) {
return false
}

if (fragment === 'iterative' && hasExplicitReviewPhase) {

Choose a reason for hiding this comment

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

[⚠️ correctness]
The check for fragment === 'iterative' is case-sensitive. If there is any chance that fragment could be in a different case (e.g., 'Iterative'), consider normalizing the case of fragment before comparison to ensure consistency.

// Treat records that are tied to the official Review phase as review entries,
// even when the review type string mentions "Iterative".
return false
}

return true
})
))

if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
Expand Down
248 changes: 126 additions & 122 deletions src/apps/wallet/src/home/tabs/home/HomeTab.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable react/jsx-wrap-multilines */
import { FC, useEffect, useState } from 'react'
import { FC, useMemo } from 'react'

import { UserProfile } from '~/libs/core'
import { IconOutline, LinkButton, LoadingCircles } from '~/libs/ui'

import { Balance } from '../../../lib/models/WalletDetails'
import { InfoRow, PayoutGuard } from '../../../lib'
import { BannerImage, BannerText } from '../../../lib/assets/home'
import { nullToZero } from '../../../lib/util'
Expand All @@ -17,21 +16,136 @@ interface HomeTabProps {
profile: UserProfile
}

type WalletDetailsData = NonNullable<WalletDetailsResponse['data']>
interface WalletInfoRowsProps {
walletDetails: WalletDetailsData
profile: UserProfile
balanceSum: number
}

const WalletInfoRows: FC<WalletInfoRowsProps> = props => (
<div className={styles['info-row-container']}>
<InfoRow
title='Account Balance'
value={`$${props.balanceSum}`}
action={
<LinkButton
label='MANAGE YOUR WINNINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#winnings'
/>
}
/>

<PayoutGuard profile={props.profile}>
{props.walletDetails.withdrawalMethod.isSetupComplete && (
<InfoRow
title='Est. Payment Fees'
value={`$${nullToZero(props.walletDetails.estimatedFees)} USD`}
action={
<LinkButton
label='ADJUST YOUR PAYOUT SETTINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
{props.walletDetails.taxForm.isSetupComplete && (
<InfoRow
title='Est. Tax Withholding %'
value={`${nullToZero(props.walletDetails.taxWithholdingPercentage)}%`}
action={
<LinkButton
label='ADJUST YOUR PAYOUT SETTINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}

{!props.walletDetails.withdrawalMethod.isSetupComplete && (
<InfoRow
title='Withdrawal Method'
value={<Chip text='Setup Required' />}
action={
<LinkButton
label='SETUP WITHDRAWAL METHOD'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}

{!props.walletDetails.taxForm.isSetupComplete && (
<InfoRow
title='Tax Form'
value={<Chip text='Setup Required' />}
action={
<LinkButton
label='COMPLETE TAX FORM'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
{!props.walletDetails.identityVerification.isSetupComplete && (
<InfoRow
title='ID Verification'
value={<Chip text='Setup Required' />}
action={
<LinkButton
label='COMPLETE VERIFICATION'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
</PayoutGuard>
</div>
)

const HomeTab: FC<HomeTabProps> = props => {

const { data: walletDetails, isLoading, error }: WalletDetailsResponse = useWalletDetails()
const [balanceSum, setBalanceSum] = useState(0)

useEffect(() => {
if (walletDetails) {
setBalanceSum(
walletDetails.account.balances.reduce((sum: number, balance: Balance) => sum + balance.amount, 0),
)
}
}, [walletDetails])
const balanceSum = useMemo(

Choose a reason for hiding this comment

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

[⚠️ performance]
Using useMemo here is appropriate for performance optimization, but ensure that walletDetails.account.balances is not mutated elsewhere in the component lifecycle, as it could lead to stale values being used.

() => (walletDetails ? walletDetails.account.balances.reduce((sum, balance) => sum + balance.amount, 0) : 0),
[walletDetails],
)

if (error) {
return <div>{error}</div>
let errorMessage = 'Unable to load wallet details.'

if (typeof error === 'string') {
errorMessage = error
} else if (error && typeof error === 'object' && 'message' in error) {
errorMessage = (error as Error).message || errorMessage

Choose a reason for hiding this comment

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

[⚠️ correctness]
The error handling logic assumes that the error object has a message property. Consider adding a type guard or validation to ensure this property exists to avoid potential runtime errors.

}

return <div>{errorMessage}</div>
}

return (
Expand All @@ -42,117 +156,7 @@ const HomeTab: FC<HomeTabProps> = props => {
</div>
{isLoading && <LoadingCircles />}
{!isLoading && walletDetails && (
<div className={styles['info-row-container']}>
<InfoRow
title='Account Balance'
value={`$${balanceSum}`}
action={
<LinkButton
label='MANAGE YOUR WINNINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#winnings'
/>
}
/>

<PayoutGuard profile={props.profile}>
{walletDetails.withdrawalMethod.isSetupComplete && (
<InfoRow
title='Est. Payment Fees'
value={`$${nullToZero(walletDetails.estimatedFees)} USD`}
action={
<LinkButton
label='ADJUST YOUR PAYOUT SETTINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
{walletDetails.taxForm.isSetupComplete && (
<InfoRow
title='Est. Tax Withholding %'
value={`${nullToZero(walletDetails.taxWithholdingPercentage)}%`}
action={
<LinkButton
label='ADJUST YOUR PAYOUT SETTINGS'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}

{!walletDetails?.withdrawalMethod.isSetupComplete && (
<InfoRow
title='Withdrawal Method'
value={
walletDetails?.withdrawalMethod.isSetupComplete ? (
'Your preferred method'
) : (
<Chip text='Setup Required' />
)
}
action={
<LinkButton
label='SETUP WITHDRAWAL METHOD'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}

{!walletDetails?.taxForm.isSetupComplete && (
<InfoRow
title='Tax Form'
value={
walletDetails?.taxForm.isSetupComplete
? 'All set'
: <Chip text='Setup Required' />
}
action={
<LinkButton
label='COMPLETE TAX FORM'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
{!walletDetails?.identityVerification.isSetupComplete && (
<InfoRow
title='ID Verification'
value={<Chip text='Setup Required' />}
action={
<LinkButton
label='COMPLETE VERIFICATION'
iconToRight
icon={IconOutline.ArrowRightIcon}
size='md'
link
to='#payout'
/>
}
/>
)}
</PayoutGuard>
</div>
<WalletInfoRows walletDetails={walletDetails} profile={props.profile} balanceSum={balanceSum} />
)}
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion src/apps/wallet/src/lib/hooks/use-wallet-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getWalletDetails } from '../services/wallet'

export interface Response<T> {
data?: Readonly<T>
error?: Readonly<string>
error?: Readonly<Error> | string

Choose a reason for hiding this comment

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

[⚠️ correctness]
Changing error to Readonly<Error> | string allows for more flexibility, but ensure that all parts of the code handling this error property can correctly process both Error objects and strings. This change might require additional checks or transformations where error is used to avoid runtime issues.

mutate: KeyedMutator<any>
isLoading?: Readonly<boolean>
}
Expand Down