Skip to content

Commit 88773e6

Browse files
authored
[WALL] Lubega / WALL-3317 & WALL-3318 / Wallets Withdrawal and Deposit Locked scenarios (deriv-com#13188)
* feat: withdrawal and deposit locked * fix: improved sonarcloud analysis * fix: applied comments * fix: simplified logic * fix: applied comments * fix: applied css bem styling * fix: updated and implemented wallet link component * fix: removed classnames * fix: applied comments
1 parent f717142 commit 88773e6

19 files changed

+1175
-6
lines changed

packages/api/src/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export { default as useAccesiblePlatforms } from './useAccesiblePlatforms';
1414
export { default as useAvailableMT5Accounts } from './useAvailableMT5Accounts';
1515
export { default as useAvailableWallets } from './useAvailableWallets';
1616
export { default as useBalance } from './useBalance';
17+
export { default as useCashierValidation } from './useCashierValidation';
1718
export { default as useCloseDerivAccount } from './useCloseDerivAccount';
1819
export { default as useCFDAccountsList } from './useCFDAccountsList';
1920
export { default as useCFDCompareAccounts } from './useCFDCompareAccounts';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useMemo } from 'react';
2+
import useGetAccountStatus from './useGetAccountStatus';
3+
4+
/** A custom hook to check the cashier validations for the cashier locked scenarios. */
5+
const useCashierValidation = () => {
6+
const { data: get_account_status_data, ...rest } = useGetAccountStatus();
7+
8+
const modified_cashier_validation = useMemo(() => {
9+
if (!get_account_status_data?.cashier_validation) return;
10+
11+
const cashier_validation = new Set(get_account_status_data?.cashier_validation);
12+
13+
return {
14+
transfer_blocked: cashier_validation.has('transfer_blocked'),
15+
no_residence: cashier_validation.has('no_residence'),
16+
unwelcome_status: cashier_validation.has('unwelcome_status'),
17+
self_exclusion: cashier_validation.has('SelfExclusion'),
18+
no_withdrawal_or_trading_status: cashier_validation.has('no_withdrawal_or_trading_status'),
19+
only_pa_withdrawals_allowed_status: cashier_validation.has('only_pa_withdrawals_allowed_status'),
20+
withdraw_service_unavailable_for_pa: cashier_validation.has('WithdrawServiceUnavailableForPA'),
21+
withdrawal_locked_status: cashier_validation.has('withdrawal_locked_status'),
22+
documents_expired: cashier_validation.has('documents_expired'),
23+
cashier_locked_status: cashier_validation.has('cashier_locked_status'),
24+
disabled_status: cashier_validation.has('disabled_status'),
25+
financial_assessment_required: cashier_validation.has('FinancialAssessmentRequired'),
26+
ask_currency: cashier_validation.has('ASK_CURRENCY'),
27+
ask_authenticate: cashier_validation.has('ASK_AUTHENTICATE'),
28+
ask_financial_risk_approval: cashier_validation.has('ASK_FINANCIAL_RISK_APPROVAL'),
29+
ask_tin_information: cashier_validation.has('ASK_TIN_INFORMATION'),
30+
ask_self_exclusion_max_turnover_set: cashier_validation.has('ASK_SELF_EXCLUSION_MAX_TURNOVER_SET'),
31+
ask_fix_details: cashier_validation.has('ASK_FIX_DETAILS'),
32+
ask_uk_funds_protection: cashier_validation.has('ASK_UK_FUNDS_PROTECTION'),
33+
pa_commision_withdrawal_limit: cashier_validation.has('PACommisionWithdrawalLimit'),
34+
};
35+
}, [get_account_status_data?.cashier_validation]);
36+
37+
return {
38+
/** The cashier validation response. */
39+
data: modified_cashier_validation,
40+
...rest,
41+
};
42+
};
43+
44+
export default useCashierValidation;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
.wallets-link {
2+
cursor: pointer;
23
color: var(--brand-coral, #ff444f);
3-
text-decoration: underline;
4+
border: none;
5+
background: none;
6+
font-size: inherit;
7+
padding: 0;
8+
9+
&__variant {
10+
&--normal {
11+
text-decoration: underline;
12+
}
13+
14+
&--bold {
15+
font-weight: bold;
16+
text-decoration: none;
17+
18+
&:hover {
19+
text-decoration: underline;
20+
}
21+
}
22+
}
423
}

packages/wallets/src/components/Base/WalletLink/WalletLink.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import React from 'react';
22
import { getStaticUrl } from '../../../helpers/urls';
33
import './WalletLink.scss';
44

5+
type TVariant = 'bold' | 'normal';
6+
57
interface LinkProps {
68
children?: React.ReactNode;
79
href?: React.AnchorHTMLAttributes<HTMLAnchorElement>['href'];
810
staticUrl?: React.AnchorHTMLAttributes<HTMLAnchorElement>['href'];
11+
variant?: TVariant;
912
}
1013

11-
const WalletLink: React.FC<LinkProps> = ({ children, href, staticUrl }) => (
14+
const WalletLink: React.FC<LinkProps> = ({ children, href, staticUrl, variant = 'normal' }) => (
1215
<a
13-
className='wallets-link'
16+
className={`wallets-link wallets-link__variant--${variant}`}
1417
href={href ?? (staticUrl ? getStaticUrl(staticUrl) : '#')}
1518
rel='noopener noreferrer'
1619
target='_blank'

packages/wallets/src/components/WalletsActionScreen/WalletsActionScreen.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type TProps = {
1212
| ReactElement<ComponentProps<typeof WalletButton>>
1313
| ReactElement<ComponentProps<typeof WalletButtonGroup>>
1414
| null;
15-
title?: string;
15+
title?: ReactNode;
1616
titleSize?: ComponentProps<typeof WalletText>['size'];
1717
};
1818

packages/wallets/src/features/cashier/components/WalletCashierContent/WalletCashierContent.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { WalletResetBalance } from '../../flows/WalletResetBalance';
66
import { WalletTransactions } from '../../flows/WalletTransactions';
77
import { WalletTransfer } from '../../flows/WalletTransfer';
88
import { WalletWithdrawal } from '../../flows/WalletWithdrawal';
9+
import { DepositLocked, WithdrawalLocked } from '../../screens';
910

1011
const WalletCashierContent = () => {
1112
const history = useHistory();
@@ -24,7 +25,12 @@ const WalletCashierContent = () => {
2425
}
2526
}, [isTransfer, isDeposit, isTransactions, isWithdraw, isResetBalance, isFiatOnRamp, history]);
2627

27-
if (isDeposit) return <WalletDeposit />;
28+
if (isDeposit)
29+
return (
30+
<DepositLocked>
31+
<WalletDeposit />
32+
</DepositLocked>
33+
);
2834

2935
if (isFiatOnRamp) return <WalletFiatOnRamp />;
3036

@@ -35,7 +41,11 @@ const WalletCashierContent = () => {
3541
if (isTransactions) return <WalletTransactions />;
3642

3743
if (isWithdraw) {
38-
return <WalletWithdrawal />;
44+
return (
45+
<WithdrawalLocked>
46+
<WalletWithdrawal />
47+
</WithdrawalLocked>
48+
);
3949
}
4050

4151
return <></>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.wallets-deposit-locked {
2+
margin-top: 9.2rem; //140px - 48px from cashier content
3+
4+
@include mobile {
5+
margin-top: 1.6rem; //40px - 16px from cashier content
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from 'react';
2+
import { Trans } from 'react-i18next';
3+
import {
4+
useAccountStatus,
5+
useActiveWalletAccount,
6+
useAuthentication,
7+
useCashierValidation,
8+
useQuery,
9+
useSettings,
10+
} from '@deriv/api';
11+
import { WalletsActionScreen } from '../../../../components';
12+
import getDepositLockedDesc from './DepositLockedContent';
13+
import './DepositLocked.scss';
14+
15+
const DepositLocked: React.FC<React.PropsWithChildren> = ({ children }) => {
16+
const { data: activeWallet } = useActiveWalletAccount();
17+
const { data: settings } = useSettings();
18+
const { data: websiteStatus } = useQuery('website_status');
19+
const { data: authentication } = useAuthentication();
20+
const { data: cashierValidation } = useCashierValidation();
21+
const { data: status } = useAccountStatus();
22+
23+
const currency = activeWallet?.currency || 'USD';
24+
const excludedUntil = activeWallet?.excluded_until;
25+
const isMFAccount = activeWallet?.loginid?.startsWith('MF') || false;
26+
27+
const clientTncStatus = settings?.client_tnc_status;
28+
const websiteTncVersion = websiteStatus?.website_status?.terms_conditions_version;
29+
30+
const poaNeedsVerification = authentication?.is_poa_needed;
31+
const poiNeedsVerification = authentication?.is_poa_needed;
32+
const poaStatus = authentication?.poa_status || 'none';
33+
const poiStatus = authentication?.poi_status || 'none';
34+
35+
const askFixDetails = cashierValidation?.ask_fix_details;
36+
const selfExclusion = cashierValidation?.self_exclusion;
37+
const unwelcomeStatus = cashierValidation?.unwelcome_status;
38+
39+
const isDepositLocked = status?.is_deposit_locked;
40+
const financialInformationNotComplete = status?.is_financial_information_not_complete;
41+
const tradingExperienceNotComplete = status?.is_trading_experience_not_complete;
42+
43+
if (isDepositLocked) {
44+
return (
45+
<div className='wallets-deposit-locked'>
46+
<WalletsActionScreen
47+
description={
48+
getDepositLockedDesc({
49+
askFixDetails,
50+
clientTncStatus,
51+
excludedUntil,
52+
financialInformationNotComplete,
53+
isMFAccount,
54+
poaNeedsVerification,
55+
poaStatus,
56+
poiNeedsVerification,
57+
poiStatus,
58+
selfExclusion,
59+
tradingExperienceNotComplete,
60+
unwelcomeStatus,
61+
websiteTncVersion,
62+
})?.description
63+
}
64+
title={
65+
<Trans
66+
defaults='Deposits into your {{currency}} Wallet are temporarily locked.'
67+
values={{ currency }}
68+
/>
69+
}
70+
/>
71+
</div>
72+
);
73+
}
74+
75+
return <>{children}</>;
76+
};
77+
78+
export default DepositLocked;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import React from 'react';
2+
import { Trans } from 'react-i18next';
3+
import { WalletLink, WalletText } from '../../../../components';
4+
5+
type TDepositLockedDescProps = {
6+
askFixDetails?: boolean;
7+
clientTncStatus?: string | null;
8+
excludedUntil?: Date;
9+
financialInformationNotComplete?: boolean;
10+
isMFAccount: boolean;
11+
poaNeedsVerification?: boolean;
12+
poaStatus: string;
13+
poiNeedsVerification?: boolean;
14+
poiStatus: string;
15+
selfExclusion?: boolean;
16+
tradingExperienceNotComplete?: boolean;
17+
unwelcomeStatus?: boolean;
18+
websiteTncVersion?: string;
19+
};
20+
21+
const getDepositLockedDesc = ({
22+
askFixDetails,
23+
clientTncStatus,
24+
excludedUntil,
25+
financialInformationNotComplete,
26+
isMFAccount,
27+
poaNeedsVerification,
28+
poaStatus,
29+
poiNeedsVerification,
30+
poiStatus,
31+
selfExclusion,
32+
tradingExperienceNotComplete,
33+
unwelcomeStatus,
34+
websiteTncVersion,
35+
}: TDepositLockedDescProps) => {
36+
if (poiNeedsVerification && poiStatus !== 'none')
37+
return {
38+
description: (
39+
<WalletText align='center'>
40+
<Trans
41+
components={[<WalletLink href='/account/proof-of-identity' key={0} variant='bold' />]}
42+
defaults='To enable deposits, you must check your <0>proof of identity document verification status</0>.'
43+
/>
44+
</WalletText>
45+
),
46+
};
47+
48+
if (poaNeedsVerification && poaStatus !== 'none')
49+
return {
50+
description: (
51+
<WalletText align='center'>
52+
<Trans
53+
components={[<WalletLink href='/account/proof-of-address' key={0} variant='bold' />]}
54+
defaults='To enable deposits, you must check your <0>proof of address document verification status</0>.'
55+
/>
56+
</WalletText>
57+
),
58+
};
59+
60+
if (clientTncStatus !== websiteTncVersion)
61+
return {
62+
description: (
63+
<WalletText align='center'>
64+
<Trans
65+
components={[<WalletLink key={0} staticUrl='/terms-and-conditions/#clients' variant='bold' />]}
66+
defaults='To enable deposits, you must accept our <0>updated terms and conditions</0>.'
67+
/>
68+
</WalletText>
69+
),
70+
};
71+
72+
if (isMFAccount && (financialInformationNotComplete || tradingExperienceNotComplete))
73+
return {
74+
description: (
75+
<WalletText align='center'>
76+
<Trans
77+
components={[<WalletLink href='/account/financial-assessment' key={0} variant='bold' />]}
78+
defaults='To enable deposits, you must complete the <0>financial assessment form</0>.'
79+
/>
80+
</WalletText>
81+
),
82+
};
83+
84+
if (askFixDetails)
85+
return {
86+
description: (
87+
<WalletText align='center'>
88+
<Trans
89+
components={[<WalletLink href='/account/personal-details' key={0} variant='bold' />]}
90+
defaults='Your <0>personal details</0> are incomplete. Please go to your account settings and complete your personal details to enable deposits.'
91+
/>
92+
</WalletText>
93+
),
94+
};
95+
96+
if (selfExclusion)
97+
return {
98+
description: (
99+
<WalletText align='center'>
100+
<Trans
101+
components={[
102+
<button
103+
className='wallets-link wallets-link__variant--bold'
104+
key={0}
105+
onClick={() => window.LC_API.open_chat_window()}
106+
/>,
107+
]}
108+
defaults='You have chosen to exclude yourself from trading on our website until {{excludedUntil}}. If you are unable to place a trade or deposit after your self-exclusion period, please contact us via <0>live chat</0>.'
109+
values={{ excludedUntil }}
110+
/>
111+
</WalletText>
112+
),
113+
};
114+
115+
if (unwelcomeStatus)
116+
return {
117+
description: (
118+
<WalletText align='center'>
119+
<Trans
120+
components={[
121+
<button
122+
className='wallets-link wallets-link__variant--bold'
123+
key={0}
124+
onClick={() => window.LC_API.open_chat_window()}
125+
/>,
126+
]}
127+
defaults='Please contact us via <0>live chat</0>.'
128+
/>
129+
</WalletText>
130+
),
131+
};
132+
};
133+
134+
export default getDepositLockedDesc;

0 commit comments

Comments
 (0)