@@ -94,16 +123,16 @@ const WalletInformation = ({ account, blockchain_address }) => {
const CryptoWithdrawReceipt = ({
account,
blockchain_address,
- withdraw_amount,
crypto_transactions,
currency,
is_switching,
- resetWithrawForm,
recentTransactionOnMount,
+ resetWithrawForm,
setIsCryptoTransactionsVisible,
setIsWithdrawConfirmed,
tab_index,
-}) => {
+ withdraw_amount,
+}: TCryptoWithdrawReceiptProps) => {
React.useEffect(() => {
recentTransactionOnMount();
}, [recentTransactionOnMount]);
@@ -172,21 +201,7 @@ const CryptoWithdrawReceipt = ({
);
};
-CryptoWithdrawReceipt.propTypes = {
- account: PropTypes.object,
- crypto_transactions: PropTypes.array,
- blockchain_address: PropTypes.string,
- currency: PropTypes.string,
- is_switching: PropTypes.bool,
- resetWithrawForm: PropTypes.func,
- recentTransactionOnMount: PropTypes.func,
- setIsCryptoTransactionsVisible: PropTypes.func,
- setIsWithdrawConfirmed: PropTypes.func,
- tab_index: PropTypes.number,
- withdraw_amount: PropTypes.string,
-};
-
-export default connect(({ client, modules }) => ({
+export default connect(({ client, modules }: TRootStore) => ({
account: modules.cashier.account_transfer.selected_from,
blockchain_address: modules.cashier.withdraw.blockchain_address,
withdraw_amount: modules.cashier.withdraw.withdraw_amount,
diff --git a/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.js b/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.js
deleted file mode 100644
index 1ec7fa4ee102..000000000000
--- a/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import CryptoWithdrawReceipt from './crypto-withdraw-receipt.jsx';
-
-export default CryptoWithdrawReceipt;
diff --git a/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.ts b/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.ts
new file mode 100644
index 000000000000..f774459a4b2b
--- /dev/null
+++ b/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/index.ts
@@ -0,0 +1,3 @@
+import CryptoWithdrawReceipt from './crypto-withdraw-receipt';
+
+export default CryptoWithdrawReceipt;
diff --git a/packages/cashier/src/pages/withdrawal/index.js b/packages/cashier/src/pages/withdrawal/index.js
deleted file mode 100644
index 726d0870ab30..000000000000
--- a/packages/cashier/src/pages/withdrawal/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Withdrawal from './withdrawal.jsx';
-
-export default Withdrawal;
diff --git a/packages/cashier/src/pages/withdrawal/index.ts b/packages/cashier/src/pages/withdrawal/index.ts
new file mode 100644
index 000000000000..6c67413ae062
--- /dev/null
+++ b/packages/cashier/src/pages/withdrawal/index.ts
@@ -0,0 +1,3 @@
+import Withdrawal from './withdrawal';
+
+export default Withdrawal;
diff --git a/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.js b/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx
similarity index 96%
rename from packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.js
rename to packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx
index 57f930192df0..ecaba4b8cca6 100644
--- a/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.js
+++ b/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
-import Withdraw from '../withdraw.jsx';
+import Withdraw from '../withdraw';
jest.mock('Stores/connect', () => ({
__esModule: true,
diff --git a/packages/cashier/src/pages/withdrawal/withdraw/index.js b/packages/cashier/src/pages/withdrawal/withdraw/index.js
deleted file mode 100644
index a422707c22f7..000000000000
--- a/packages/cashier/src/pages/withdrawal/withdraw/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Withdraw from './withdraw.jsx';
-
-export default Withdraw;
diff --git a/packages/cashier/src/pages/withdrawal/withdraw/index.ts b/packages/cashier/src/pages/withdrawal/withdraw/index.ts
new file mode 100644
index 000000000000..8a6a989c41d7
--- /dev/null
+++ b/packages/cashier/src/pages/withdrawal/withdraw/index.ts
@@ -0,0 +1,3 @@
+import Withdraw from './withdraw';
+
+export default Withdraw;
diff --git a/packages/cashier/src/pages/withdrawal/withdraw/withdraw.jsx b/packages/cashier/src/pages/withdrawal/withdraw/withdraw.tsx
similarity index 69%
rename from packages/cashier/src/pages/withdrawal/withdraw/withdraw.jsx
rename to packages/cashier/src/pages/withdrawal/withdraw/withdraw.tsx
index c317ea28d6b2..7fc77b2eda52 100644
--- a/packages/cashier/src/pages/withdrawal/withdraw/withdraw.jsx
+++ b/packages/cashier/src/pages/withdrawal/withdraw/withdraw.tsx
@@ -1,7 +1,18 @@
-import PropTypes from 'prop-types';
import React from 'react';
-import { connect } from 'Stores/connect';
import { Real } from 'Components/cashier-container';
+import { connect } from 'Stores/connect';
+import { TClientStore, TRootStore } from 'Types';
+
+type TWithdrawProps = {
+ container: string;
+ iframe_height: number | string;
+ iframe_url: string;
+ is_loading: boolean;
+ verification_code: string;
+ clearIframe: () => void;
+ onMount: (verification_code: TClientStore['verification_code']['payment_withdraw']) => void;
+ setActiveTab: (container: string) => void;
+};
const Withdraw = ({
container,
@@ -12,7 +23,7 @@ const Withdraw = ({
onMount,
setActiveTab,
verification_code,
-}) => {
+}: TWithdrawProps) => {
React.useEffect(() => {
setActiveTab(container);
onMount(verification_code);
@@ -24,18 +35,7 @@ const Withdraw = ({
);
};
-Withdraw.propTypes = {
- clearIframe: PropTypes.func,
- container: PropTypes.string,
- iframe_height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- iframe_url: PropTypes.string,
- is_loading: PropTypes.bool,
- onMount: PropTypes.func,
- setActiveTab: PropTypes.func,
- verification_code: PropTypes.string,
-};
-
-export default connect(({ client, modules }) => ({
+export default connect(({ client, modules }: TRootStore) => ({
container: modules.cashier.withdraw.container,
iframe_height: modules.cashier.iframe.iframe_height,
iframe_url: modules.cashier.iframe.iframe_url,
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.js b/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx
similarity index 62%
rename from packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.js
rename to packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx
index 3dbfdc6e4430..4b93a7448487 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.js
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx
@@ -2,9 +2,10 @@ import React from 'react';
import { Router } from 'react-router';
import { createBrowserHistory } from 'history';
import { fireEvent, render, screen } from '@testing-library/react';
-import { Checklist } from '@deriv/components';
import { routes } from '@deriv/shared';
-import WithdrawalLocked from '../withdrawal-locked.jsx';
+import WithdrawalLocked from '../withdrawal-locked';
+
+type TStatus = 'document' | 'none' | 'pending' | '';
jest.mock('Stores/connect', () => ({
__esModule: true,
@@ -12,20 +13,32 @@ jest.mock('Stores/connect', () => ({
connect: () => Component => Component,
}));
-jest.mock('Components/cashier-locked', () => () =>
CashierLocked
);
+jest.mock('Components/cashier-locked', () => jest.fn(() => 'CashierLocked'));
-const fireButtonEvent = (container, text_content) => {
- const node_list = container.querySelectorAll('.dc-checklist__item');
- let node = Array.from(node_list).find(node => {
- if (node.textContent === text_content) {
- return node;
+const fireButtonEvent = (button: 'proof_of_identity_btn' | 'proof_of_address_btn' | 'financial_assessment_btn') => {
+ const [proof_of_identity_btn, proof_of_address_btn, financial_assessment_btn] = screen.getAllByTestId(
+ 'dt_checklist_item_status_action'
+ );
+ switch (button) {
+ case 'proof_of_identity_btn': {
+ fireEvent.click(proof_of_identity_btn);
+ break;
}
- });
- const btn = node.querySelector('.dc-checklist__item-status--action');
- fireEvent.click(btn);
+ case 'proof_of_address_btn': {
+ fireEvent.click(proof_of_address_btn);
+ break;
+ }
+ case 'financial_assessment_btn': {
+ fireEvent.click(financial_assessment_btn);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
};
-const setAccountStatus = (identity_status, document_status, needs_verification) => {
+const setAccountStatus = (identity_status: TStatus, document_status: TStatus, needs_verification: TStatus) => {
return {
authentication: {
identity: {
@@ -40,92 +53,72 @@ const setAccountStatus = (identity_status, document_status, needs_verification)
};
describe('WithdrawalLocked', () => {
+ const history = createBrowserHistory();
it('Should show "Check proof of identity document verification status" message and redirect to account/proof-of-identity when "-->" button clicked', () => {
- const history = createBrowserHistory();
const need_poi_account_status = setAccountStatus('pending', '', '');
- const { container } = render(
+ render(
);
- fireButtonEvent(container, 'Check proof of identity document verification status');
+ fireButtonEvent('proof_of_identity_btn');
expect(history.location.pathname).toBe(routes.proof_of_identity);
});
it('Should show "Upload a proof of identity to verify your identity" message and redirect to account/proof-of-identity when "-->" button clicked', () => {
- const history = createBrowserHistory();
const need_poi_account_status = setAccountStatus('none', '', '');
- const { container } = render(
+ render(
);
- fireButtonEvent(container, 'Upload a proof of identity to verify your identity');
+ fireButtonEvent('proof_of_identity_btn');
expect(history.location.pathname).toBe(routes.proof_of_identity);
});
it('Should show "Check proof of address document verification status" message and redirect to account/proof_of_address when "-->" button clicked', () => {
- const history = createBrowserHistory();
const need_poa_account_status = setAccountStatus('', 'pending', 'document');
- const { container } = render(
+ render(
);
- fireButtonEvent(container, 'Check proof of address document verification status');
+ fireButtonEvent('proof_of_address_btn');
expect(history.location.pathname).toBe(routes.proof_of_address);
});
it('Should show "Upload a proof of address to verify your address" message and redirect to account/proof_of_address when "-->" button clicked', () => {
- const history = createBrowserHistory();
const need_poa_account_status = setAccountStatus('', 'none', 'document');
- const { container } = render(
+ render(
);
- fireButtonEvent(container, 'Upload a proof of address to verify your address');
+ fireButtonEvent('proof_of_address_btn');
expect(history.location.pathname).toBe(routes.proof_of_address);
});
it('Should show "Complete the financial assessment form" message and redirect to account/financial_assessment when "-->" button clicked', () => {
- const history = createBrowserHistory();
const account_status = setAccountStatus('', '', '');
- const { container } = render(
+ render(
);
- fireButtonEvent(container, 'Complete the financial assessment form');
+ fireButtonEvent('financial_assessment_btn');
expect(history.location.pathname).toBe(routes.financial_assessment);
});
- it('Should trigger click on the checklist item', () => {
- const onClick = jest.fn();
- const items = [
- {
- content: 'Complete the financial assessment form',
- status: 'action',
- onClick: onClick,
- },
- ];
- const { container } = render(
);
- const btn = container.querySelector('.dc-checklist__item-status--action');
-
- fireEvent.click(btn);
- expect(onClick).toHaveBeenCalled();
- });
-
it('should render
component', () => {
const account_status = setAccountStatus('', '', '');
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.js b/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.js
deleted file mode 100644
index 28c75b058a4d..000000000000
--- a/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import WithdrawalLocked from './withdrawal-locked.jsx';
-
-export default WithdrawalLocked;
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.ts b/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.ts
new file mode 100644
index 000000000000..c5ba4b13c4c6
--- /dev/null
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-locked/index.ts
@@ -0,0 +1,3 @@
+import WithdrawalLocked from './withdrawal-locked';
+
+export default WithdrawalLocked;
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.jsx b/packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.tsx
similarity index 84%
rename from packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.jsx
rename to packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.tsx
index b5b4e9235ffd..ba260b169843 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.jsx
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-locked/withdrawal-locked.tsx
@@ -1,21 +1,33 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
-import { routes } from '@deriv/shared';
import { Icon, Checklist, Text } from '@deriv/components';
import { localize, Localize } from '@deriv/translations';
+import { routes } from '@deriv/shared';
import { connect } from 'Stores/connect';
+import { TClientStore, TRootStore } from 'Types';
import CashierLocked from 'Components/cashier-locked';
-const WithdrawalLocked = ({ account_status, is_10K_limit, is_ask_financial_risk_approval }) => {
+type TWithdrawalLockedProps = {
+ account_status: Required
;
+ is_10K_limit: boolean;
+ is_ask_financial_risk_approval: boolean;
+};
+
+type TItem = {
+ content: string;
+ status: string;
+ onClick: () => void;
+};
+
+const WithdrawalLocked = ({ account_status, is_10K_limit, is_ask_financial_risk_approval }: TWithdrawalLockedProps) => {
const { document, identity, needs_verification } = account_status.authentication;
- const is_poi_needed = is_10K_limit && identity.status !== 'verified';
- const has_poi_submitted = identity.status !== 'none';
+ const is_poi_needed = is_10K_limit && identity?.status !== 'verified';
+ const has_poi_submitted = identity?.status !== 'none';
const is_poa_needed = is_10K_limit && (needs_verification.includes('document') || document?.status !== 'verified');
const has_poa_submitted = document?.status !== 'none';
const is_ask_financial_risk_approval_needed = is_10K_limit && is_ask_financial_risk_approval;
const history = useHistory();
- const items = [
+ const items: TItem[] = [
...(is_poi_needed
? [
{
@@ -70,13 +82,7 @@ const WithdrawalLocked = ({ account_status, is_10K_limit, is_ask_financial_risk_
);
};
-WithdrawalLocked.propTypes = {
- account_status: PropTypes.object,
- is_10K_limit: PropTypes.bool,
- is_ask_financial_risk_approval: PropTypes.bool,
-};
-
-export default connect(({ modules, client }) => ({
+export default connect(({ modules, client }: TRootStore) => ({
account_status: client.account_status,
is_10K_limit: modules.cashier.withdraw.is_10k_withdrawal_limit_reached,
is_ask_financial_risk_approval: modules.cashier.withdraw.error.is_ask_financial_risk_approval,
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.js b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.tsx
similarity index 51%
rename from packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.js
rename to packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.tsx
index 7ebc64478250..139f075d3a65 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.js
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/__tests__/withdrawal-verification-email.spec.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
-import WithdrawalVerificationEmail from '../withdrawal-verification-email.jsx';
+import WithdrawalVerificationEmail from '../withdrawal-verification-email';
jest.mock('Stores/connect.js', () => ({
__esModule: true,
@@ -8,30 +8,29 @@ jest.mock('Stores/connect.js', () => ({
connect: () => Component => Component,
}));
+jest.mock('Components/verification-email', () => jest.fn(() => 'VerificationEmail'));
+
describe('', () => {
- const recentTransactionOnMount = jest.fn();
- const sendVerificationEmail = jest.fn();
+ const props = {
+ is_email_sent: true,
+ recentTransactionOnMount: jest.fn(),
+ sendVerificationEmail: jest.fn(),
+ };
it('component should be rendered', () => {
- const { container } = render(
-
- );
+ render();
- expect(container.querySelector('.cashier__wrapper')).toBeInTheDocument();
+ expect(screen.getByTestId('dt_cashier_wrapper')).toBeInTheDocument();
});
- it(" component should be rendered when 'is_email_sent' prop is true", () => {
- const { container } = render(
-
- );
+ it(" component should be rendered when 'is_email_sent' prop is true", () => {
+ render();
- expect(container.querySelector('.verification-email')).toBeInTheDocument();
+ expect(screen.getByText('VerificationEmail')).toBeInTheDocument();
});
it("React.Fragment should be rendered when 'is_email_sent' prop is false", () => {
- render(
-
- );
+ render();
expect(screen.getByText('Please help us verify your withdrawal request.')).toBeInTheDocument();
expect(
@@ -43,25 +42,17 @@ describe('', () => {
});
it("'Send email' button should be rendered when 'is_email_sent' prop is false", () => {
- render(
-
- );
+ render();
expect(screen.getByRole('button', { name: 'Send email' })).toBeInTheDocument();
});
it("sendVerificationEmail func should be triggered when click on 'Send email' button", () => {
- render(
-
- );
+ render();
const btn = screen.getByRole('button', { name: 'Send email' });
fireEvent.click(btn);
- expect(sendVerificationEmail).toHaveBeenCalled();
+ expect(props.sendVerificationEmail).toHaveBeenCalled();
});
});
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.js b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.ts
similarity index 87%
rename from packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.js
rename to packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.ts
index 5def41e3d2ee..c9889c704e79 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.js
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/index.ts
@@ -1,3 +1,3 @@
-import WithdrawalVerificationEmail from './withdrawal-verification-email.jsx';
+import WithdrawalVerificationEmail from './withdrawal-verification-email';
export default WithdrawalVerificationEmail;
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.jsx b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx
similarity index 83%
rename from packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.jsx
rename to packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx
index 84fcf5125615..c90fb5ea5234 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.jsx
+++ b/packages/cashier/src/pages/withdrawal/withdrawal-verification-email/withdrawal-verification-email.tsx
@@ -1,36 +1,45 @@
-import PropTypes from 'prop-types';
import React from 'react';
import { Button, Icon, MobileWrapper, Text } from '@deriv/components';
import { isCryptocurrency, isMobile } from '@deriv/shared';
import { localize, Localize } from '@deriv/translations';
import { connect } from 'Stores/connect';
+import { TClientStore, TCryptoTransactionDetails, TRootStore } from 'Types';
import RecentTransaction from 'Components/recent-transaction';
import VerificationEmail from 'Components/verification-email';
import './withdrawal-verification-email.scss';
+type TWithdrawalVerificationEmailProps = {
+ crypto_transactions: TCryptoTransactionDetails[];
+ currency: TClientStore['currency'];
+ is_email_sent: boolean;
+ is_resend_clicked: boolean;
+ recentTransactionOnMount: () => void;
+ resendVerificationEmail: () => void;
+ setIsResendClicked: (value: boolean) => void;
+ sendVerificationEmail: () => void;
+};
+
const WithdrawalVerificationEmail = ({
crypto_transactions,
currency,
is_email_sent,
is_resend_clicked,
- resendVerificationEmail,
recentTransactionOnMount,
- sendVerificationEmail,
+ resendVerificationEmail,
setIsResendClicked,
-}) => {
+ sendVerificationEmail,
+}: TWithdrawalVerificationEmailProps) => {
React.useEffect(() => {
recentTransactionOnMount();
}, [recentTransactionOnMount]);
return (
-
+
{is_email_sent ? (
) : (
@@ -62,18 +71,7 @@ const WithdrawalVerificationEmail = ({
);
};
-WithdrawalVerificationEmail.propTypes = {
- crypto_transactions: PropTypes.array,
- currency: PropTypes.string,
- is_email_sent: PropTypes.bool,
- is_resend_clicked: PropTypes.bool,
- recentTransactionOnMount: PropTypes.func,
- resendVerificationEmail: PropTypes.func,
- sendVerificationEmail: PropTypes.func,
- setIsResendClicked: PropTypes.func,
-};
-
-export default connect(({ client, modules }) => ({
+export default connect(({ client, modules }: TRootStore) => ({
crypto_transactions: modules.cashier.transaction_history.crypto_transactions,
currency: client.currency,
is_email_sent: modules.cashier.withdraw.verification.is_email_sent,
diff --git a/packages/cashier/src/pages/withdrawal/withdrawal.jsx b/packages/cashier/src/pages/withdrawal/withdrawal.tsx
similarity index 78%
rename from packages/cashier/src/pages/withdrawal/withdrawal.jsx
rename to packages/cashier/src/pages/withdrawal/withdrawal.tsx
index ad3a209e80cb..193d4ceec37b 100644
--- a/packages/cashier/src/pages/withdrawal/withdrawal.jsx
+++ b/packages/cashier/src/pages/withdrawal/withdrawal.tsx
@@ -1,24 +1,78 @@
-import PropTypes from 'prop-types';
import React from 'react';
import { Loading } from '@deriv/components';
import { Localize } from '@deriv/translations';
import { isCryptocurrency, isDesktop } from '@deriv/shared';
import { connect } from 'Stores/connect';
+import { TClientStore, TCryptoTransactionDetails, TRootStore } from 'Types';
+import CryptoTransactionsHistory from 'Components/crypto-transactions-history';
import CryptoWithdrawForm from './crypto-withdraw-form';
import CryptoWithdrawReceipt from './crypto-withdraw-receipt';
import Withdraw from './withdraw';
+import WithdrawalLocked from './withdrawal-locked';
import WithdrawalVerificationEmail from './withdrawal-verification-email';
+import CashierLocked from 'Components/cashier-locked';
import Error from 'Components/error';
import NoBalance from 'Components/no-balance';
-import { Virtual } from 'Components/cashier-container';
-import WithdrawalLocked from './withdrawal-locked';
-import CashierLocked from 'Components/cashier-locked';
+import RecentTransaction from 'Components/recent-transaction';
import SideNote from 'Components/side-note';
import USDTSideNote from 'Components/usdt-side-note';
-import CryptoTransactionsHistory from 'Components/crypto-transactions-history';
-import RecentTransaction from 'Components/recent-transaction';
+import { Virtual } from 'Components/cashier-container';
-const WithdrawalSideNote = ({ is_mobile, currency }) => {
+type TErrorFull = {
+ code?: string;
+ fields?: string;
+ is_ask_authentication: boolean;
+ is_ask_financial_risk_approval: boolean;
+ is_ask_uk_funds_protection: boolean;
+ is_self_exclusion_max_turnover_set: boolean;
+ is_show_full_page: boolean | null;
+ message?: string;
+ onClickButton?: () => void | null;
+};
+
+type TErrorShort = {
+ code: string;
+ message: string;
+};
+
+type TWithdrawalSideNoteProps = {
+ currency: string;
+ is_mobile?: boolean;
+};
+
+type TWithdrawalProps = {
+ balance: TClientStore['balance'];
+ container: string;
+ crypto_transactions: TCryptoTransactionDetails[];
+ current_currency_type: TClientStore['current_currency_type'];
+ currency: TClientStore['currency'];
+ error: TErrorFull;
+ iframe_url: string;
+ is_10k_withdrawal_limit_reached: boolean;
+ is_cashier_locked: boolean;
+ is_crypto: boolean;
+ is_crypto_transactions_visible: boolean;
+ is_switching: TClientStore['is_switching'];
+ is_system_maintenance: boolean;
+ is_virtual: TClientStore['is_virtual'];
+ is_withdraw_confirmed: boolean;
+ is_withdrawal_locked: boolean;
+ tab_index: number;
+ verification_code: TClientStore['verification_code']['payment_withdraw'];
+ verify_error: TErrorFull;
+ check10kLimit: () => void;
+ setActiveTab: (container: string) => void;
+ setErrorMessage: (
+ error: TErrorShort | string,
+ onClickButton?: () => void | null,
+ is_show_full_page?: boolean | null
+ ) => void;
+ setSideNotes: (notes: (JSX.Element | JSX.Element[])[] | null) => void;
+ willMountWithdraw: (verification_code: TClientStore['verification_code']['payment_withdraw']) => void;
+ recentTransactionOnMount: () => void;
+};
+
+const WithdrawalSideNote = ({ is_mobile, currency }: TWithdrawalSideNoteProps) => {
const notes = [
{
+}: TWithdrawalProps) => {
React.useEffect(() => {
if (!is_crypto_transactions_visible) {
recentTransactionOnMount();
@@ -125,7 +179,7 @@ const Withdrawal = ({
if (is_withdrawal_locked || is_10k_withdrawal_limit_reached) {
return ;
}
- if (!+balance) {
+ if (!Number(balance)) {
return (
<>
@@ -165,35 +219,7 @@ const Withdrawal = ({
);
};
-Withdrawal.propTypes = {
- balance: PropTypes.string,
- check10kLimit: PropTypes.func,
- container: PropTypes.string,
- crypto_transactions: PropTypes.array,
- currency: PropTypes.string,
- current_currency_type: PropTypes.string,
- error: PropTypes.object,
- iframe_url: PropTypes.string,
- is_10k_withdrawal_limit_reached: PropTypes.bool,
- is_cashier_locked: PropTypes.bool,
- is_crypto: PropTypes.bool,
- is_crypto_transactions_visible: PropTypes.bool,
- is_switching: PropTypes.bool,
- is_system_maintenance: PropTypes.bool,
- is_virtual: PropTypes.bool,
- is_withdraw_confirmed: PropTypes.bool,
- is_withdrawal_locked: PropTypes.bool,
- recentTransactionOnMount: PropTypes.func,
- setActiveTab: PropTypes.func,
- setErrorMessage: PropTypes.func,
- setSideNotes: PropTypes.func,
- tab_index: PropTypes.number,
- verification_code: PropTypes.string,
- verify_error: PropTypes.object,
- willMountWithdraw: PropTypes.func,
-};
-
-export default connect(({ client, modules }) => ({
+export default connect(({ client, modules }: TRootStore) => ({
balance: client.balance,
check10kLimit: modules.cashier.withdraw.check10kLimit,
container: modules.cashier.withdraw.container,
diff --git a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.js b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.js
index aa1b415fd500..d06fd234a59a 100644
--- a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.js
+++ b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.js
@@ -84,7 +84,7 @@ beforeEach(() => {
],
}),
},
- balanceAll: jest.fn().mockResolvedValue(),
+ balanceAll: jest.fn().mockResolvedValue({ balance_response: { balance: '20' } }),
mt5LoginList: jest.fn().mockResolvedValue(),
tradingPlatformAccountsList: jest.fn().mockResolvedValue({
trading_platform_accounts: [
diff --git a/packages/cashier/src/stores/connect.js b/packages/cashier/src/stores/connect.js
index 4ef42c8d18b6..ffa0905ef038 100644
--- a/packages/cashier/src/stores/connect.js
+++ b/packages/cashier/src/stores/connect.js
@@ -1,4 +1,4 @@
-import { useObserver } from 'mobx-react';
+import { useObserver } from 'mobx-react-lite';
import React from 'react';
const isClassComponent = Component =>
diff --git a/packages/cashier/src/stores/general-store.js b/packages/cashier/src/stores/general-store.js
index 6201f7d6b11c..62adf97f2295 100644
--- a/packages/cashier/src/stores/general-store.js
+++ b/packages/cashier/src/stores/general-store.js
@@ -51,6 +51,7 @@ export default class GeneralStore extends BaseStore {
@observable percentage = 0;
@observable show_p2p_in_cashier_onboarding = false;
@observable onRemount = () => {};
+ @observable p2p_completed_orders = null;
active_container = Constants.containers.deposit;
is_populating_values = false;
@@ -263,6 +264,21 @@ export default class GeneralStore extends BaseStore {
this.setIsP2pVisible(!(is_p2p_restricted || this.root_store.client.is_virtual));
}
+ @action.bound
+ setP2pCompletedOrders(p2p_completed_orders) {
+ this.p2p_completed_orders = p2p_completed_orders;
+ }
+
+ @action.bound
+ async getP2pCompletedOrders() {
+ await this.WS.authorized.send({ p2p_order_list: 1, active: 0 }).then(response => {
+ if (!response?.error) {
+ const { p2p_order_list } = response;
+ this.setP2pCompletedOrders(p2p_order_list.list);
+ }
+ });
+ }
+
@action.bound
async onMountCommon(should_remount) {
const { client, common, modules } = this.root_store;
diff --git a/packages/cashier/src/types/index.ts b/packages/cashier/src/types/index.ts
index 987e02965be1..7a659e40a4a7 100644
--- a/packages/cashier/src/types/index.ts
+++ b/packages/cashier/src/types/index.ts
@@ -1,3 +1,2 @@
-export * from './crypto-transaction-details.types';
-export * from './props.types';
-export * from './stores.types';
+export * from './shared';
+export * from './stores';
diff --git a/packages/cashier/src/types/shared/account.types.ts b/packages/cashier/src/types/shared/account.types.ts
new file mode 100644
index 000000000000..07f42dadb904
--- /dev/null
+++ b/packages/cashier/src/types/shared/account.types.ts
@@ -0,0 +1,26 @@
+/* -------------------------------------------------------------------------- */
+/* ACCOUNT TYPES */
+/* -------------------------------------------------------------------------- */
+import { DetailsOfEachMT5Loginid } from '@deriv/api-types';
+
+export type TAccount = {
+ balance?: string | number;
+ currency?: string;
+ disabled?: boolean;
+ is_dxtrade?: boolean;
+ is_mt?: boolean;
+ market_type?: string;
+ nativepicker_text: string;
+ platform_icon?: string;
+ text: JSX.Element | string;
+ value?: string;
+};
+
+export type TMt5LoginList = Array;
+
+export type TAccountsList = {
+ mt5_login_list: TMt5LoginList;
+ account: TAccount;
+ idx: string | number;
+ is_dark_mode_on: boolean;
+};
diff --git a/packages/cashier/src/types/crypto-transaction-details.types.ts b/packages/cashier/src/types/shared/crypto-transaction-details.types.ts
similarity index 100%
rename from packages/cashier/src/types/crypto-transaction-details.types.ts
rename to packages/cashier/src/types/shared/crypto-transaction-details.types.ts
diff --git a/packages/cashier/src/types/shared/error.types.ts b/packages/cashier/src/types/shared/error.types.ts
new file mode 100644
index 000000000000..bb50f607fd69
--- /dev/null
+++ b/packages/cashier/src/types/shared/error.types.ts
@@ -0,0 +1,19 @@
+import { TServerError } from 'Types';
+
+// Type of the instance of the ErrorStore
+export type TError = {
+ code?: string;
+ fields: string;
+ is_ask_authentication: boolean;
+ is_ask_financial_risk_approval: boolean;
+ is_ask_uk_funds_protection: boolean;
+ is_self_exclusion_max_turnover_set: boolean;
+ is_show_full_page: boolean;
+ message?: string;
+ onClickButton: (() => void) | null;
+ setErrorMessage: (
+ error: TServerError | string,
+ onClickButton: TError['onClickButton'],
+ is_show_full_page: boolean
+ ) => void;
+};
diff --git a/packages/cashier/src/types/shared/index.ts b/packages/cashier/src/types/shared/index.ts
new file mode 100644
index 000000000000..14797bc9e716
--- /dev/null
+++ b/packages/cashier/src/types/shared/index.ts
@@ -0,0 +1,7 @@
+export * from './account.types';
+export * from './crypto-transaction-details.types';
+export * from './error.types';
+export * from './props.types';
+export * from './provider.types';
+export * from './routes.types';
+export * from './websocket.types';
diff --git a/packages/cashier/src/types/props.types.ts b/packages/cashier/src/types/shared/props.types.ts
similarity index 77%
rename from packages/cashier/src/types/props.types.ts
rename to packages/cashier/src/types/shared/props.types.ts
index 534c46656d97..db4b7ff1f5d6 100644
--- a/packages/cashier/src/types/props.types.ts
+++ b/packages/cashier/src/types/shared/props.types.ts
@@ -7,3 +7,5 @@ export type TReactMouseEvent = React.MouseEvent;
export type TReactFormEvent = React.FormEvent;
export type TReactElement = React.ReactElement;
+
+export type TSideNotesProps = Array | null;
diff --git a/packages/cashier/src/types/shared/provider.types.ts b/packages/cashier/src/types/shared/provider.types.ts
new file mode 100644
index 000000000000..e15d03ba6026
--- /dev/null
+++ b/packages/cashier/src/types/shared/provider.types.ts
@@ -0,0 +1,22 @@
+import { MutableRefObject } from 'react';
+
+export type TProviderDetails = {
+ icon: {
+ dark: string;
+ light: string;
+ };
+ name: string;
+ getDescription: () => string;
+ getAllowedResidencies: () => string[];
+ getPaymentIcons: () => {
+ dark: string;
+ light: string;
+ }[];
+ getScriptDependencies: () => any[];
+ getDefaultFromCurrency: () => string;
+ getFromCurrencies: () => string;
+ getToCurrencies: () => string;
+ getWidgetHtml: () => Promise;
+ onMountWidgetContainer: (ref?: MutableRefObject) => void;
+ should_show_deposit_address: boolean;
+};
diff --git a/packages/cashier/src/types/shared/routes.types.ts b/packages/cashier/src/types/shared/routes.types.ts
new file mode 100644
index 000000000000..5828c7d951e4
--- /dev/null
+++ b/packages/cashier/src/types/shared/routes.types.ts
@@ -0,0 +1,20 @@
+import { Redirect } from 'react-router-dom';
+import { TPage404 } from 'Constants/routes-config';
+
+export type TRoute = {
+ default?: boolean;
+ exact?: boolean;
+ id?: string;
+ icon_component?: string;
+ is_invisible?: boolean;
+ path?: string;
+ to?: string;
+ component: ((cashier_routes?: TRoute[]) => JSX.Element) | TPage404 | typeof Redirect;
+ getTitle: () => string;
+};
+
+export type TRouteConfig = TRoute & {
+ is_modal?: boolean;
+ is_authenticated?: boolean;
+ routes?: TRoute[];
+};
diff --git a/packages/cashier/src/types/shared/websocket.types.ts b/packages/cashier/src/types/shared/websocket.types.ts
new file mode 100644
index 000000000000..6e8b81bc6745
--- /dev/null
+++ b/packages/cashier/src/types/shared/websocket.types.ts
@@ -0,0 +1,5 @@
+export type TServerError = {
+ code: string;
+ message: string;
+ details?: { [key: string]: string };
+};
diff --git a/packages/cashier/src/types/stores.types.ts b/packages/cashier/src/types/stores.types.ts
deleted file mode 100644
index 67d54fc25e06..000000000000
--- a/packages/cashier/src/types/stores.types.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export class RootStore {
- client: any;
- common: any;
- modules: any;
- ui: any;
- constructor(core_store: { client: any; common: any; modules: any; ui: any }) {
- this.client = core_store.client;
- this.common = core_store.common;
- this.modules = core_store.modules;
- this.ui = core_store.ui;
- }
-}
diff --git a/packages/cashier/src/types/stores/client-store.types.ts b/packages/cashier/src/types/stores/client-store.types.ts
new file mode 100644
index 000000000000..3e24813784dd
--- /dev/null
+++ b/packages/cashier/src/types/stores/client-store.types.ts
@@ -0,0 +1,51 @@
+import { GetAccountStatus, Authorize, DetailsOfEachMT5Loginid } from '@deriv/api-types';
+
+type TAccount = NonNullable[0];
+
+export type TClientStore = {
+ accounts: { [k: string]: TAccount };
+ account_limits: {
+ daily_transfers?: {
+ [k: string]: {
+ allowed: boolean;
+ available: boolean;
+ };
+ };
+ };
+ account_status: GetAccountStatus;
+ balance?: string;
+ currency: string;
+ current_currency_type?: string;
+ current_fiat_currency?: string;
+ getLimits: () => void;
+ is_account_setting_loaded: boolean;
+ is_deposit_lock: boolean;
+ is_dxtrade_allowed: boolean;
+ is_financial_account: boolean;
+ is_financial_information_incomplete: boolean;
+ is_trading_experience_incomplete: boolean;
+ is_identity_verification_needed: boolean;
+ is_logged_in: boolean;
+ is_logging_in: boolean;
+ is_switching: boolean;
+ is_virtual: boolean;
+ is_withdrawal_lock: boolean;
+ local_currency_config: {
+ currency: string;
+ decimal_places?: number;
+ };
+ loginid?: string;
+ mt5_login_list: Array;
+ residence: string;
+ switchAccount: (value?: string) => void;
+ verification_code: {
+ payment_agent_withdraw: string;
+ payment_withdraw: string;
+ request_email: string;
+ reset_password: string;
+ signup: string;
+ system_email_change: string;
+ trading_platform_dxtrade_password_reset: string;
+ trading_platform_mt5_password_reset: string;
+ };
+};
diff --git a/packages/cashier/src/types/stores/common-store.types.ts b/packages/cashier/src/types/stores/common-store.types.ts
new file mode 100644
index 000000000000..86e6efb0041c
--- /dev/null
+++ b/packages/cashier/src/types/stores/common-store.types.ts
@@ -0,0 +1,22 @@
+import { RouteComponentProps } from 'react-router';
+
+type TError = {
+ header: string | JSX.Element;
+ message: string | JSX.Element;
+ type?: string;
+ redirect_label: string;
+ redirect_to: string;
+ should_clear_error_on_click: boolean;
+ should_show_refresh: boolean;
+ redirectOnClick: () => void;
+ setError: (has_error: boolean, error: TError | null) => void;
+};
+
+export type TCommonStore = {
+ error: TError;
+ is_from_derivgo: boolean;
+ has_error: boolean;
+ platform: string;
+ routeBackInApp: (history: Pick, additional_platform_path?: string[]) => void;
+ routeTo: (pathname: string) => void;
+};
diff --git a/packages/cashier/src/types/stores/index.ts b/packages/cashier/src/types/stores/index.ts
new file mode 100644
index 000000000000..6acec19f6868
--- /dev/null
+++ b/packages/cashier/src/types/stores/index.ts
@@ -0,0 +1,4 @@
+export * from './client-store.types';
+export * from './common-store.types';
+export * from './root-store.types';
+export * from './ui-store.types';
diff --git a/packages/cashier/src/types/stores/root-store.types.ts b/packages/cashier/src/types/stores/root-store.types.ts
new file mode 100644
index 000000000000..323579fd932f
--- /dev/null
+++ b/packages/cashier/src/types/stores/root-store.types.ts
@@ -0,0 +1,12 @@
+import { TClientStore } from './client-store.types';
+import { TCommonStore } from './common-store.types';
+import { TUiStore } from './ui-store.types';
+
+export type RootStore = {
+ client: TClientStore;
+ common: TCommonStore;
+ modules: any;
+ ui: TUiStore;
+};
+
+export type TRootStore = RootStore;
diff --git a/packages/cashier/src/types/stores/ui-store.types.ts b/packages/cashier/src/types/stores/ui-store.types.ts
new file mode 100644
index 000000000000..078110fee212
--- /dev/null
+++ b/packages/cashier/src/types/stores/ui-store.types.ts
@@ -0,0 +1,11 @@
+export type TUiStore = {
+ current_focus: string | null;
+ is_cashier_visible: boolean;
+ is_dark_mode_on: boolean;
+ is_mobile: boolean;
+ disableApp: () => void;
+ enableApp: () => void;
+ setCurrentFocus: (value: string) => void;
+ toggleAccountsDialog: () => void;
+ toggleCashier: () => void;
+};
diff --git a/packages/cashier/src/_common/server_time.js b/packages/cashier/src/utils/server_time.js
similarity index 100%
rename from packages/cashier/src/_common/server_time.js
rename to packages/cashier/src/utils/server_time.js
diff --git a/packages/cashier/src/_common/utility.js b/packages/cashier/src/utils/utility.js
similarity index 100%
rename from packages/cashier/src/_common/utility.js
rename to packages/cashier/src/utils/utility.js
diff --git a/packages/cashier/src/utils/validator/__tests__/error.spec.js b/packages/cashier/src/utils/validator/__tests__/error.spec.js
deleted file mode 100644
index d99897f955be..000000000000
--- a/packages/cashier/src/utils/validator/__tests__/error.spec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { expect } from 'chai';
-import Errors from '../errors';
-
-describe('Error', () => {
- let errors;
- beforeEach(() => {
- errors = new Errors();
- errors.add('Error', 100);
- });
-
- describe('.add', () => {
- it('should add error to errors', () => {
- errors.add('Error', 101);
- expect(errors.errors).to.have.property('Error').with.length(2);
- });
- it('should not add error if already existed', () => {
- errors.add('Error', 100);
- expect(errors.errors).to.have.property('Error').with.length(1);
- });
- });
-
- describe('.all', () => {
- it('should return all errors', () => {
- expect(errors.all()).to.be.eql({
- Error: [100],
- });
- });
- });
-
- describe('.first', () => {
- it('should return first error if attribute exists', () => {
- expect(errors.first('Error')).to.eql(100);
- });
- });
-
- describe('.get', () => {
- it('should return data if attribute exists', () => {
- expect(errors.get('Error')).to.eql([100]);
- });
- it('should return [] if attribute does not exist', () => {
- expect(errors.get('')).to.eql([]);
- });
- });
-
- describe('.has', () => {
- it('should return true if attribute exists', () => {
- expect(errors.has('Error')).to.be.true;
- });
- it('should return false if attribute does not exists', () => {
- expect(errors.has('')).to.be.false;
- });
- });
-});
diff --git a/packages/cashier/src/utils/validator/validator.js b/packages/cashier/src/utils/validator/validator.js
index 432ed7d7adbf..a91a3822bd80 100644
--- a/packages/cashier/src/utils/validator/validator.js
+++ b/packages/cashier/src/utils/validator/validator.js
@@ -1,5 +1,5 @@
import { getPreBuildDVRs } from '@deriv/shared';
-import { template } from '_common/utility';
+import { template } from '../utility';
import Error from './errors';
class Validator {
diff --git a/packages/cfd/globals.d.ts b/packages/cfd/globals.d.ts
index d048ec16b0ef..a29d49c4b3ec 100644
--- a/packages/cfd/globals.d.ts
+++ b/packages/cfd/globals.d.ts
@@ -1,10 +1,3 @@
-declare module '@deriv/account';
-declare module '@deriv/bot-skeleton';
-declare module '@deriv/bot-web-ui';
-declare module '@deriv/cashier';
declare module '@deriv/components';
-declare module '@deriv/appstore';
-declare module '@deriv/p2p';
-declare module '@deriv/trader';
-declare module '@deriv/translations';
declare module '@deriv/shared';
+declare module '@deriv/account';
diff --git a/packages/cfd/package.json b/packages/cfd/package.json
index cbc7c252ef6f..8894b0532aa2 100644
--- a/packages/cfd/package.json
+++ b/packages/cfd/package.json
@@ -19,7 +19,7 @@
"deploy:production": "echo \"No deploy:production specified\""
},
"engines": {
- "node": "^14.17.1"
+ "node": "^16.16.0"
},
"repository": {
"type": "git",
@@ -37,6 +37,8 @@
"devDependencies": {
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
+ "@types/react": "^18.0.7",
+ "@types/react-dom": "^18.0.0",
"chai": "^4.2.0",
"circular-dependency-plugin": "^5.2.2",
"clean-webpack-plugin": "^3.0.0",
@@ -76,6 +78,7 @@
"stylelint-formatter-pretty": "^2.1.1",
"svgo": "^2.8.0",
"terser-webpack-plugin": "^5.1.1",
+ "typescript": "^4.6.3",
"webpack": "^5.46.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2",
@@ -106,6 +109,6 @@
"react-dom": "^16.14.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
- "react-transition-group": "^4.3.0"
+ "react-transition-group": "4.4.2"
}
}
diff --git a/packages/cfd/src/Components/cfd-personal-details-form.tsx b/packages/cfd/src/Components/cfd-personal-details-form.tsx
index c311c71003c5..cd767e5b45f6 100644
--- a/packages/cfd/src/Components/cfd-personal-details-form.tsx
+++ b/packages/cfd/src/Components/cfd-personal-details-form.tsx
@@ -135,15 +135,17 @@ const validatePersonalDetails = ({
has_place_of_birth,
}: TValidatePersonalDetailsParams) => {
const [tax_residence_obj] = residence_list.filter(res => res.text === values.tax_residence && res.tin_format);
- const [tin_format] = tax_residence_obj?.tin_format ?? [];
- const tin_regex = tin_format || '^[A-Za-z0-9./s-]{0,25}$'; // fallback to API's default rule check
+
+ const tin_format = tax_residence_obj?.tin_format;
+
+ const tin_regex = tin_format || ['^[A-Za-z0-9./s-]{0,25}$']; // fallback to API's default rule check
const validations: { [key: string]: ((v: string) => boolean | RegExpMatchArray | null)[] } = {
citizen: [(v: string) => !!v, (v: string) => residence_list.map(i => i.text).includes(v)],
tax_residence: [(v: string) => !!v, (v: string) => residence_list.map(i => i.text).includes(v)],
tax_identification_number: [
(v: string) => ((!values.tax_residence && is_tin_required) || tin_format ? !!v : true),
- (v: string) => (tin_regex ? v.match(tin_regex) : true),
+ (v: string) => (tin_regex ? tin_regex?.some(regex => v.match(regex)) : true),
],
account_opening_reason: [
(v: string) => !!v,
diff --git a/packages/cfd/src/Components/cfd-poi.tsx b/packages/cfd/src/Components/cfd-poi.tsx
index dd719bf4ad29..b4b0ee0db278 100644
--- a/packages/cfd/src/Components/cfd-poi.tsx
+++ b/packages/cfd/src/Components/cfd-poi.tsx
@@ -30,6 +30,8 @@ export type TCFDPOIProps = {
height: string;
is_switching: boolean;
is_virtual: boolean;
+ is_high_risk: boolean;
+ is_withdrawal_lock: boolean;
onSave: (index: number, values: TFormValues) => void;
refreshNotifications: () => void;
removeNotificationByKey: (key: TCFDNotificationByKey) => void;
@@ -67,6 +69,8 @@ export default connect(({ client, common, notifications }: RootStore) => ({
fetchResidenceList: client.fetchResidenceList,
is_switching: client.is_switching,
is_virtual: client.is_virtual,
+ is_high_risk: client.is_high_risk,
+ is_withdrawal_lock: client.is_withdrawal_lock,
refreshNotifications: notifications.refreshNotifications,
routeBackInApp: common.routeBackInApp,
should_allow_authentication: client.should_allow_authentication,
diff --git a/packages/cfd/src/Containers/__tests__/cfd-dashboard-container.spec.js b/packages/cfd/src/Containers/__tests__/cfd-dashboard-container.spec.js
new file mode 100644
index 000000000000..7e17abf89544
--- /dev/null
+++ b/packages/cfd/src/Containers/__tests__/cfd-dashboard-container.spec.js
@@ -0,0 +1,115 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import CFDDashboardContainer from '../cfd-dashboard-container';
+
+jest.mock('@deriv/components', () => {
+ const original_module = jest.requireActual('@deriv/components');
+ return {
+ ...original_module,
+ Icon: jest.fn(props => {props.icon}
),
+ };
+});
+
+describe('CFDDashboardContainer', () => {
+ const mock_props = {
+ platform: 'mt5',
+ active_index: 0,
+ is_dark_mode_on: false,
+ dxtrade_tokens: {
+ demo: '',
+ real: '',
+ },
+ };
+
+ it('should render correctly', () => {
+ render();
+ expect(screen.getByTestId(/dt_cfd_dashboard_download_center_container/i)).toBeInTheDocument();
+ });
+ it('should render correct text according to the MT5 platform', () => {
+ render();
+ expect(
+ screen.getByText(/run MT5 from your browser or download the MT5 app for your devices/i)
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(/the mt5 desktop app is not supported by windows XP, windows 2003, and windows vista/i)
+ ).toBeInTheDocument();
+ });
+ it('should show the proper icons for the MT5 platform ', () => {
+ render();
+ expect(screen.getByText(/IcMt5DeviceDesktop/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcMt5DeviceLaptop/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationWindows/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationMacos/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationLinux/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcMt5DeviceTablet/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcMt5DevicePhone/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationGoogle/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationApple/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationHuawei/i)).toBeInTheDocument();
+ });
+
+ it('should download/redirect the correct file for MT5 platform', () => {
+ render();
+ expect(screen.getByText(/IcInstallationWindows/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://download.mql5.com/cdn/web/deriv.limited/mt5/derivmt5setup.exe'
+ );
+ expect(screen.getByText(/IcInstallationMacos/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://download.mql5.com/cdn/web/metaquotes.software.corp/mt5/MetaTrader5.dmg'
+ );
+ expect(screen.getByText(/IcInstallationLinux/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://www.metatrader5.com/en/terminal/help/start_advanced/install_linux'
+ );
+ expect(screen.getByText(/IcInstallationGoogle/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://download.mql5.com/cdn/mobile/mt5/android?server=Deriv-Demo,Deriv-Server'
+ );
+ expect(screen.getByText(/IcInstallationApple/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://download.mql5.com/cdn/mobile/mt5/ios?server=Deriv-Demo,Deriv-Server'
+ );
+ expect(screen.getByText(/IcInstallationHuawei/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://appgallery.huawei.com/#/app/C102015329'
+ );
+ });
+
+ it('should render the correct icons and text for the Deriv X platform', () => {
+ render();
+ expect(screen.getByText(/IcDxtradeDeviceDesktop/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDeviceLaptop/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcBrandDxtrade/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDeviceTablet/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDevicePhone/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationGoogle/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcInstallationApple/i)).toBeInTheDocument();
+ expect(screen.getByText(/Run deriv x on your browser or download the mobile app/i)).toBeInTheDocument();
+ expect(screen.getByText(/web terminal/i)).toBeInTheDocument();
+ });
+ it('should render the correct icons if dark mode is on for DerivX', () => {
+ render();
+ expect(screen.getByText(/IcDxtradeDeviceDesktopLight/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDeviceLaptopLight/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDeviceTabletLight/i)).toBeInTheDocument();
+ expect(screen.getByText(/IcDxtradeDevicePhoneLight/i)).toBeInTheDocument();
+ });
+
+ it('should download/redirect the correct file for DerivX', () => {
+ render();
+ expect(screen.getByText(/IcBrandDxtrade/i).closest('a')).toHaveAttribute('href', 'https://dx.deriv.com');
+ expect(screen.getByText(/IcInstallationGoogle/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://play.google.com/store/apps/details?id=com.deriv.dx'
+ );
+ expect(screen.getByText(/IcInstallationApple/i).closest('a')).toHaveAttribute(
+ 'href',
+ 'https://apps.apple.com/us/app/deriv-x/id1563337503'
+ );
+ });
+ it('should render demo account dashboard and the demo link for derivx web terminal if active_index is 1 ', () => {
+ render();
+ expect(screen.getByText(/IcBrandDxtrade/i).closest('a')).toHaveAttribute('href', 'https://dx-demo.deriv.com');
+ });
+});
diff --git a/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js b/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js
new file mode 100644
index 000000000000..9fb8808584c2
--- /dev/null
+++ b/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js
@@ -0,0 +1,150 @@
+import React from 'react';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import CFDTopUpDemoModal from '../cfd-top-up-demo-modal.tsx';
+
+jest.mock('Stores/connect.js', () => ({
+ __esModule: true,
+ default: 'mockedDefaultExport',
+ connect: () => Component => Component,
+}));
+
+jest.mock('../../Components/success-dialog.jsx', () => () => Success Dialog
);
+
+describe('CFDTopUpDemoModal', () => {
+ let modal_root_el;
+
+ beforeAll(() => {
+ modal_root_el = document.createElement('div');
+ modal_root_el.setAttribute('id', 'modal_root');
+ document.body.appendChild(modal_root_el);
+ });
+ afterAll(() => {
+ document.body.removeChild(modal_root_el);
+ });
+
+ const synthetic_config = {
+ account_type: 'synthetic',
+ leverage: 500,
+ short_title: 'Synthetic',
+ };
+
+ const financial_config = {
+ account_type: 'financial',
+ leverage: 1000,
+ short_title: 'Financial',
+ };
+
+ const mock_props = {
+ dxtrade_companies: {},
+ mt5_companies: {
+ demo: {
+ synthetic: {
+ mt5_account_type: synthetic_config.account_type,
+ leverage: synthetic_config.leverage,
+ title: 'Demo Synthetic',
+ short_title: synthetic_config.short_title,
+ },
+ financial: {
+ mt5_account_type: financial_config.account_type,
+ leverage: financial_config.leverage,
+ title: 'Demo Financial',
+ short_title: financial_config.short_title,
+ },
+ },
+ real: {
+ synthetic: {
+ mt5_account_type: synthetic_config.account_type,
+ leverage: synthetic_config.leverage,
+ title: 'Synthetic',
+ short_title: synthetic_config.short_title,
+ },
+ financial: {
+ mt5_account_type: financial_config.account_type,
+ leverage: financial_config.leverage,
+ title: 'CFDs',
+ short_title: financial_config.short_title,
+ },
+ },
+ },
+ current_account: { category: 'demo', type: 'financial', balance: '700', display_balance: '700' },
+ closeSuccessTopUpModal: jest.fn(),
+ closeTopUpModal: jest.fn(),
+ is_top_up_virtual_open: true,
+ is_top_up_virtual_in_progress: false,
+ is_top_up_virtual_success: false,
+ platform: 'test platform',
+ topUpVirtual: jest.fn(),
+ };
+ it('should render the button texts correctly', () => {
+ render();
+ expect(screen.getByText('Fund top up')).toBeInTheDocument();
+ expect(screen.getByText('Current balance')).toBeInTheDocument();
+ expect(screen.getByText('Demo Financial account')).toBeInTheDocument();
+ expect(screen.queryByTestId('dt_top_up_virtual_description')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /Top up/i })).toBeInTheDocument();
+ });
+
+ it('should render the proper balance in the current balance', () => {
+ render();
+ expect(screen.getByText('700.00')).toBeInTheDocument();
+ });
+
+ it('should disable the top up button if the balance is higher than 1000 USD', () => {
+ render(
+
+ );
+ const top_up_btn = screen.getByRole('button', { name: /Top up/i });
+ expect(top_up_btn).toBeDisabled();
+ });
+
+ it('should enable the top up button if the balance is lower than 1000 USD', () => {
+ render(
+
+ );
+ const top_up_btn = screen.getByRole('button', { name: /Top up/i });
+ expect(top_up_btn).toBeEnabled();
+ });
+
+ it('should render the success dialog component if the user has less than 1000 USD and clicks on top up', () => {
+ render(
+
+ );
+ const top_up_btn = screen.getByRole('button', { name: /Top up/i });
+ fireEvent.click(top_up_btn);
+ expect(screen.getByText('Success Dialog')).toBeInTheDocument();
+ });
+
+ it('should render the success component if the is_top_up_virtual_success is true', () => {
+ render(
+
+ );
+ expect(screen.getByText('Success Dialog')).toBeInTheDocument();
+ expect(screen.queryByTestId('dt_top_up_virtual_description')).not.toBeInTheDocument();
+ });
+
+ it('should not render the component if conditions are false', () => {
+ render(
+
+ );
+ expect(screen.queryByTestId('dt_top_up_virtual_description')).not.toBeInTheDocument();
+ });
+});
diff --git a/packages/cfd/src/Containers/__tests__/compare-accounts-modal.spec.js b/packages/cfd/src/Containers/__tests__/compare-accounts-modal.spec.js
new file mode 100644
index 000000000000..d9b050c54e47
--- /dev/null
+++ b/packages/cfd/src/Containers/__tests__/compare-accounts-modal.spec.js
@@ -0,0 +1,88 @@
+import React from 'react';
+import { render, screen, waitFor, fireEvent } from '@testing-library/react';
+import CompareAccountsModal from '../compare-accounts-modal';
+
+jest.mock('Stores/connect.js', () => ({
+ __esModule: true,
+ default: 'mockedDefaultExport',
+ connect: () => Component => Component,
+}));
+
+jest.mock('../mt5-compare-table-content', () => jest.fn(() => 'MockedMt5CompareTableContent'));
+
+describe('CompareAccountsModal', () => {
+ let modal_root_el;
+
+ beforeAll(() => {
+ modal_root_el = document.createElement('div');
+ modal_root_el.setAttribute('id', 'modal_root');
+ document.body.appendChild(modal_root_el);
+ });
+
+ afterAll(() => {
+ document.body.removeChild(modal_root_el);
+ });
+
+ let mock_props;
+ beforeEach(() => {
+ mock_props = {
+ disableApp: jest.fn(),
+ enableApp: jest.fn(),
+ is_compare_accounts_visible: true,
+ is_loading: false,
+ is_logged_in: true,
+ is_eu: false,
+ is_uk: false,
+ is_eu_country: false,
+ is_real_enabled: true,
+ platform: 'mt5',
+ residence: 'id',
+ is_demo_tab: true,
+ toggleCompareAccounts: jest.fn(),
+ openPasswordModal: jest.fn(),
+ openDerivRealAccountNeededModal: jest.fn(),
+ };
+ });
+ it('should render the modal', async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText(/compare available accounts/i)).toBeInTheDocument();
+ });
+ });
+ it('should render the MockedMT5CompareTableContent for mt5', async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText(/compare available accounts/i)).toBeInTheDocument();
+ });
+ expect(screen.getByText(/MockedMt5CompareTableContent/i)).toBeInTheDocument();
+ });
+ it('should render the CompareAccountsModal if the platform is dxtrade', async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getAllByText(/compare accounts/i)[0]).toBeInTheDocument();
+ });
+ expect(screen.getAllByText(/maximum leverage/i)[0]).toBeInTheDocument();
+ });
+ it('should render the MockedMt5CompareTableContent if the user is not logged in', async () => {
+ render();
+ await waitFor(() => {
+ expect(screen.getByText(/compare available accounts/i)).toBeInTheDocument();
+ });
+ expect(screen.getByText(/MockedMt5CompareTableContent/i)).toBeInTheDocument();
+ });
+ it('should call toggleCompareAccountsModal if the compare accounts button is clicked', async () => {
+ render(
+
+ );
+ await waitFor(() => {
+ expect(screen.getAllByText(/compare accounts/i)[0]).toBeInTheDocument();
+ });
+ fireEvent.click(screen.getByText(/compare accounts/i));
+ expect(mock_props.toggleCompareAccounts).toHaveBeenCalled();
+ });
+});
diff --git a/packages/cfd/src/Containers/cfd-dashboard-container.tsx b/packages/cfd/src/Containers/cfd-dashboard-container.tsx
index eff46d6db1d9..df2c0f5cf415 100644
--- a/packages/cfd/src/Containers/cfd-dashboard-container.tsx
+++ b/packages/cfd/src/Containers/cfd-dashboard-container.tsx
@@ -16,6 +16,7 @@ const CFDDashboardContainer = ({ platform, active_index, is_dark_mode_on, dxtrad
className={classNames('cfd-dashboard__download-center', {
'cfd-dashboard__download-center--mt5': platform === CFD_PLATFORMS.MT5,
})}
+ data-testid='dt_cfd_dashboard_download_center_container'
>
{general_messages.getDownloadHeader(platform)}
void;
toggleCFDVerificationModal: () => void;
account_status: GetAccountStatus;
+ mt5_login_list: DetailsOfEachMT5Loginid[];
};
const JurisdictionModal = ({
@@ -67,6 +74,7 @@ const JurisdictionModal = ({
setJurisdictionSelectedShortcode,
toggleCFDVerificationModal,
account_status,
+ mt5_login_list,
}: TJurisdictionModalProps) => {
const [checked, setChecked] = React.useState(false);
const [has_submitted_personal_details, setHasSubmittedPersonalDetails] = React.useState(false);
@@ -92,13 +100,26 @@ const JurisdictionModal = ({
const poa_failed = poa_status === 'suspected' || poa_status === 'rejected' || poa_status === 'expired';
const poi_poa_not_submitted = poi_status === 'none' || poa_status === 'none';
- React.useEffect(() => {
- if (is_jurisdiction_modal_visible) {
- if ((poa_status === 'pending' || poi_status === 'pending') && !is_eu) {
+ const selectSVGJurisdiction = () => {
+ if (account_type.type && !is_eu) {
+ const created_svg_accounts = mt5_login_list.filter(
+ data =>
+ data.market_type === account_type.type &&
+ data.landing_company_short === 'svg' &&
+ data.account_type === 'real'
+ );
+ if (!created_svg_accounts.length && (poa_status === 'pending' || poi_status === 'pending')) {
setJurisdictionSelectedShortcode('svg');
} else {
setJurisdictionSelectedShortcode('');
}
+ } else {
+ setJurisdictionSelectedShortcode('');
+ }
+ };
+ React.useEffect(() => {
+ if (is_jurisdiction_modal_visible) {
+ selectSVGJurisdiction();
if (!has_submitted_personal_details) {
let get_settings_response: GetSettings = {};
if (!account_settings) {
@@ -347,4 +368,5 @@ export default connect(({ modules, ui, client }: RootStore) => ({
toggleCFDVerificationModal: modules.cfd.toggleCFDVerificationModal,
setJurisdictionSelectedShortcode: modules.cfd.setJurisdictionSelectedShortcode,
account_status: client.account_status,
+ mt5_login_list: client.mt5_login_list,
}))(JurisdictionModal);
diff --git a/packages/cfd/src/Containers/mt5-trade-modal.tsx b/packages/cfd/src/Containers/mt5-trade-modal.tsx
index 59b03433e1bb..cd3d5f984244 100644
--- a/packages/cfd/src/Containers/mt5-trade-modal.tsx
+++ b/packages/cfd/src/Containers/mt5-trade-modal.tsx
@@ -94,7 +94,7 @@ const account_icons: { [key: string]: TAccountIconValues } = {
};
const getTitle = (market_type: string, is_eu_user: boolean) => {
- if (is_eu_user) localize('MT5 CFDs MFSA');
+ if (is_eu_user) localize('MT5 CFDs');
return market_type;
};
diff --git a/packages/cfd/tsconfig.json b/packages/cfd/tsconfig.json
index c184ea647b78..a81405eae140 100644
--- a/packages/cfd/tsconfig.json
+++ b/packages/cfd/tsconfig.json
@@ -9,11 +9,11 @@
"Modules/*": ["./src/Modules/*"],
"Sass/*": ["./src/sass/*"],
"Stores/*": ["./src/Stores/*"],
- "Utils/*": ["./src/Utils/*"]
+ "Utils/*": ["./src/Utils/*"],
+ "@deriv/*": ["../*/src"]
},
"outDir": "./dist",
- "rootDir": "./src",
"baseUrl": "./"
},
- "include": ["./src/**/*.ts", "./src/**/*.tsx", "globals.d.ts"]
+ "include": ["src", "globals.d.ts"]
}
diff --git a/packages/components/globals.d.ts b/packages/components/globals.d.ts
deleted file mode 100644
index f783afdf1d4b..000000000000
--- a/packages/components/globals.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-declare module '@deriv/translations';
-declare module '@deriv/shared';
diff --git a/packages/components/package.json b/packages/components/package.json
index aa658cc4e7b8..f79337faf84e 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -5,7 +5,7 @@
"main": "src/index.js",
"private": true,
"engines": {
- "node": "^14.17.1"
+ "node": "^16.16.0"
},
"scripts": {
"start": "rimraf lib && npm run test && npm run serve",
@@ -41,6 +41,8 @@
"@storybook/addon-info": "^5.3.21",
"@storybook/addon-knobs": "^6.4.0",
"@testing-library/react": "^12.0.0",
+ "@types/react": "^18.0.7",
+ "@types/react-dom": "^18.0.0",
"babel-loader": "^8.1.0",
"copy-webpack-plugin": "^9.0.1",
"copy-webpack-plugin-v6": "npm:copy-webpack-plugin@6",
@@ -60,6 +62,7 @@
"style-loader": "^1.2.1",
"svg-sprite-loader": "^5.2.1",
"svgo-loader": "^3.0.0",
+ "typescript": "^4.6.3",
"webpack": "^5.46.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2"
@@ -83,7 +86,7 @@
"react-router-dom": "^5.2.0",
"react-swipeable": "^5.5.1",
"react-tiny-popover": "^5.1.0",
- "react-transition-group": "^4.3.0",
+ "react-transition-group": "4.4.2",
"react-virtualized": "^9.22.2"
}
}
diff --git a/packages/components/src/components/autocomplete/autocomplete.jsx b/packages/components/src/components/autocomplete/autocomplete.jsx
index 20cb84a14885..fca0278f29c2 100644
--- a/packages/components/src/components/autocomplete/autocomplete.jsx
+++ b/packages/components/src/components/autocomplete/autocomplete.jsx
@@ -33,6 +33,7 @@ const Autocomplete = React.memo(props => {
autoComplete,
className,
dropdown_offset,
+ historyValue,
error,
has_updating_list = true,
input_id,
@@ -65,10 +66,18 @@ const Autocomplete = React.memo(props => {
React.useEffect(() => {
if (has_updating_list) {
setFilteredItems(list_items);
- setActiveIndex(null);
- setInputValue('');
+ if (historyValue) {
+ const index = filtered_items.findIndex(object => {
+ return object.text === historyValue;
+ });
+ setInputValue(historyValue);
+ setActiveIndex(index);
+ } else {
+ setInputValue('');
+ setActiveIndex(null);
+ }
}
- }, [list_items, has_updating_list]);
+ }, [list_items, has_updating_list, historyValue]);
React.useEffect(() => {
if (should_show_list && list_item_ref.current) {
diff --git a/packages/components/src/components/checklist/checklist.jsx b/packages/components/src/components/checklist/checklist.jsx
index fdfc1d9537fc..ef23f54dd091 100644
--- a/packages/components/src/components/checklist/checklist.jsx
+++ b/packages/components/src/components/checklist/checklist.jsx
@@ -17,7 +17,11 @@ const ItemStatus = ({ status, onClick, button_text }) => {
case 'action':
default:
return (
-
+
);
diff --git a/packages/components/src/components/composite-checkbox/composite-checkbox.jsx b/packages/components/src/components/composite-checkbox/composite-checkbox.jsx
index b2e05df07fc5..0b327d56c4c8 100644
--- a/packages/components/src/components/composite-checkbox/composite-checkbox.jsx
+++ b/packages/components/src/components/composite-checkbox/composite-checkbox.jsx
@@ -44,7 +44,7 @@ CompositeCheckbox.propTypes = {
label: PropTypes.string.isRequired,
id: PropTypes.string,
description: PropTypes.string.isRequired,
- children: PropTypes.oneOfType(PropTypes.node, PropTypes.arrayOf(PropTypes.node)),
+ children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
};
export default CompositeCheckbox;
diff --git a/packages/components/src/components/dropdown/display-text.jsx b/packages/components/src/components/dropdown/display-text.jsx
index 16f88862d688..92a1a7b4b8e4 100644
--- a/packages/components/src/components/dropdown/display-text.jsx
+++ b/packages/components/src/components/dropdown/display-text.jsx
@@ -56,7 +56,7 @@ DisplayText.propTypes = {
list: listPropType(),
name: PropTypes.string,
placeholder: PropTypes.string,
- value: PropTypes.string,
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
is_align_text_left: PropTypes.bool,
};
diff --git a/packages/components/src/components/dropdown/dropdown.jsx b/packages/components/src/components/dropdown/dropdown.jsx
index c4cc54a32fb7..a1fbfdc4c0dc 100644
--- a/packages/components/src/components/dropdown/dropdown.jsx
+++ b/packages/components/src/components/dropdown/dropdown.jsx
@@ -16,21 +16,21 @@ const DropdownList = React.forwardRef((props, list_ref) => {
const {
classNameItems,
classNameLabel,
- has_symbol,
handleSelect,
+ has_symbol,
initial_offset,
- is_list_visible,
+ is_align_text_left,
is_alignment_left,
is_alignment_top,
- is_align_text_left,
is_large,
+ is_list_visible,
list,
nodes,
onKeyPressed,
+ parent_ref,
portal_id,
suffix_icon,
value,
- parent_ref,
} = props;
const [list_dimensions, setListDimensions] = React.useState([initial_offset, 0]);
@@ -184,20 +184,21 @@ const Dropdown = ({
has_symbol,
hint,
initial_offset = 0,
- is_alignment_top,
- is_alignment_left,
is_align_text_left,
+ is_alignment_left,
+ is_alignment_top,
is_large,
- is_nativepicker,
is_nativepicker_visible,
+ is_nativepicker,
label,
- list,
list_portal_id,
+ list,
name,
no_border,
+ onChange,
placeholder,
suffix_icon,
- onChange,
+ test_id,
value,
}) => {
const dropdown_ref = React.useRef();
@@ -360,6 +361,7 @@ const Dropdown = ({
name={name}
readOnly='readonly'
type='hidden'
+ data-testid={test_id}
value={value || 0}
/>
@@ -396,7 +398,7 @@ const Dropdown = ({
is_align_text_left={is_align_text_left}
is_title={is_list_visible}
placeholder={placeholder}
- value={value || 0}
+ value={value ?? 0}
list={list}
/>
@@ -416,24 +418,24 @@ const Dropdown = ({
)}
{!error && hint && (
@@ -454,31 +456,32 @@ const Dropdown = ({
Dropdown.propTypes = {
className: PropTypes.string,
classNameDisplay: PropTypes.string,
+ classNameHint: PropTypes.string,
classNameItems: PropTypes.string,
classNameLabel: PropTypes.string,
- classNameHint: PropTypes.string,
disabled: PropTypes.bool,
- list_portal_id: PropTypes.string,
+ error: PropTypes.string,
+ handleBlur: PropTypes.func,
has_symbol: PropTypes.bool,
+ hint: PropTypes.string,
initial_offset: PropTypes.number,
+ is_align_text_left: PropTypes.bool,
is_alignment_left: PropTypes.bool,
+ is_alignment_top: PropTypes.bool,
is_large: PropTypes.bool,
- is_nativepicker: PropTypes.bool,
is_nativepicker_visible: PropTypes.bool,
+ is_nativepicker: PropTypes.bool,
label: PropTypes.string,
- list: listPropType(),
list_height: PropTypes.string,
+ list_portal_id: PropTypes.string,
+ list: listPropType(),
name: PropTypes.string,
no_border: PropTypes.bool,
onChange: PropTypes.func,
placeholder: PropTypes.string,
suffix_icon: PropTypes.string,
+ test_id: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- error: PropTypes.string,
- handleBlur: PropTypes.func,
- hint: PropTypes.string,
- is_alignment_top: PropTypes.bool,
- is_align_text_left: PropTypes.bool,
};
export default Dropdown;
diff --git a/packages/components/src/components/fade-wrapper/index.js b/packages/components/src/components/fade-wrapper/index.js
index 909ca3163890..cfa081a7dba4 100644
--- a/packages/components/src/components/fade-wrapper/index.js
+++ b/packages/components/src/components/fade-wrapper/index.js
@@ -1,3 +1,4 @@
+import FadeWrapper from './fade-wrapper.jsx';
import './fade-wrapper.scss';
-export default from './fade-wrapper.jsx';
+export default FadeWrapper;
diff --git a/packages/components/src/components/icon-trade-types/index.js b/packages/components/src/components/icon-trade-types/index.js
index 0e40d8cce46e..72bb933df322 100644
--- a/packages/components/src/components/icon-trade-types/index.js
+++ b/packages/components/src/components/icon-trade-types/index.js
@@ -1,3 +1,4 @@
+import IconTradeTypes from './icon-trade-types.jsx';
import './icon-trade-types.scss';
-export default from './icon-trade-types.jsx';
+export default IconTradeTypes;
diff --git a/packages/components/src/components/icon/cashier/ic-cashier-sort.svg b/packages/components/src/components/icon/cashier/ic-cashier-sort.svg
index 25c5bc612e19..52cc59dace11 100644
--- a/packages/components/src/components/icon/cashier/ic-cashier-sort.svg
+++ b/packages/components/src/components/icon/cashier/ic-cashier-sort.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-email-sent-p2p.svg b/packages/components/src/components/icon/common/ic-email-sent-p2p.svg
new file mode 100644
index 000000000000..ac6603322838
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-email-sent-p2p.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-email-verification-link-blocked.svg b/packages/components/src/components/icon/common/ic-email-verification-link-blocked.svg
new file mode 100644
index 000000000000..6da1da0ce86a
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-email-verification-link-blocked.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-email-verification-link-invalid.svg b/packages/components/src/components/icon/common/ic-email-verification-link-invalid.svg
new file mode 100644
index 000000000000..6edf85f052d5
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-email-verification-link-invalid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-email-verification-link-valid.svg b/packages/components/src/components/icon/common/ic-email-verification-link-valid.svg
new file mode 100644
index 000000000000..c7f5bcb37d46
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-email-verification-link-valid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-empty-star.svg b/packages/components/src/components/icon/common/ic-empty-star.svg
new file mode 100644
index 000000000000..ecc930e6a0cb
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-empty-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-full-star.svg b/packages/components/src/components/icon/common/ic-full-star.svg
new file mode 100644
index 000000000000..9e97ff38b5c4
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-full-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-thumbs-down.svg b/packages/components/src/components/icon/common/ic-thumbs-down.svg
new file mode 100644
index 000000000000..924f37a36232
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-thumbs-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/common/ic-thumbs-up.svg b/packages/components/src/components/icon/common/ic-thumbs-up.svg
new file mode 100644
index 000000000000..ca34f05cc64e
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-thumbs-up.svg
@@ -0,0 +1 @@
+
diff --git a/packages/components/src/components/icon/icons.js b/packages/components/src/components/icon/icons.js
index e8a87d6156e2..14eb0e3b79ac 100644
--- a/packages/components/src/components/icon/icons.js
+++ b/packages/components/src/components/icon/icons.js
@@ -238,18 +238,24 @@ import './common/ic-edit.svg';
import './common/ic-email-firewall.svg';
import './common/ic-email-outline.svg';
import './common/ic-email-sent-dashboard.svg';
+import './common/ic-email-sent-p2p.svg';
import './common/ic-email-sent.svg';
import './common/ic-email-spam.svg';
import './common/ic-email-typo.svg';
+import './common/ic-email-verification-link-blocked.svg';
+import './common/ic-email-verification-link-invalid.svg';
+import './common/ic-email-verification-link-valid.svg';
import './common/ic-email-verified.svg';
import './common/ic-email.svg';
import './common/ic-empty-folder.svg';
+import './common/ic-empty-star.svg';
import './common/ic-eye.svg';
import './common/ic-filter.svg';
import './common/ic-folder-open-filled.svg';
import './common/ic-folder-open.svg';
import './common/ic-full-screen-restore.svg';
import './common/ic-full-screen.svg';
+import './common/ic-full-star.svg';
import './common/ic-gear-light.svg';
import './common/ic-gear.svg';
import './common/ic-get-platform.svg';
@@ -377,6 +383,8 @@ import './common/ic-stop.svg';
import './common/ic-success-reset-trading-password.svg';
import './common/ic-success.svg';
import './common/ic-theme.svg';
+import './common/ic-thumbs-down.svg';
+import './common/ic-thumbs-up.svg';
import './common/ic-trade.svg';
import './common/ic-transactions.svg';
import './common/ic-unarchive.svg';
diff --git a/packages/components/src/components/popover/popover.jsx b/packages/components/src/components/popover/popover.jsx
index 4db0066c4076..a2b21c265352 100644
--- a/packages/components/src/components/popover/popover.jsx
+++ b/packages/components/src/components/popover/popover.jsx
@@ -25,11 +25,12 @@ const Popover = ({
message,
onBubbleClose,
onBubbleOpen,
+ onClick = () => {},
relative_render,
should_disable_pointer_events,
+ should_show_cursor,
window_border,
zIndex,
- should_show_cursor,
}) => {
const ref = React.useRef();
const [popover_ref, setPopoverRef] = React.useState(undefined);
@@ -53,7 +54,7 @@ const Popover = ({
const icon_class_name = classNames(classNameTargetIcon, icon);
return (
-
+
{relative_render && (
@@ -219,9 +220,10 @@ Popover.propTypes = {
message: PropTypes.oneOfType([PropTypes.node, PropTypes.object, PropTypes.string]),
onBubbleOpen: PropTypes.func,
onBubbleClose: PropTypes.func,
- zIndex: PropTypes.number,
+ onClick: PropTypes.func,
should_disable_pointer_events: PropTypes.bool,
should_show_cursor: PropTypes.bool,
+ zIndex: PropTypes.number,
window_border: PropTypes.number,
};
diff --git a/packages/components/stories/icon/icons.js b/packages/components/stories/icon/icons.js
index c038feb6999b..20071acf72d1 100644
--- a/packages/components/stories/icon/icons.js
+++ b/packages/components/stories/icon/icons.js
@@ -245,18 +245,24 @@ export const icons =
'IcEmailFirewall',
'IcEmailOutline',
'IcEmailSentDashboard',
+ 'IcEmailSentP2p',
'IcEmailSent',
'IcEmailSpam',
'IcEmailTypo',
+ 'IcEmailVerificationLinkBlocked',
+ 'IcEmailVerificationLinkInvalid',
+ 'IcEmailVerificationLinkValid',
'IcEmailVerified',
'IcEmail',
'IcEmptyFolder',
+ 'IcEmptyStar',
'IcEye',
'IcFilter',
'IcFolderOpenFilled',
'IcFolderOpen',
'IcFullScreenRestore',
'IcFullScreen',
+ 'IcFullStar',
'IcGearLight',
'IcGear',
'IcGetPlatform',
@@ -384,6 +390,8 @@ export const icons =
'IcSuccessResetTradingPassword',
'IcSuccess',
'IcTheme',
+ 'IcThumbsDown',
+ 'IcThumbsUp',
'IcTrade',
'IcTransactions',
'IcUnarchive',
diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json
index e2eb22d0b831..4b70bee9f97b 100644
--- a/packages/components/tsconfig.json
+++ b/packages/components/tsconfig.json
@@ -2,8 +2,10 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
- "rootDir": "./src",
- "baseUrl": "./"
+ "baseUrl": "./",
+ "paths": {
+ "@deriv/*": ["../*/src"]
+ }
},
- "include": ["./src/**/*.ts", "./src/**/*.tsx", "globals.d.ts"]
+ "include": ["src"]
}
diff --git a/packages/core/package.json b/packages/core/package.json
index a5f90297efe3..84e9680012c7 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -30,13 +30,15 @@
"url": "https://github.com/binary-com/deriv-app/issues"
},
"engines": {
- "node": "^14.17.1"
+ "node": "^16.16.0"
},
"homepage": "https://github.com/binary-com/deriv-app",
"devDependencies": {
"@babel/plugin-proposal-unicode-property-regex": "^7.16.7",
"@babel/eslint-parser": "^7.17.0",
"@babel/preset-react": "^7.16.7",
+ "@types/react": "^18.0.7",
+ "@types/react-dom": "^18.0.0",
"babel-loader": "^8.1.0",
"chai": "^4.2.0",
"circular-dependency-plugin": "^5.2.2",
@@ -82,6 +84,7 @@
"stylelint-webpack-plugin": "^2.1.1",
"svgo": "^2.8.0",
"terser-webpack-plugin": "^5.1.1",
+ "typescript": "^4.6.3",
"webpack": "^5.46.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2",
@@ -136,7 +139,7 @@
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-tiny-popover": "^5.1.0",
- "react-transition-group": "^4.3.0",
+ "react-transition-group": "4.4.2",
"react-window": "^1.8.5",
"web-push-notifications": "^3.24.0"
}
diff --git a/packages/core/src/App/Components/Elements/NotificationMessage/notification-order.jsx b/packages/core/src/App/Components/Elements/NotificationMessage/notification-order.jsx
new file mode 100644
index 000000000000..b978fad1e9f2
--- /dev/null
+++ b/packages/core/src/App/Components/Elements/NotificationMessage/notification-order.jsx
@@ -0,0 +1,74 @@
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import React from 'react';
+import { Button, Text } from '@deriv/components';
+import { isEmptyObject } from '@deriv/shared';
+import { BinaryLink } from 'App/Components/Routes';
+import CloseButton from './close-button.jsx';
+import NotificationStatusIcons from './notification-status-icons.jsx';
+
+const NotificationOrder = ({ action, header, message, onClose }) => {
+ setTimeout(onClose, 60000);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {header}
+
+
+ {message}
+
+
+ {!isEmptyObject(action) && (
+
+ {action.route ? (
+
+
+ {action.text}
+
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
+
+NotificationOrder.propTypes = {
+ action: PropTypes.object,
+ header: PropTypes.string,
+ is_auto_close: PropTypes.bool,
+ message: PropTypes.string,
+ onClose: PropTypes.func,
+};
+
+export default NotificationOrder;
diff --git a/packages/core/src/App/Components/Elements/NotificationMessage/notification.jsx b/packages/core/src/App/Components/Elements/NotificationMessage/notification.jsx
index 9429dea5fd4a..ad71aebc7520 100644
--- a/packages/core/src/App/Components/Elements/NotificationMessage/notification.jsx
+++ b/packages/core/src/App/Components/Elements/NotificationMessage/notification.jsx
@@ -10,6 +10,7 @@ import { default_delay, types } from './constants';
import NotificationPromo from './notification-promo.jsx';
import { BinaryLink } from '../../Routes';
import NotificationCloseMxMlt from './notification-close-mx-mlt.jsx';
+import NotificationOrder from './notification-order.jsx';
const Notification = ({ data, removeNotificationMessage }) => {
const linear_progress_container_ref = React.useRef(null);
@@ -76,6 +77,10 @@ const Notification = ({ data, removeNotificationMessage }) => {
onClose={destroy}
/>
);
+ case 'p2p_completed_order':
+ return (
+
+ );
default:
return (
@@ -171,14 +184,25 @@ const BuySellRow = ({ row: advert }) => {
- {!!advert.advertiser_details.total_completion_rate && (
-
-
+ {!!rating_count && !!rating_average ? (
+
-
- )}
+ ) : (
+
+
+
+ )}
+
diff --git a/packages/p2p/src/components/buy-sell/buy-sell-row.scss b/packages/p2p/src/components/buy-sell/buy-sell-row.scss
index e69db535f40c..52b8793b0436 100644
--- a/packages/p2p/src/components/buy-sell/buy-sell-row.scss
+++ b/packages/p2p/src/components/buy-sell/buy-sell-row.scss
@@ -114,6 +114,18 @@
}
}
+ &__rating {
+ margin-top: 0.1rem;
+
+ &--star {
+ pointer-events: none;
+
+ > svg {
+ margin-right: 0.1rem;
+ }
+ }
+ }
+
&__table-cell {
&--left {
align-items: flex-start;
diff --git a/packages/p2p/src/components/buy-sell/buy-sell-table.jsx b/packages/p2p/src/components/buy-sell/buy-sell-table.jsx
index 71354780233f..50c928fdc2d7 100644
--- a/packages/p2p/src/components/buy-sell/buy-sell-table.jsx
+++ b/packages/p2p/src/components/buy-sell/buy-sell-table.jsx
@@ -60,6 +60,7 @@ const BuySellTable = ({ onScroll }) => {
className='sort'
is_open={buy_sell_store.is_sort_dropdown_open}
height='10rem'
+ toggleModal={() => buy_sell_store.setIsSortDropdownOpen(false)}
width='80vw'
>
{
);
}
- if (buy_sell_store.show_advertiser_page && !buy_sell_store.should_show_verification) {
- return (
-
-
-
- );
- }
-
return (
diff --git a/packages/p2p/src/components/buy-sell/buy-sell.scss b/packages/p2p/src/components/buy-sell/buy-sell.scss
index 15890e005dfa..bb9026399279 100644
--- a/packages/p2p/src/components/buy-sell/buy-sell.scss
+++ b/packages/p2p/src/components/buy-sell/buy-sell.scss
@@ -5,6 +5,8 @@
flex-direction: column;
&__advertiser-page-return {
+ margin: 0 0 2.4rem;
+
.dc-text {
align-self: flex-start;
}
diff --git a/packages/p2p/src/components/buy-sell/sort-dropdown.scss b/packages/p2p/src/components/buy-sell/sort-dropdown.scss
index b4d930e04ff8..0ed4b61a29f4 100644
--- a/packages/p2p/src/components/buy-sell/sort-dropdown.scss
+++ b/packages/p2p/src/components/buy-sell/sort-dropdown.scss
@@ -50,7 +50,8 @@
padding: 0.8rem;
@include mobile {
- grid-column: 2;
+ display: flex;
+ justify-content: center;
justify-self: center;
height: 4rem;
width: 4rem;
diff --git a/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.jsx b/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.jsx
new file mode 100644
index 000000000000..249b4887c8a9
--- /dev/null
+++ b/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Icon, Modal, Text } from '@deriv/components';
+import { Localize } from 'Components/i18next';
+
+const EmailLinkBlockedModal = ({
+ // TODO: Uncomment when time is available in BE response
+ // blocked_for_minutes,
+ email_link_blocked_modal_error_message,
+ is_email_link_blocked_modal_open,
+ setIsEmailLinkBlockedModalOpen,
+}) => {
+ return (
+
<>>}
+ toggleModal={() => setIsEmailLinkBlockedModalOpen(false)}
+ width='440px'
+ >
+
+
+
+
+
+
+ {email_link_blocked_modal_error_message}
+
+
+
+ );
+};
+
+EmailLinkBlockedModal.propTypes = {
+ // TODO: Uncomment when time is available in BE response
+ // blocked_for_minutes: PropTypes.number,
+ email_link_blocked_modal_error_message: PropTypes.string,
+ is_email_link_blocked_modal_open: PropTypes.bool,
+ setIsEmailLinkBlockedModalOpen: PropTypes.func,
+};
+
+export default EmailLinkBlockedModal;
diff --git a/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.scss b/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.scss
new file mode 100644
index 000000000000..975c829f0532
--- /dev/null
+++ b/packages/p2p/src/components/email-link-blocked-modal/email-link-blocked-modal.scss
@@ -0,0 +1,9 @@
+.email-link-blocked-modal {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+
+ &--text {
+ margin: 2.4rem 0;
+ }
+}
diff --git a/packages/p2p/src/components/email-link-blocked-modal/index.js b/packages/p2p/src/components/email-link-blocked-modal/index.js
new file mode 100644
index 000000000000..356741ec4cc6
--- /dev/null
+++ b/packages/p2p/src/components/email-link-blocked-modal/index.js
@@ -0,0 +1,4 @@
+import EmailLinkBlockedModal from './email-link-blocked-modal.jsx';
+import './email-link-blocked-modal.scss';
+
+export default EmailLinkBlockedModal;
diff --git a/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.jsx b/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.jsx
new file mode 100644
index 000000000000..cd4cdb8d9656
--- /dev/null
+++ b/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.jsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Icon, Modal, Text } from '@deriv/components';
+import { Localize } from 'Components/i18next';
+
+const EmailLinkVerifiedModal = ({
+ amount,
+ currency,
+ is_email_link_verified_modal_open,
+ onClickConfirm,
+ setIsEmailLinkVerifiedModalOpen,
+}) => {
+ return (
+
<>>}
+ toggleModal={() => setIsEmailLinkVerifiedModalOpen(false)}
+ width='440px'
+ >
+
+
+
+
+
+
+
+
+
+
+ {
+ setIsEmailLinkVerifiedModalOpen(false);
+ onClickConfirm();
+ }}
+ >
+
+
+
+
+ );
+};
+
+EmailLinkVerifiedModal.propTypes = {
+ amount: PropTypes.string,
+ currency: PropTypes.string,
+ is_email_link_verified_modal_open: PropTypes.bool,
+ onClickConfirm: PropTypes.func,
+ setIsEmailLinkVerifiedModalOpen: PropTypes.func,
+};
+
+export default EmailLinkVerifiedModal;
diff --git a/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.scss b/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.scss
new file mode 100644
index 000000000000..e5b0c2fbb694
--- /dev/null
+++ b/packages/p2p/src/components/email-link-verified-modal/email-link-verified-modal.scss
@@ -0,0 +1,13 @@
+.email-verified-modal {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+
+ &--footer {
+ align-self: center;
+ }
+
+ &--text {
+ margin: 2.4rem 0;
+ }
+}
diff --git a/packages/p2p/src/components/email-link-verified-modal/index.js b/packages/p2p/src/components/email-link-verified-modal/index.js
new file mode 100644
index 000000000000..35446e1c3a44
--- /dev/null
+++ b/packages/p2p/src/components/email-link-verified-modal/index.js
@@ -0,0 +1,4 @@
+import EmailLinkVerifiedModal from './email-link-verified-modal.jsx';
+import './email-link-verified-modal.scss';
+
+export default EmailLinkVerifiedModal;
diff --git a/packages/p2p/src/components/email-verification-modal/email-verification-modal.jsx b/packages/p2p/src/components/email-verification-modal/email-verification-modal.jsx
new file mode 100644
index 000000000000..b17d9a663e86
--- /dev/null
+++ b/packages/p2p/src/components/email-verification-modal/email-verification-modal.jsx
@@ -0,0 +1,109 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Icon, Modal, Text } from '@deriv/components';
+import { Localize } from 'Components/i18next';
+
+const EmailVerificationModal = ({
+ email_address,
+ is_email_verification_modal_open,
+ onClickResendEmailButton,
+ setIsEmailVerificationModalOpen,
+ should_show_resend_email_button = true,
+ // TODO: Uncomment when time is available in BE response
+ // remaining_time,
+ // verification_link_expiry_time,
+}) => {
+ const [should_show_reasons_if_no_email, setShouldShowReasonsIfNoEmail] = React.useState(false);
+
+ return (
+
<>>}
+ toggleModal={() => setIsEmailVerificationModalOpen(false)}
+ width='440px'
+ >
+
+
+
+ ]}
+ values={{ email_address }}
+ />
+
+
+ {/* TODO: Uncomment when time is available in BE response */}
+
+
+ setShouldShowReasonsIfNoEmail(true)}
+ size='xs'
+ weight='bold'
+ >
+
+
+ {should_show_reasons_if_no_email && (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {should_show_resend_email_button && should_show_reasons_if_no_email && (
+
+
+
+ {/* TODO: Uncomment when time is available in BE response
+ */}
+
+
+ )}
+
+ );
+};
+
+EmailVerificationModal.propTypes = {
+ email_address: PropTypes.string,
+ is_email_verification_modal_open: PropTypes.bool,
+ onClickResendEmailButton: PropTypes.func,
+ // TODO: Uncomment when time is available in BE response
+ // remaining_time: PropTypes.string,
+ setIsEmailVerificationModalOpen: PropTypes.func,
+ should_show_resend_email_button: PropTypes.bool,
+ // TODO: Uncomment when time is available in BE response
+ // verification_link_expiry_time: PropTypes.number,
+};
+
+export default EmailVerificationModal;
diff --git a/packages/p2p/src/components/email-verification-modal/email-verification-modal.scss b/packages/p2p/src/components/email-verification-modal/email-verification-modal.scss
new file mode 100644
index 000000000000..ad8d834fa79b
--- /dev/null
+++ b/packages/p2p/src/components/email-verification-modal/email-verification-modal.scss
@@ -0,0 +1,42 @@
+.dc-modal__container_email-verification-modal {
+ max-height: 80vh;
+ overflow: auto;
+}
+
+.email-verification-modal {
+ &--body {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ }
+
+ &--email_text {
+ margin: 2.4rem 0;
+ }
+
+ &--footer {
+ @include mobile {
+ justify-content: center;
+ }
+ }
+
+ &--reason {
+ display: flex;
+ flex-direction: row;
+ gap: 1.6rem;
+ margin: 2.4rem 0;
+
+ &__text {
+ max-width: 34rem;
+ }
+ }
+
+ &--receive_email_text {
+ cursor: pointer;
+ margin: 3rem 0 0.6rem;
+
+ @include mobile {
+ margin: 3rem 0 2.6rem;
+ }
+ }
+}
diff --git a/packages/p2p/src/components/email-verification-modal/index.js b/packages/p2p/src/components/email-verification-modal/index.js
new file mode 100644
index 000000000000..6101a5043db1
--- /dev/null
+++ b/packages/p2p/src/components/email-verification-modal/index.js
@@ -0,0 +1,4 @@
+import EmailVerificationModal from './email-verification-modal.jsx';
+import './email-verification-modal.scss';
+
+export default EmailVerificationModal;
diff --git a/packages/p2p/src/components/error-modal/error-modal.jsx b/packages/p2p/src/components/error-modal/error-modal.jsx
new file mode 100644
index 000000000000..05a356c5d7cc
--- /dev/null
+++ b/packages/p2p/src/components/error-modal/error-modal.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Modal } from '@deriv/components';
+import { Localize } from 'Components/i18next';
+
+const ErrorModal = ({ error_message, error_modal_title, is_error_modal_open, setIsErrorModalOpen }) => {
+ return (
+
+ {error_message}
+
+ setIsErrorModalOpen(false)}>
+
+
+
+
+ );
+};
+
+ErrorModal.propTypes = {
+ error_message: PropTypes.string,
+ error_modal_title: PropTypes.string,
+ is_error_modal_open: PropTypes.bool,
+ setIsErrorModalOpen: PropTypes.func,
+};
diff --git a/packages/p2p/src/components/error-modal/index.js b/packages/p2p/src/components/error-modal/index.js
new file mode 100644
index 000000000000..1e38a565cb4c
--- /dev/null
+++ b/packages/p2p/src/components/error-modal/index.js
@@ -0,0 +1,3 @@
+import ErrorModal from './error-modal.jsx';
+
+export default ErrorModal;
diff --git a/packages/p2p/src/components/hooks/index.js b/packages/p2p/src/components/hooks/index.js
index 77c053c8f38a..a6242b00cc2b 100644
--- a/packages/p2p/src/components/hooks/index.js
+++ b/packages/p2p/src/components/hooks/index.js
@@ -1,2 +1 @@
export * from './use-updating-available-balance.jsx';
-export * from './use-payment-method-validator.jsx';
diff --git a/packages/p2p/src/components/hooks/use-payment-method-validator.jsx b/packages/p2p/src/components/hooks/use-payment-method-validator.jsx
deleted file mode 100644
index 119fe3e39981..000000000000
--- a/packages/p2p/src/components/hooks/use-payment-method-validator.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useStores } from 'Stores';
-import { localize } from 'Components/i18next';
-
-export const usePaymentMethodValidator = () => {
- const { my_profile_store } = useStores();
- const no_symbols_regex = /^[a-zA-Z0-9\s\-.@_+#(),:;']+$/;
- const no_symbols_message =
- "{{field_name}} can only include letters, numbers, spaces, and any of these symbols: -+.,'#@():;";
- const max_characters_error_message = '{{field_name}} has exceeded maximum length of 200 characters.';
-
- // Generates suitable error message
- const setErrorMessage = (user_input, field) => {
- if (!no_symbols_regex.test(user_input)) {
- return localize(no_symbols_message, {
- field_name: my_profile_store.payment_method_field_set[field],
- interpolation: { escapeValue: false }, // To prevent the conversion of characters to UNIcode
- });
- } else if (user_input.length > 200) {
- return localize(max_characters_error_message, {
- field_name: my_profile_store.payment_method_field_set[field],
- interpolation: { escapeValue: false }, // To prevent the conversion of characters to UNIcode
- });
- }
- return null;
- };
-
- const validateFields = values => {
- const errors = {};
- if (values.account) {
- const account_err_message = setErrorMessage(values.account, 'account');
- if (account_err_message) {
- errors.account = account_err_message;
- }
- }
- if (values.bank_name) {
- const bank_name_err_message = setErrorMessage(values.bank_name, 'bank_name');
- if (bank_name_err_message) {
- errors.bank_name = bank_name_err_message;
- }
- }
- if (values.branch) {
- const branch_err_message = setErrorMessage(values.branch, 'branch');
- if (branch_err_message) {
- errors.branch = branch_err_message;
- }
- }
- if (values.instructions) {
- const instruction_err_message = setErrorMessage(values.instructions, 'instructions');
- if (instruction_err_message) {
- errors.instructions = instruction_err_message;
- }
- }
- if (values.name) {
- const name_err_message = setErrorMessage(values.name, 'name');
- if (name_err_message) {
- errors.name = name_err_message;
- }
- }
- if (values.bank_code) {
- const bank_code_err_message = setErrorMessage(values.bank_code, 'bank_code');
- if (bank_code_err_message) {
- errors.bank_code = bank_code_err_message;
- }
- }
- return errors;
- };
- return validateFields;
-};
diff --git a/packages/p2p/src/components/invalid-verification-link-modal/index.js b/packages/p2p/src/components/invalid-verification-link-modal/index.js
new file mode 100644
index 000000000000..bf5d817dfe0e
--- /dev/null
+++ b/packages/p2p/src/components/invalid-verification-link-modal/index.js
@@ -0,0 +1,4 @@
+import InvalidVerificationLinkModal from './invalid-verification-link-modal.jsx';
+import './invalid-verification-link-modal.scss';
+
+export default InvalidVerificationLinkModal;
diff --git a/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.jsx b/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.jsx
new file mode 100644
index 000000000000..640d18aef1a2
--- /dev/null
+++ b/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.jsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Icon, Modal, Text } from '@deriv/components';
+import { Localize } from 'Components/i18next';
+
+const InvalidVerificationLinkModal = ({
+ invalid_verification_link_error_message,
+ is_invalid_verification_link_modal_open,
+ onClickGetNewLinkButton,
+ setIsInvalidVerificationLinkModalOpen,
+ // TODO: Uncomment when time is available in BE response
+ // verification_link_expiry_time,
+}) => {
+ return (
+
<>>}
+ toggleModal={() => setIsInvalidVerificationLinkModalOpen(false)}
+ width='440px'
+ >
+
+
+
+
+
+
+ {invalid_verification_link_error_message}
+
+
+
+ {
+ setIsInvalidVerificationLinkModalOpen(false);
+ onClickGetNewLinkButton();
+ }}
+ >
+
+
+
+
+ );
+};
+
+InvalidVerificationLinkModal.propTypes = {
+ invalid_verification_link_error_message: PropTypes.string,
+ is_invalid_verification_link_modal_open: PropTypes.bool,
+ onClickGetNewLinkButton: PropTypes.func,
+ setIsInvalidVerificationLinkModalOpen: PropTypes.func,
+ // TODO: Uncomment when time is available in BE response
+ // verification_link_expiry_time: PropTypes.number,
+};
+
+export default InvalidVerificationLinkModal;
diff --git a/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.scss b/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.scss
new file mode 100644
index 000000000000..887366940e0f
--- /dev/null
+++ b/packages/p2p/src/components/invalid-verification-link-modal/invalid-verification-link-modal.scss
@@ -0,0 +1,13 @@
+.invalid-verification-link-modal {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+
+ &--footer {
+ align-self: center;
+ }
+
+ &--text {
+ margin: 2.4rem 0;
+ }
+}
diff --git a/packages/p2p/src/components/loading-modal/index.js b/packages/p2p/src/components/loading-modal/index.js
new file mode 100644
index 000000000000..41606f6895e6
--- /dev/null
+++ b/packages/p2p/src/components/loading-modal/index.js
@@ -0,0 +1,3 @@
+import LoadingModal from './loading-modal.jsx';
+
+export default LoadingModal;
diff --git a/packages/p2p/src/components/loading-modal/loading-modal.jsx b/packages/p2p/src/components/loading-modal/loading-modal.jsx
new file mode 100644
index 000000000000..7ff444d16f41
--- /dev/null
+++ b/packages/p2p/src/components/loading-modal/loading-modal.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Loading, Modal } from '@deriv/components';
+
+const LoadingModal = ({ is_loading_modal_open }) => {
+ return (
+
+
+
+ );
+};
+
+LoadingModal.propTypes = {
+ is_loading_modal_open: PropTypes.bool,
+};
+
+export default LoadingModal;
diff --git a/packages/p2p/src/components/my-ads/ad-status.jsx b/packages/p2p/src/components/my-ads/ad-status.jsx
index 5697ff150947..bb2afc6da43a 100644
--- a/packages/p2p/src/components/my-ads/ad-status.jsx
+++ b/packages/p2p/src/components/my-ads/ad-status.jsx
@@ -8,14 +8,28 @@ import './ad-status.scss';
const AdStatus = ({ is_active }) => {
if (!is_active) {
return (
-
+
);
}
return (
-
+
);
diff --git a/packages/p2p/src/components/my-ads/ad-status.scss b/packages/p2p/src/components/my-ads/ad-status.scss
index f8f3a167b289..888018e7a1f9 100644
--- a/packages/p2p/src/components/my-ads/ad-status.scss
+++ b/packages/p2p/src/components/my-ads/ad-status.scss
@@ -1,21 +1,28 @@
+@mixin ad-status-base($background-color) {
+ &:before {
+ content: '';
+ height: 100%;
+ width: 100%;
+ opacity: 0.16;
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ border-radius: 1.6rem;
+ background-color: $background-color;
+ }
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ padding: 0.1rem 1.2rem;
+ position: relative;
+ text-align: center;
+}
+
.ad-status {
&--active {
- &:before {
- content: '';
- height: 100%;
- width: 100%;
- background-color: var(--status-success);
- opacity: 0.16;
- display: block;
- position: absolute;
- left: 0;
- top: 0;
- border-radius: 1.6rem;
- }
- padding: 0.1rem 1.2rem;
- text-align: center;
- display: flex;
- position: relative;
+ @include ad-status-base(var(--status-success));
+
width: 6.7rem;
@include mobile {
@@ -25,22 +32,7 @@
}
&--inactive {
- &:before {
- content: '';
- height: 100%;
- width: 100%;
- background-color: var(--status-danger);
- opacity: 0.16;
- display: block;
- position: absolute;
- left: 0;
- top: 0;
- border-radius: 1.6rem;
- }
- padding: 0.1rem 1.2rem;
- text-align: center;
- display: flex;
- position: relative;
+ @include ad-status-base(var(--status-danger));
width: 8rem;
}
}
diff --git a/packages/p2p/src/components/my-ads/create-ad-form.jsx b/packages/p2p/src/components/my-ads/create-ad-form.jsx
index 6fbd5d2f176e..c8acd8eb4ece 100644
--- a/packages/p2p/src/components/my-ads/create-ad-form.jsx
+++ b/packages/p2p/src/components/my-ads/create-ad-form.jsx
@@ -380,7 +380,11 @@ const CreateAdForm = () => {
-
+ {is_sell_advert ? (
+
+ ) : (
+
+ )}
{
-
+ {is_sell_advert ? (
+
+ ) : (
+
+ )}