From 614596d6e79d6b4495976fea32ba1e254d67a120 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 25 Oct 2024 16:34:09 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"[WALL]=20aum=20/=20WALL-[4916=20/=204?= =?UTF-8?q?925]=20/=20wallets-implement-default-jurisdict=E2=80=A6"=20(#17?= =?UTF-8?q?324)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7b49beb1e663070414523defa8ad506a762dcb7a. --- .../__tests__/useSortedMT5Accounts.spec.ts | 296 ------------------ .../api-v2/src/hooks/useSortedMT5Accounts.ts | 85 +++-- .../CFDPasswordModalTnc.scss | 9 + .../CFDPasswordModalTnc.tsx | 57 ++++ .../__tests__/CFDPasswordModalTnc.spec.tsx} | 33 +- .../components/CFDPasswordModalTnc/index.ts | 1 + .../CFDPlatformsListAccounts.tsx | 13 +- .../ClientVerificationStatusBadge.scss | 9 - .../ClientVerificationStatusBadge.tsx | 76 ----- .../ClientVerificationBadge/index.ts | 1 - .../PlatformStatusBadge.tsx | 4 +- .../src/features/cfd/components/index.ts | 1 - .../wallets/src/features/cfd/constants.tsx | 18 +- .../AddedMT5AccountsList.scss | 3 - .../AddedMT5AccountsList.tsx | 189 ++++++++--- .../__tests__/AddedMT5AccountsList.spec.tsx | 258 ++++++--------- .../__tests__/useAddedMT5Account.spec.ts | 111 ------- .../MT5/AddedMT5AccountsList/hooks/index.ts | 1 - .../hooks/useAddedMT5Account.ts | 61 ---- .../AvailableMT5AccountsList.tsx | 68 +++- .../__test__/AvailableMT5AcountsList.spec.tsx | 121 +++---- .../ClientVerificationModal.scss | 22 -- .../ClientVerificationModal.tsx | 46 --- .../DocumentsList/DocumentsList.scss | 7 - .../DocumentsList/DocumentsList.tsx | 62 ---- .../__tests__/DocumentsList.spec.tsx | 172 ---------- .../components/DocumentTile/DocumentTile.scss | 28 -- .../components/DocumentTile/DocumentTile.tsx | 30 -- .../components/DocumentTile/index.ts | 1 - .../DocumentsList/components/index.ts | 1 - .../components/DocumentsList/index.ts | 1 - .../components/index.ts | 1 - .../modals/ClientVerificationModal/index.ts | 1 - .../JurisdictionModal/JurisdictionModal.tsx | 19 +- .../MT5AccountAdded/MT5AccountAdded.tsx | 4 +- .../MT5PasswordModal/MT5PasswordModal.tsx | 65 ++-- .../modals/MT5TradeModal/MT5TradeModal.tsx | 5 +- .../wallets/src/features/cfd/modals/index.ts | 1 - .../CreatePasswordMT5/CreatePasswordMT5.tsx | 21 +- .../screens/EnterPassword/EnterPassword.tsx | 18 +- .../__test__/EnterPassword.spec.tsx | 41 +-- .../screens/MT5TradeScreen/MT5TradeScreen.tsx | 3 +- .../MT5LicenceMessage/MT5LicenceMessage.scss | 5 - .../MT5LicenceMessage/MT5LicenceMessage.tsx | 53 ---- .../__tests__/MT5LicenceMessage.spec.tsx | 48 --- .../components/MT5LicenceMessage/index.ts | 1 - .../MT5PasswordModalTnc.scss | 5 - .../MT5PasswordModalTnc.tsx | 44 --- .../components/MT5PasswordModalTnc/index.ts | 1 - .../features/cfd/screens/components/index.ts | 2 - packages/wallets/src/features/cfd/types.ts | 23 -- .../wallets/src/features/cfd/utils/index.ts | 1 - .../wallets/src/features/cfd/utils/utils.ts | 28 -- 53 files changed, 590 insertions(+), 1585 deletions(-) delete mode 100644 packages/api-v2/src/hooks/__tests__/useSortedMT5Accounts.spec.ts create mode 100644 packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.scss create mode 100644 packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.tsx rename packages/wallets/src/features/cfd/{screens/components/MT5PasswordModalTnc/__tests__/MT5PasswordModalTnc.spec.tsx => components/CFDPasswordModalTnc/__tests__/CFDPasswordModalTnc.spec.tsx} (54%) create mode 100644 packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/index.ts delete mode 100644 packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.scss delete mode 100644 packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.tsx delete mode 100644 packages/wallets/src/features/cfd/components/ClientVerificationBadge/index.ts delete mode 100644 packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/__tests__/useAddedMT5Account.spec.ts delete mode 100644 packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/index.ts delete mode 100644 packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/useAddedMT5Account.ts delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.scss delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.tsx delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.scss delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.tsx delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/__tests__/DocumentsList.spec.tsx delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.scss delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.tsx delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/index.ts delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/index.ts delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/index.ts delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/index.ts delete mode 100644 packages/wallets/src/features/cfd/modals/ClientVerificationModal/index.ts delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.scss delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.tsx delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/__tests__/MT5LicenceMessage.spec.tsx delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/index.ts delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.scss delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.tsx delete mode 100644 packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/index.ts delete mode 100644 packages/wallets/src/features/cfd/screens/components/index.ts delete mode 100644 packages/wallets/src/features/cfd/types.ts delete mode 100644 packages/wallets/src/features/cfd/utils/index.ts delete mode 100644 packages/wallets/src/features/cfd/utils/utils.ts diff --git a/packages/api-v2/src/hooks/__tests__/useSortedMT5Accounts.spec.ts b/packages/api-v2/src/hooks/__tests__/useSortedMT5Accounts.spec.ts deleted file mode 100644 index c556c4b76780..000000000000 --- a/packages/api-v2/src/hooks/__tests__/useSortedMT5Accounts.spec.ts +++ /dev/null @@ -1,296 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import useActiveAccount from '../useActiveAccount'; -import useAvailableMT5Accounts from '../useAvailableMT5Accounts'; -import useIsEuRegion from '../useIsEuRegion'; -import useMT5AccountsList from '../useMT5AccountsList'; -import useSortedMT5Accounts from '../useSortedMT5Accounts'; -import { cleanup } from '@testing-library/react'; - -jest.mock('../useActiveAccount', () => jest.fn()); -jest.mock('../useAvailableMT5Accounts', () => jest.fn()); -jest.mock('../useIsEuRegion', () => jest.fn()); -jest.mock('../useMT5AccountsList', () => jest.fn()); - -const mockMT5NonEUAvailableAccounts = [ - { - is_default_jurisdiction: 'false', - product: 'standard', - shortcode: 'svg', - }, - { - is_default_jurisdiction: 'false', - product: 'financial', - shortcode: 'svg', - }, - { - is_default_jurisdiction: 'true', - product: 'financial', - shortcode: 'vanuatu', - }, - { - is_default_jurisdiction: 'true', - product: 'stp', - shortcode: 'vanuatu', - }, - { - is_default_jurisdiction: 'true', - product: 'standard', - shortcode: 'vanuatu', - }, - { - is_default_jurisdiction: 'true', - product: 'zero_spread', - shortcode: 'bvi', - }, - { - is_default_jurisdiction: 'true', - product: 'swap_free', - shortcode: 'svg', - }, -]; - -const mockMT5NonEUAddedAccounts = [ - { - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'standard', - }, - { - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'financial', - }, - { - is_virtual: false, - landing_company_short: 'bvi', - product: 'zero_spread', - }, -]; - -const mockMT5EUAvailableAccounts = [ - { - is_default_jurisdiction: 'true', - product: 'financial', - shortcode: 'maltainvest', - }, -]; - -const mockMT5EUAddedAccounts = [ - { - is_virtual: false, - landing_company_short: 'maltainvest', - product: 'financial', - }, -]; - -describe('useSortedMT5Accounts', () => { - beforeEach(() => { - (useActiveAccount as jest.Mock).mockReturnValue({ - data: { is_virtual: false }, - }); - (useIsEuRegion as jest.Mock).mockReturnValue({ - isEUCountry: false, - }); - }); - afterEach(cleanup); - - it('returns non-eu available accounts with default jurisdiction', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: [], - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).toEqual([ - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'standard', - shortcode: 'vanuatu', - }, - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'financial', - shortcode: 'vanuatu', - }, - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'swap_free', - shortcode: 'svg', - }, - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'zero_spread', - shortcode: 'bvi', - }, - ]); - }); - - it('returns eu available accounts with default jurisdiction', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5EUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: [], - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).toEqual([ - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'financial', - shortcode: 'maltainvest', - }, - ]); - }); - - it('returns list of non-eu added and available accounts after some accounts are created', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAddedAccounts, - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).toEqual([ - { - is_added: true, - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'standard', - }, - { - is_added: true, - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'financial', - }, - { - is_added: false, - is_default_jurisdiction: 'true', - product: 'swap_free', - shortcode: 'svg', - }, - { - is_added: true, - is_virtual: false, - landing_company_short: 'bvi', - product: 'zero_spread', - }, - ]); - }); - - it('returns list of eu added and available accounts after some accounts are created', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5EUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: mockMT5EUAddedAccounts, - }); - (useIsEuRegion as jest.Mock).mockReturnValue({ - isEUCountry: true, - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).toEqual([ - { - is_added: true, - is_virtual: false, - landing_company_short: 'maltainvest', - product: 'financial', - }, - ]); - }); - - it('returns sorted non-eu accounts list in the correct order', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: [], - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data?.map(account => account.product)).toStrictEqual([ - 'standard', - 'financial', - 'swap_free', - 'zero_spread', - ]); - }); - - it('filters-out available MT5 financial stp account disabling clients to create it', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: [], - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).not.toContain({ - is_added: false, - is_default_jurisdiction: 'true', - product: 'stp', - shortcode: 'vanuatu', - }); - }); - - it('all available MT5 accounts are created', () => { - (useAvailableMT5Accounts as jest.Mock).mockReturnValue({ - data: mockMT5NonEUAvailableAccounts, - }); - (useMT5AccountsList as jest.Mock).mockReturnValue({ - data: [ - ...mockMT5NonEUAddedAccounts, - { - is_virtual: false, - landing_company_short: 'svg', - product: 'swap_free', - }, - ], - }); - - const { result } = renderHook(() => useSortedMT5Accounts()); - - expect(result.current.data).toEqual([ - { - is_added: true, - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'standard', - }, - { - is_added: true, - is_virtual: false, - landing_company_short: 'vanuatu', - product: 'financial', - }, - { - is_added: true, - is_virtual: false, - landing_company_short: 'svg', - product: 'swap_free', - }, - { - is_added: true, - is_virtual: false, - landing_company_short: 'bvi', - product: 'zero_spread', - }, - ]); - }); -}); diff --git a/packages/api-v2/src/hooks/useSortedMT5Accounts.ts b/packages/api-v2/src/hooks/useSortedMT5Accounts.ts index e85841d39c02..7db93f7d010f 100644 --- a/packages/api-v2/src/hooks/useSortedMT5Accounts.ts +++ b/packages/api-v2/src/hooks/useSortedMT5Accounts.ts @@ -28,42 +28,79 @@ const useSortedMT5Accounts = (regulation?: string) => { : account.landing_company_short !== 'maltainvest') ); - const available_accounts = filtered_available_accounts.filter(available => { - return ( - !filtered_mt5_accounts.find(added => added.product === available.product) && - // @ts-expect-error type for is_default_jurisdiction is unavailable in mt5_login_list and trading_platform_available_accounts - available.is_default_jurisdiction === 'true' && - /* - TODO: remove this check to filter out the creation of `stp` accounts in phase 2 of default jurisdiction - when this product type is separated from `financial` type. - */ - // @ts-expect-error type `stp` is unavailable in the type for trading_platform_available_accounts - available.product !== 'stp' - ); - }); + return filtered_available_accounts?.map(available_account => { + const created_account = filtered_mt5_accounts?.find(account => { + return ( + available_account.market_type === account.market_type && + available_account.shortcode === account.landing_company_short + ); + }); - const combined_accounts = [ - ...available_accounts.map(account => ({ ...account, is_added: false })), - ...filtered_mt5_accounts.map(account => ({ ...account, is_added: true })), - ]; - return combined_accounts; + if (created_account) + return { + ...created_account, + /** Determine if the account is added or not */ + is_added: true, + } as const; + + return { + ...available_account, + /** Determine if the account is added or not */ + is_added: false, + } as const; + }); }, [activeAccount?.is_virtual, all_available_mt5_accounts, isEU, mt5_accounts]); + // // Reduce out the added and non added accounts to make sure only one of each market_type is shown for not added + const filtered_data = useMemo(() => { + if (!modified_data) return; + + const added_accounts = modified_data.filter(account => account.is_added); + const non_added_accounts = modified_data.filter(account => !account.is_added); + + const filtered_non_added_accounts = non_added_accounts.reduce((acc, account) => { + const { market_type, product } = account; + const key = product === 'zero_spread' ? `${market_type}_${product}` : market_type; + + const existing_account = acc.find(acc_account => + acc_account.product === 'zero_spread' + ? `${acc_account.market_type}_${acc_account.product}` === key + : acc_account.market_type === key + ); + const added_account = added_accounts.find(acc_account => + acc_account.product === 'zero_spread' + ? `${acc_account.market_type}_${acc_account.product}` === key + : acc_account.market_type === key + ); + if (existing_account || added_account) return acc; + + return [...acc, account]; + }, [] as typeof non_added_accounts); + + return [...added_accounts, ...filtered_non_added_accounts]; + }, [modified_data]); + + // Sort the data by market_type and product to make sure the order is 'synthetic', 'financial', 'swap_free' and 'zero_spread' const sorted_data = useMemo(() => { - const sorting_order = ['standard', 'financial', 'stp', 'swap_free', 'zero_spread']; + const sorting_order = ['synthetic', 'financial', 'swap_free', 'zero_spread']; - if (!modified_data) return; + if (!filtered_data) return; const sorted_data = sorting_order.reduce((acc, sort_order) => { - const accounts = modified_data.filter(account => account.product === sort_order); + const accounts = filtered_data.filter(account => { + if (account.market_type === 'all') { + return account.product === sort_order; + } + return account.market_type === sort_order; + }); if (!accounts.length) return acc; return [...acc, ...accounts]; - }, [] as typeof modified_data); + }, [] as typeof filtered_data); return sorted_data; - }, [modified_data]); + }, [filtered_data]); - const areAllAccountsCreated = modified_data?.length === all_available_mt5_accounts?.length; + const areAllAccountsCreated = sorted_data?.length === all_available_mt5_accounts?.length; return { data: sorted_data, diff --git a/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.scss b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.scss new file mode 100644 index 000000000000..5f1047baca7e --- /dev/null +++ b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.scss @@ -0,0 +1,9 @@ +.wallets-cfd-modal-tnc { + display: flex; + flex-direction: column; + gap: 1.6rem; + + @include mobile-or-tablet-screen { + margin-top: auto; + } +} diff --git a/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.tsx b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.tsx new file mode 100644 index 000000000000..ed0ccb3c891c --- /dev/null +++ b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/CFDPasswordModalTnc.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Localize, useTranslations } from '@deriv-com/translations'; +import { Checkbox, InlineMessage, Text, useDevice } from '@deriv-com/ui'; +import { WalletLink } from '../../../../components/Base'; +import { useModal } from '../../../../components/ModalProvider'; +import { THooks, TPlatforms } from '../../../../types'; +import { companyNamesAndUrls, getMarketTypeDetails, PlatformDetails } from '../../constants'; +import './CFDPasswordModalTnc.scss'; + +export type TCFDPasswordModalTncProps = { + checked: boolean; + onChange: () => void; + platform: TPlatforms.All; + product?: THooks.AvailableMT5Accounts['product']; +}; + +const CFDPasswordModalTnc = ({ checked, onChange, platform, product }: TCFDPasswordModalTncProps) => { + const { isDesktop } = useDevice(); + const { getModalState } = useModal(); + const { localize } = useTranslations(); + const selectedJurisdiction = getModalState('selectedJurisdiction'); + const selectedCompany = companyNamesAndUrls[selectedJurisdiction as keyof typeof companyNamesAndUrls]; + const platformTitle = PlatformDetails[platform].title; + const productTitle = getMarketTypeDetails(localize, product).all.title; + + return ( +
+ + + + + + + ]} + i18n_default_text='I confirm and accept {{company}}’s <0>terms and conditions' + values={{ + company: selectedCompany.name, + }} + /> + + } + name='zerospread-checkbox' + onChange={onChange} + /> +
+ ); +}; + +export default CFDPasswordModalTnc; diff --git a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/__tests__/MT5PasswordModalTnc.spec.tsx b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/__tests__/CFDPasswordModalTnc.spec.tsx similarity index 54% rename from packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/__tests__/MT5PasswordModalTnc.spec.tsx rename to packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/__tests__/CFDPasswordModalTnc.spec.tsx index 35e05b9cb1ac..820b654032c3 100644 --- a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/__tests__/MT5PasswordModalTnc.spec.tsx +++ b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/__tests__/CFDPasswordModalTnc.spec.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; -import MT5PasswordModalTnc, { type TMT5PasswordModalTncProps } from '../MT5PasswordModalTnc'; +import CFDPasswordModalTnc, { type TCFDPasswordModalTncProps } from '../CFDPasswordModalTnc'; jest.mock('@deriv-com/ui', () => ({ Checkbox: jest.fn(({ checked, label, onChange }) => ( @@ -18,44 +18,53 @@ jest.mock('@deriv-com/ui', () => ({ useDevice: jest.fn(() => ({ isDesktop: true })), })); -jest.mock('../../../../../../components/ModalProvider', () => ({ +jest.mock('../../../../../components/ModalProvider', () => ({ useModal: jest.fn(() => ({ getModalState: jest.fn(() => 'bvi'), })), })); -jest.mock('../../../../../../components/Base/WalletLink', () => ({ +jest.mock('../../../../../components/Base/WalletLink', () => ({ WalletLink: ({ children }: { children: React.ReactNode }) => {children}, })); const mockOnChange = jest.fn(); -describe('MT5PasswordModalTnc', () => { - const defaultProps: TMT5PasswordModalTncProps = { +describe('CFDPasswordModalTnc', () => { + const defaultProps: TCFDPasswordModalTncProps = { checked: false, onChange: mockOnChange, + platform: 'mt5', + product: 'zero_spread', }; it('renders correctly', () => { - render(); - expect(screen.getByTestId('dt_wallets_mt5_tnc_checkbox')).toBeInTheDocument(); + render(); + expect(screen.getByTestId('dt_wallets_tnc_checkbox')).toBeInTheDocument(); + expect(screen.getByTestId('dt_wallets_tnc_inline_message')).toBeInTheDocument(); }); it('displays correct text content', () => { - render(); - expect(screen.getByText("I confirm and accept Deriv (BVI) Ltd's")).toBeInTheDocument(); + render(); + expect(screen.getByText(/You are adding your Deriv MT5/i)).toBeInTheDocument(); + expect(screen.getByText(/I confirm and accept/i)).toBeInTheDocument(); }); it('handles checkbox change', () => { - render(); - const checkbox = screen.getByTestId('dt_wallets_mt5_tnc_checkbox'); + render(); + const checkbox = screen.getByTestId('dt_wallets_tnc_checkbox'); fireEvent.click(checkbox); expect(mockOnChange).toHaveBeenCalledTimes(1); }); it('renders the terms and conditions link', () => { - render(); + render(); const link = screen.getByText('terms and conditions'); expect(link).toHaveAttribute('href', 'https://example.com'); }); + + it('uses the correct platform and product titles', () => { + render(); + expect(screen.getByText(/MT5.*Zero Spread/)).toBeInTheDocument(); + }); }); diff --git a/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/index.ts b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/index.ts new file mode 100644 index 000000000000..bea48cf4ec04 --- /dev/null +++ b/packages/wallets/src/features/cfd/components/CFDPasswordModalTnc/index.ts @@ -0,0 +1 @@ +export { default as CFDPasswordModalTnc } from './CFDPasswordModalTnc'; diff --git a/packages/wallets/src/features/cfd/components/CFDPlatformsListAccounts/CFDPlatformsListAccounts.tsx b/packages/wallets/src/features/cfd/components/CFDPlatformsListAccounts/CFDPlatformsListAccounts.tsx index 0a3ea97a7b3c..68f97577d784 100644 --- a/packages/wallets/src/features/cfd/components/CFDPlatformsListAccounts/CFDPlatformsListAccounts.tsx +++ b/packages/wallets/src/features/cfd/components/CFDPlatformsListAccounts/CFDPlatformsListAccounts.tsx @@ -9,7 +9,6 @@ import { AvailableDxtradeAccountsList, AvailableMT5AccountsList, } from '../../flows'; -import { TAddedMT5Account, TAvailableMT5Account } from '../../types'; import './CFDPlatformsListAccounts.scss'; const CFDPlatformsListAccounts: React.FC = () => { @@ -56,18 +55,10 @@ const CFDPlatformsListAccounts: React.FC = () => {
{mt5AccountsList?.map((account, index) => { if (account.is_added) - return ( - - ); + return ; return ( - + ); })} {!isRestricted && ( diff --git a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.scss b/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.scss deleted file mode 100644 index 101e4280e4a1..000000000000 --- a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.scss +++ /dev/null @@ -1,9 +0,0 @@ -.wallets-client-verification-badge { - margin-inline-end: auto; - - &__content { - &--underlined { - text-decoration: underline; - } - } -} diff --git a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.tsx b/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.tsx deleted file mode 100644 index 6eea858836d2..000000000000 --- a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/ClientVerificationStatusBadge.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { - LabelPairedCircleCheckCaptionBoldIcon, - LabelPairedCircleExclamationCaptionBoldIcon, - LabelPairedClockThreeCaptionBoldIcon, - LabelPairedTriangleExclamationCaptionBoldIcon, -} from '@deriv/quill-icons'; -import { useTranslations } from '@deriv-com/translations'; -import { Badge, Text, useDevice } from '@deriv-com/ui'; -import { TTranslations } from '../../../../types'; -import './ClientVerificationStatusBadge.scss'; - -type TBadgeColor = React.ComponentProps['color']; - -const getBadgeVariations = (localize: TTranslations['localize']) => { - return { - failed: { - color: 'danger-secondary', - content: localize('Failed'), - icon: , - }, - in_review: { - color: 'warning-secondary', - content: localize('In review'), - icon: , - }, - needs_verification: { - color: 'blue-secondary', - content: localize('Needs verification'), - icon: , - }, - verified: { - color: 'success-secondary', - content: localize('Verified'), - icon: , - }, - }; -}; - -type TClientVerificationBadgeProps = { - onClick?: VoidFunction; - variant: keyof ReturnType; -}; - -const ClientVerificationStatusBadge: React.FC = ({ onClick, variant }) => { - const { localize } = useTranslations(); - const { isDesktop } = useDevice(); - const { color, content, icon } = getBadgeVariations(localize)[variant]; - return ( - { - if (onClick) { - e.stopPropagation(); - onClick(); - } - }} - > - - {content} - - - ); -}; - -export default ClientVerificationStatusBadge; diff --git a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/index.ts b/packages/wallets/src/features/cfd/components/ClientVerificationBadge/index.ts deleted file mode 100644 index 7c8dfffb6e93..000000000000 --- a/packages/wallets/src/features/cfd/components/ClientVerificationBadge/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ClientVerificationStatusBadge } from './ClientVerificationStatusBadge'; diff --git a/packages/wallets/src/features/cfd/components/PlatformStatusBadge/PlatformStatusBadge.tsx b/packages/wallets/src/features/cfd/components/PlatformStatusBadge/PlatformStatusBadge.tsx index eabf13023033..61faecd6b7b0 100644 --- a/packages/wallets/src/features/cfd/components/PlatformStatusBadge/PlatformStatusBadge.tsx +++ b/packages/wallets/src/features/cfd/components/PlatformStatusBadge/PlatformStatusBadge.tsx @@ -3,15 +3,15 @@ import { useTradingPlatformStatus } from '@deriv/api-v2'; import { LegacyWarningIcon } from '@deriv/quill-icons'; import { useTranslations } from '@deriv-com/translations'; import { Badge, Text } from '@deriv-com/ui'; +import { THooks } from '../../../../types'; import type { TAccount } from '../../../cashier/modules/Transfer/types'; import { MT5_ACCOUNT_STATUS, TRADING_PLATFORM_STATUS } from '../../constants'; -import { TAddedMT5Account } from '../../types'; type TProps = { badgeSize: ComponentProps['badgeSize']; cashierAccount?: TAccount; className?: ComponentProps['className']; - mt5Account?: TAddedMT5Account; + mt5Account?: THooks.MT5AccountsList; }; const PlatformStatusBadge: React.FC = ({ badgeSize, cashierAccount, className, mt5Account }) => { diff --git a/packages/wallets/src/features/cfd/components/index.ts b/packages/wallets/src/features/cfd/components/index.ts index 04b24a2fe7fc..ac62e79079c5 100644 --- a/packages/wallets/src/features/cfd/components/index.ts +++ b/packages/wallets/src/features/cfd/components/index.ts @@ -1,5 +1,4 @@ export * from './CFDPlatformsListAccounts'; -export * from './ClientVerificationBadge'; export * from './CompareAccountsCarousel'; export * from './ModalTradeWrapper'; export * from './PlatformStatusBadge'; diff --git a/packages/wallets/src/features/cfd/constants.tsx b/packages/wallets/src/features/cfd/constants.tsx index 07a36a720225..bd51f9972fba 100644 --- a/packages/wallets/src/features/cfd/constants.tsx +++ b/packages/wallets/src/features/cfd/constants.tsx @@ -185,24 +185,12 @@ export const MT5_ACCOUNT_STATUS = { FAILED: 'failed', MIGRATED_WITH_POSITION: 'migrated_with_position', MIGRATED_WITHOUT_POSITION: 'migrated_without_position', - PENDING: 'pending', - UNAVAILABLE: 'unavailable', - UNDER_MAINTENANCE: 'under_maintenance', - // TODO: remove all the statuses below once the KYC statuses are consolidated by BE - // eslint-disable-next-line sort-keys - POA_FAILED: 'poa_failed', - POA_OUTDATED: 'poa_outdated', - PROOF_FAILED: 'proof_failed', - - // eslint-disable-next-line sort-keys NEEDS_VERIFICATION: 'needs_verification', - POA_REQUIRED: 'poa_required', - - // eslint-disable-next-line sort-keys + PENDING: 'pending', POA_PENDING: 'poa_pending', - VERIFICATION_PENDING: 'verification_pending', - // eslint-disable-next-line sort-keys POA_VERIFIED: 'poa_verified', + UNAVAILABLE: 'unavailable', + UNDER_MAINTENANCE: 'under_maintenance', } as const; /** diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.scss b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.scss index 80a851dae2be..b2a54c389c1a 100644 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.scss +++ b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.scss @@ -94,8 +94,5 @@ &--pending { opacity: 0.5; } - &--disabled { - opacity: 0.48; - } } } diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.tsx b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.tsx index dab700346633..d6d4f84ae9ba 100644 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.tsx +++ b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/AddedMT5AccountsList.tsx @@ -1,67 +1,161 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import classNames from 'classnames'; +import { useJurisdictionStatus, useTradingPlatformStatus } from '@deriv/api-v2'; import { LabelPairedChevronLeftCaptionRegularIcon, LabelPairedChevronRightCaptionRegularIcon, + LabelPairedCircleExclamationLgBoldIcon, + LabelPairedTriangleExclamationMdBoldIcon, } from '@deriv/quill-icons'; -import { useTranslations } from '@deriv-com/translations'; -import { Text } from '@deriv-com/ui'; +import { Localize, useTranslations } from '@deriv-com/translations'; +import { InlineMessage, Text } from '@deriv-com/ui'; import { WalletDisabledAccountModal, WalletStatusBadge } from '../../../../../components'; import { useModal } from '../../../../../components/ModalProvider'; import { TradingAccountCard } from '../../../../../components/TradingAccountCard'; import useIsRtl from '../../../../../hooks/useIsRtl'; -import { ClientVerificationStatusBadge, PlatformStatusBadge } from '../../../components'; -import { MARKET_TYPE, PlatformDetails } from '../../../constants'; -import { ClientVerificationModal, MT5TradeModal, TradingPlatformStatusModal } from '../../../modals'; -import { TAddedMT5Account } from '../../../types'; -import { useAddedMT5Account } from './hooks'; +import { THooks } from '../../../../../types'; +import { PlatformStatusBadge } from '../../../components/PlatformStatusBadge'; +import { + getMarketTypeDetails, + JURISDICTION, + MARKET_TYPE, + MT5_ACCOUNT_STATUS, + PlatformDetails, + TRADING_PLATFORM_STATUS, +} from '../../../constants'; +import { MT5TradeModal, TradingPlatformStatusModal, VerificationFailedModal } from '../../../modals'; import './AddedMT5AccountsList.scss'; type TProps = { - account: TAddedMT5Account; + account: THooks.MT5AccountsList; +}; + +type TTradingAccountJurisdictionStatusInfoProps = { + isAccountDisabled?: boolean; + isJurisdictionFailure?: boolean; + isJurisdictionPending?: boolean; + selectedJurisdiction: THooks.MT5AccountsList['landing_company_short']; +}; + +const TradingAccountJurisdictionStatusInfo: React.FC = ({ + isAccountDisabled, + isJurisdictionFailure, + isJurisdictionPending, + selectedJurisdiction, +}) => { + const { show } = useModal(); + if (isAccountDisabled) { + return ; + } + if (isJurisdictionPending) { + return ( + + } + > + + + + + ); + } + + if (isJurisdictionFailure) { + return ( + + } + > + + + show(, { + defaultRootId: 'wallets_modal_root', + }) + } + />, + ]} + i18n_default_text='Verification failed <0>Why?' + /> + + + ); + } + + return null; }; const AddedMT5AccountsList: React.FC = ({ account }) => { + const [shouldShowDisabledAccountModal, setShouldShowDisabledAccountModal] = useState(false); + const { getVerificationStatus } = useJurisdictionStatus(); const { localize } = useTranslations(); const isRtl = useIsRtl(); - const { accountDetails, isAccountDisabled, isServerMaintenance, kycStatus, showMT5TradeModal, showPlatformStatus } = - useAddedMT5Account(account); - + const jurisdictionStatus = useMemo( + () => getVerificationStatus(account.landing_company_short || JURISDICTION.SVG, account.status), + [account.landing_company_short, account.status, getVerificationStatus] + ); + const { title } = getMarketTypeDetails(localize, account.product)[account.market_type ?? MARKET_TYPE.ALL]; const { show } = useModal(); - const [showDisabledAccountModal, setShowDisabledAccountModal] = useState(false); + const { getPlatformStatus } = useTradingPlatformStatus(); + const platformStatus = getPlatformStatus(account.platform); + + const hasPlatformStatus = + account.status === TRADING_PLATFORM_STATUS.UNAVAILABLE || + account.status === MT5_ACCOUNT_STATUS.UNDER_MAINTENANCE || + platformStatus === TRADING_PLATFORM_STATUS.MAINTENANCE; + + const isServerMaintenance = + platformStatus === TRADING_PLATFORM_STATUS.MAINTENANCE || + account.status === MT5_ACCOUNT_STATUS.UNDER_MAINTENANCE; + const showPlatformStatus = hasPlatformStatus && !(jurisdictionStatus.is_pending || jurisdictionStatus.is_failed); + // @ts-expect-error The enabled property exists, but the api-types are invalid + const isAccountDisabled = !account?.rights?.enabled; + const shouldShowBalance = !(jurisdictionStatus.is_failed || jurisdictionStatus.is_pending) && !isAccountDisabled; return ( <> { if (isAccountDisabled) { - setShowDisabledAccountModal(true); - return; + return setShouldShowDisabledAccountModal(true); } - - if (showPlatformStatus) { + if (hasPlatformStatus) return show(, { defaultRootId: 'wallets_modal_root', }); - } - - if (showMT5TradeModal) { - return show( - , - { defaultRootId: 'wallets_modal_root' } - ); + if (platformStatus === TRADING_PLATFORM_STATUS.ACTIVE) { + return jurisdictionStatus.is_failed + ? show(, { + defaultRootId: 'wallets_modal_root', + }) + : show( + + ); } }} > - {accountDetails.icon} + {getMarketTypeDetails(localize, account.product)[account.market_type || MARKET_TYPE.ALL].icon} = ({ account }) => { })} >
- {accountDetails.title} + {title}
- {!isAccountDisabled && !kycStatus && ( + {shouldShowBalance && ( {account.display_balance} )} + {account.display_login} - {!isAccountDisabled && kycStatus && ( - - show(, { - defaultRootId: 'wallets_modal_root', - }) - } - variant={kycStatus} - /> - )} - {isAccountDisabled && } +
{showPlatformStatus ? ( @@ -105,7 +194,7 @@ const AddedMT5AccountsList: React.FC = ({ account }) => { mt5Account={account} /> ) : ( -
+
{isRtl ? ( ) : ( @@ -118,8 +207,8 @@ const AddedMT5AccountsList: React.FC = ({ account }) => { setShowDisabledAccountModal(false)} + isVisible={shouldShowDisabledAccountModal} + onClose={() => setShouldShowDisabledAccountModal(false)} /> ); diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/__tests__/AddedMT5AccountsList.spec.tsx b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/__tests__/AddedMT5AccountsList.spec.tsx index 1b5f66f84d52..7157576d8451 100644 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/__tests__/AddedMT5AccountsList.spec.tsx +++ b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/__tests__/AddedMT5AccountsList.spec.tsx @@ -1,220 +1,160 @@ import React from 'react'; +import { useJurisdictionStatus, useTradingPlatformStatus } from '@deriv/api-v2'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { ModalProvider } from '../../../../../../components/ModalProvider'; -import { PlatformDetails } from '../../../../constants'; +import { useModal } from '../../../../../../components/ModalProvider'; +import { MT5TradeModal, TradingPlatformStatusModal, VerificationFailedModal } from '../../../../modals'; import AddedMT5AccountsList from '../AddedMT5AccountsList'; -import { useAddedMT5Account } from '../hooks'; -// mock function to check if correct props are passed to the modal components -const mockPropsFn = jest.fn(); - -jest.mock('../hooks', () => ({ - useAddedMT5Account: jest.fn(), -})); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useHistory: jest.fn(() => ({ - push: jest.fn(), - })), -})); - -jest.mock('../../../../components', () => ({ - ...jest.requireActual('../../../../components'), - ClientVerificationStatusBadge: jest.fn(props => { - mockPropsFn(props.variant); - return ( -
{ - e.stopPropagation(); - props.onClick(); - }} - > - ClientVerificationStatusBadge -
- ); - }), - PlatformStatusBadge: jest.fn(props => { - mockPropsFn(props); - return
PlatformStatusBadge
; - }), -})); - -jest.mock('../../../../modals', () => ({ - ...jest.requireActual('../../../../modals'), - ClientVerificationModal: jest.fn(props => { - mockPropsFn(props); - return
ClientVerificationModal
; - }), - MT5TradeModal: jest.fn(props => { - mockPropsFn(props); - return
MT5TradeModal
; - }), - TradingPlatformStatusModal: jest.fn(props => { - mockPropsFn(props); - return
TradingPlatformStatusModal
; - }), +jest.mock('@deriv/api-v2', () => ({ + useJurisdictionStatus: jest.fn(), + useTradingPlatformStatus: jest.fn(), })); -jest.mock('../../../../../../components', () => ({ - ...jest.requireActual('../../../../../../components'), - WalletDisabledAccountModal: jest.fn(props => { - mockPropsFn(props); - return
WalletDisabledAccountModal
; - }), - WalletStatusBadge: jest.fn(props => { - mockPropsFn(props); - return
WalletStatusBadge
; - }), +jest.mock('../../../../../../components/ModalProvider', () => ({ + useModal: jest.fn(), })); -const mockAccount = { - display_balance: 'USD 1000.00', - display_login: '12345678', - landing_company_short: 'svg', - market_type: 'financial', - platform: 'mt5', - product: 'financial', - status: 'active', -}; - -const mockUseAddedMT5AccountData = { - accountDetails: { - icon: ( - <> - icon-{mockAccount.platform}-{mockAccount.product} - - ), - title: 'Financial', - }, - isServerMaintenance: false, - showClientVerificationModal: false, - showMT5TradeModal: true, - showPlatformStatus: false, -}; - -const wrapper: React.FC = ({ children }) => ( - <> - {children} - -); - describe('AddedMT5AccountsList', () => { - // const mockShow = jest.fn(); + const mockAccount = { + display_balance: 'USD 1000.00', + display_login: '12345678', + landing_company_short: 'svg', + market_type: 'financial', + platform: 'mt5', + product: 'standard', + rights: { enabled: true }, + status: 'active', + }; + + const mockShow = jest.fn(); - beforeAll(() => { - const modalRoot = document.createElement('div'); - modalRoot.setAttribute('id', 'wallets_modal_root'); - document.body.appendChild(modalRoot); - }); beforeEach(() => { - (useAddedMT5Account as jest.Mock).mockReturnValue(mockUseAddedMT5AccountData); + (useJurisdictionStatus as jest.Mock).mockReturnValue({ + getVerificationStatus: jest.fn().mockReturnValue({ is_failed: false, is_pending: false }), + }); + (useTradingPlatformStatus as jest.Mock).mockReturnValue({ + getPlatformStatus: jest.fn().mockReturnValue('active'), + }); + (useModal as jest.Mock).mockReturnValue({ show: mockShow }); }); - it('displays added mt5 account with correct account details', () => { + it('renders added mt5 accounts list with correct account details', () => { // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); + render(); - expect(screen.getByText('icon-mt5-financial')).toBeInTheDocument(); expect(screen.getByText('Financial')).toBeInTheDocument(); expect(screen.getByText('USD 1000.00')).toBeInTheDocument(); expect(screen.getByText('12345678')).toBeInTheDocument(); }); - it('displays correct variant of ClientVerificationStatusBadge and renders modal with ClientVerificationModal when clicked on it', async () => { - (useAddedMT5Account as jest.Mock).mockReturnValue({ - ...mockUseAddedMT5AccountData, - kycStatus: 'mockKycStatus', - }); - + it('shows MT5TradeModal when list is clicked and status is active', async () => { // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); - - const badge = screen.getByText('ClientVerificationStatusBadge'); + render(); - expect(badge).toBeInTheDocument(); - expect(mockPropsFn).toBeCalledWith('mockKycStatus'); - - userEvent.click(badge); + userEvent.click(screen.getByTestId('dt_wallets_trading_account_card')); await waitFor(() => { - expect(screen.getByText('ClientVerificationModal')).toBeInTheDocument(); + expect(mockShow).toHaveBeenCalledWith( + // @ts-expect-error - since this is a mock, we only need partial properties of the account + + ); }); }); - it('shows the disabled badge when the account MT5 account is disabled', () => { - (useAddedMT5Account as jest.Mock).mockReturnValue({ - ...mockUseAddedMT5AccountData, - isAccountDisabled: true, - isServerMaintenance: true, - showPlatformStatus: true, + it('shows TradingPlatformStatusModal when platform is under maintenance', async () => { + (useTradingPlatformStatus as jest.Mock).mockReturnValue({ + getPlatformStatus: jest.fn().mockReturnValue('maintenance'), }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); + render(); - expect(screen.getByText('WalletStatusBadge')).toBeInTheDocument(); - expect(mockPropsFn).toBeCalledWith({ - badgeSize: 'md', - padding: 'tight', - status: 'disabled', + userEvent.click(screen.getByTestId('dt_wallets_trading_account_card')); + + await waitFor(() => { + expect(mockShow).toHaveBeenCalledWith(, { + defaultRootId: 'wallets_modal_root', + }); }); }); - it('shows MT5TradeModal when list is clicked and status is active', async () => { + it('shows VerificationFailedModal when verification has failed', async () => { + (useJurisdictionStatus as jest.Mock).mockReturnValue({ + getVerificationStatus: jest.fn().mockReturnValue({ is_failed: true, is_pending: false }), + }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); + render(); userEvent.click(screen.getByTestId('dt_wallets_trading_account_card')); await waitFor(() => { - expect(screen.getByText('MT5TradeModal')).toBeInTheDocument(); - expect(mockPropsFn).toBeCalledWith({ - marketType: mockAccount.market_type, - mt5Account: mockAccount, - platform: PlatformDetails.mt5.platform, + expect(mockShow).toHaveBeenCalledWith(, { + defaultRootId: 'wallets_modal_root', }); }); }); - it('shows TradingPlatformStatusModal when platform is under maintenance', async () => { - (useAddedMT5Account as jest.Mock).mockReturnValue({ - ...mockUseAddedMT5AccountData, - isServerMaintenance: true, - showPlatformStatus: true, + it('displays pending verification message when status is pending', () => { + (useJurisdictionStatus as jest.Mock).mockReturnValue({ + getVerificationStatus: jest.fn().mockReturnValue({ is_failed: false, is_pending: true }), }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); + render(); - userEvent.click(screen.getByTestId('dt_wallets_trading_account_card')); + expect(screen.getByText('Pending verification')).toBeInTheDocument(); + }); - await waitFor(() => { - expect(screen.getByText('TradingPlatformStatusModal')).toBeInTheDocument(); - expect(mockPropsFn).toBeCalledWith({ - isServerMaintenance: true, - }); + it('displays verification failed message when verification has failed', () => { + (useJurisdictionStatus as jest.Mock).mockReturnValue({ + getVerificationStatus: jest.fn().mockReturnValue({ is_failed: true, is_pending: false }), }); + // @ts-expect-error - since this is a mock, we only need partial properties of the account + render(); + + expect(screen.getByText('Verification failed')).toBeInTheDocument(); + expect(screen.getByText('Why?')).toBeInTheDocument(); }); - it('shows the WalletDisabledAccountModal when a disabled account MT5 account is clicked', async () => { - (useAddedMT5Account as jest.Mock).mockReturnValue({ - ...mockUseAddedMT5AccountData, - isAccountDisabled: true, - isServerMaintenance: true, - showPlatformStatus: true, + it('displays VerificationFailedModal when "Why?" link is clicked', async () => { + (useJurisdictionStatus as jest.Mock).mockReturnValue({ + getVerificationStatus: jest.fn().mockReturnValue({ is_failed: true, is_pending: false }), }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(, { wrapper }); + render(); - await waitFor(() => { - userEvent.click(screen.getByText('WalletStatusBadge')); - }); + const link = screen.getByText('Why?'); + userEvent.click(link); await waitFor(() => { - expect(screen.getByText('WalletDisabledAccountModal')).toBeInTheDocument(); + expect(mockShow).toHaveBeenCalledWith(, { + defaultRootId: 'wallets_modal_root', + }); }); }); + + it('shows WalletStatusBadge when account is disabled', () => { + // @ts-expect-error - since this is a mock, we only need partial properties of the account + render(); + + expect(screen.getByText('Disabled')).toBeInTheDocument(); + }); + + it('opens WalletDisabledAccountModal when disabled account card is clicked', async () => { + // @ts-expect-error - since this is a mock, we only need partial properties of the account + render(); + + const card = screen.getByTestId('dt_wallets_trading_account_card'); + await userEvent.click(card); + + expect(screen.getByText('Contact us via live chat for more details.')).toBeInTheDocument(); + + const closeButton = screen.getByTestId('dt-close-icon'); + expect(closeButton).toBeInTheDocument(); + await userEvent.click(closeButton); + + expect(screen.queryByText('Contact us via live chat for more details.')).not.toBeInTheDocument(); + }); }); diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/__tests__/useAddedMT5Account.spec.ts b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/__tests__/useAddedMT5Account.spec.ts deleted file mode 100644 index 0d7af35c6215..000000000000 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/__tests__/useAddedMT5Account.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { useTradingPlatformStatus } from '@deriv/api-v2'; -import { cleanup } from '@testing-library/react'; -import { renderHook } from '@testing-library/react-hooks'; -import { getMarketTypeDetails } from '../../../../../constants'; -import { TAddedMT5Account } from '../../../../../types'; -import useAddedMT5Account from '../useAddedMT5Account'; - -jest.mock('@deriv/api-v2', () => ({ - ...jest.requireActual('@deriv/api-v2'), - useTradingPlatformStatus: jest.fn(), -})); - -jest.mock('../../../../../constants', () => ({ - ...jest.requireActual('../../../../../constants'), - getMarketTypeDetails: jest.fn(), -})); - -const mockAccount = { - market_type: 'financial', - product: 'financial', - status: '', -} as TAddedMT5Account; - -describe('useAddedMT5Account', () => { - beforeEach(() => { - (useTradingPlatformStatus as jest.Mock).mockReturnValue({ - getPlatformStatus: jest.fn(), - }); - }); - afterEach(cleanup); - - it('provides correct account details based on the market type', () => { - (getMarketTypeDetails as jest.Mock).mockReturnValue({ financial: 'mock-account-details' }); - - const { result } = renderHook(() => useAddedMT5Account(mockAccount)); - - expect(result.current.accountDetails).toEqual('mock-account-details'); - }); - - it('isServerMaintenance is `true` when trading platform status is `maintenance`', () => { - (useTradingPlatformStatus as jest.Mock).mockReturnValue({ - getPlatformStatus: jest.fn(() => 'maintenance'), - }); - - const { result } = renderHook(() => useAddedMT5Account(mockAccount)); - - expect(result.current.isServerMaintenance).toEqual(true); - }); - - it('isServerMaintenance is `true` when account status is `under_maintenance`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'under_maintenance' })); - - expect(result.current.isServerMaintenance).toEqual(true); - }); - - it('kycStatus is `failed` when status received for account is `proof_failed`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'proof_failed' })); - - expect(result.current.kycStatus).toEqual('failed'); - }); - - it('kycStatus is `failed` when status received for account is `poa_failed`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'poa_failed' })); - - expect(result.current.kycStatus).toEqual('failed'); - }); - - it('kycStatus is `in_review` when status received for account is `verification_pending`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'verification_pending' })); - - expect(result.current.kycStatus).toEqual('in_review'); - }); - - it('kycStatus is `needs_verification` when status received for account is `needs_verification`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'needs_verification' })); - - expect(result.current.kycStatus).toEqual('needs_verification'); - }); - - it('showMT5TradeModal is `true` when platform status is `active`', () => { - (useTradingPlatformStatus as jest.Mock).mockReturnValue({ - getPlatformStatus: jest.fn(() => 'active'), - }); - - const { result } = renderHook(() => useAddedMT5Account(mockAccount)); - - expect(result.current.showMT5TradeModal).toEqual(true); - }); - - it('showPlatformStatus is `true` when account status is `unavailable`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'unavailable' })); - - expect(result.current.showPlatformStatus).toEqual(true); - }); - - it('showPlatformStatus is `true` when account status is `under_maintenance`', () => { - const { result } = renderHook(() => useAddedMT5Account({ ...mockAccount, status: 'under_maintenance' })); - - expect(result.current.showPlatformStatus).toEqual(true); - }); - - it('showPlatformStatus is `true` when trading platform status is `maintenance`', () => { - (useTradingPlatformStatus as jest.Mock).mockReturnValue({ - getPlatformStatus: jest.fn(() => 'maintenance'), - }); - - const { result } = renderHook(() => useAddedMT5Account(mockAccount)); - - expect(result.current.showPlatformStatus).toEqual(true); - }); -}); diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/index.ts b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/index.ts deleted file mode 100644 index 1ac54db629bf..000000000000 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as useAddedMT5Account } from './useAddedMT5Account'; diff --git a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/useAddedMT5Account.ts b/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/useAddedMT5Account.ts deleted file mode 100644 index e02189eab358..000000000000 --- a/packages/wallets/src/features/cfd/flows/MT5/AddedMT5AccountsList/hooks/useAddedMT5Account.ts +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useMemo } from 'react'; -import { useTradingPlatformStatus } from '@deriv/api-v2'; -import { useTranslations } from '@deriv-com/translations'; -import { ClientVerificationStatusBadge } from '../../../../components'; -import { getMarketTypeDetails, MARKET_TYPE, MT5_ACCOUNT_STATUS, TRADING_PLATFORM_STATUS } from '../../../../constants'; -import { TAddedMT5Account } from '../../../../types'; - -type TBadgeVariations = Partial['variant']> | undefined; - -const getClientKycStatus = (status: TAddedMT5Account['status']): TBadgeVariations => { - switch (status) { - case MT5_ACCOUNT_STATUS.POA_FAILED: - case MT5_ACCOUNT_STATUS.PROOF_FAILED: - return 'failed'; - case MT5_ACCOUNT_STATUS.VERIFICATION_PENDING: - case MT5_ACCOUNT_STATUS.POA_PENDING: - return 'in_review'; - case MT5_ACCOUNT_STATUS.NEEDS_VERIFICATION: - case MT5_ACCOUNT_STATUS.POA_REQUIRED: - return 'needs_verification'; - default: - } -}; - -const useAddedMT5Account = (account: TAddedMT5Account) => { - const { localize } = useTranslations(); - - // @ts-expect-error The enabled property exists, but the api-types are invalid - const isAccountDisabled = !account.rights?.enabled; - - const accountDetails = useMemo( - () => getMarketTypeDetails(localize, account.product)[account.market_type ?? MARKET_TYPE.ALL], - [account.market_type, account.product, localize] - ); - - const { getPlatformStatus } = useTradingPlatformStatus(); - const platformStatus = getPlatformStatus(account.platform); - const kycStatus = getClientKycStatus(account.status); - - const isServerMaintenance = - platformStatus === TRADING_PLATFORM_STATUS.MAINTENANCE || - account.status === MT5_ACCOUNT_STATUS.UNDER_MAINTENANCE; - - const showPlatformStatus = - account.status === MT5_ACCOUNT_STATUS.UNAVAILABLE || - account.status === MT5_ACCOUNT_STATUS.UNDER_MAINTENANCE || - platformStatus === TRADING_PLATFORM_STATUS.MAINTENANCE; - - const showMT5TradeModal = platformStatus === TRADING_PLATFORM_STATUS.ACTIVE; - - return { - accountDetails, - isAccountDisabled, - isServerMaintenance, - kycStatus, - showMT5TradeModal, - showPlatformStatus, - }; -}; - -export default useAddedMT5Account; diff --git a/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/AvailableMT5AccountsList.tsx b/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/AvailableMT5AccountsList.tsx index 3d6c53ed996e..310168f8c1a3 100644 --- a/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/AvailableMT5AccountsList.tsx +++ b/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/AvailableMT5AccountsList.tsx @@ -1,22 +1,25 @@ -import React, { useCallback } from 'react'; +import React, { lazy, Suspense, useCallback, useEffect, useState } from 'react'; import { useActiveWalletAccount, useMT5AccountsList, useTradingPlatformStatus } from '@deriv/api-v2'; import { LabelPairedChevronLeftCaptionRegularIcon, LabelPairedChevronRightCaptionRegularIcon, } from '@deriv/quill-icons'; import { Localize, useTranslations } from '@deriv-com/translations'; -import { Text } from '@deriv-com/ui'; +import { Loader, Text } from '@deriv-com/ui'; import { TradingAccountCard } from '../../../../../components'; import { useModal } from '../../../../../components/ModalProvider'; import useIsRtl from '../../../../../hooks/useIsRtl'; +import { THooks } from '../../../../../types'; import { getMarketTypeDetails, MARKET_TYPE, PRODUCT, TRADING_PLATFORM_STATUS } from '../../../constants'; -import { ClientVerificationModal, MT5PasswordModal, TradingPlatformStatusModal } from '../../../modals'; -import { TAvailableMT5Account } from '../../../types'; -import { getClientVerification } from '../../../utils'; +import { JurisdictionModal, MT5PasswordModal, TradingPlatformStatusModal } from '../../../modals'; import './AvailableMT5AccountsList.scss'; +const LazyVerification = lazy( + () => import(/* webpackChunkName: "wallets-client-verification" */ '../../ClientVerification/ClientVerification') +); + type TProps = { - account: TAvailableMT5Account; + account: THooks.AvailableMT5Accounts; }; const AvailableMT5AccountsList: React.FC = ({ account }) => { @@ -28,11 +31,10 @@ const AvailableMT5AccountsList: React.FC = ({ account }) => { const { description, title } = getMarketTypeDetails(localize, account.product)[ account.market_type || MARKET_TYPE.ALL ]; + const [showMt5PasswordModal, setShowMt5PasswordModal] = useState(false); const { data: mt5Accounts } = useMT5AccountsList(); const platformStatus = getPlatformStatus(account.platform); const hasUnavailableAccount = mt5Accounts?.some(account => account.status === 'unavailable'); - const isVirtual = activeWallet?.is_virtual; - const { hasClientKycStatus } = getClientVerification(account); const onButtonClick = useCallback(() => { if (hasUnavailableAccount) return show(); @@ -44,16 +46,58 @@ const AvailableMT5AccountsList: React.FC = ({ account }) => { return show(); case TRADING_PLATFORM_STATUS.ACTIVE: default: - if (!isVirtual && hasClientKycStatus) { - show(); + if (activeWallet?.is_virtual || account.product === PRODUCT.SWAPFREE) { + show( + + ); + } else if (account.product === PRODUCT.ZEROSPREAD) { + show( + }> + { + setShowMt5PasswordModal(true); + }} + selectedJurisdiction={account.shortcode} + /> + + ); } else { - show(); + show(); } setModalState('marketType', account.market_type); setModalState('selectedJurisdiction', account.shortcode); break; } - }, [hasUnavailableAccount, show, platformStatus, isVirtual, hasClientKycStatus, setModalState, account]); + }, [ + hasUnavailableAccount, + show, + platformStatus, + account.platform, + account.market_type, + account.product, + account.shortcode, + activeWallet?.is_virtual, + setModalState, + ]); + + useEffect(() => { + if (showMt5PasswordModal) { + show( + + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showMt5PasswordModal]); return ( diff --git a/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/__test__/AvailableMT5AcountsList.spec.tsx b/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/__test__/AvailableMT5AcountsList.spec.tsx index 8b6ef7708732..c9756151deeb 100644 --- a/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/__test__/AvailableMT5AcountsList.spec.tsx +++ b/packages/wallets/src/features/cfd/flows/MT5/AvailableMT5AccountsList/__test__/AvailableMT5AcountsList.spec.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { useActiveWalletAccount, useMT5AccountsList, useTradingPlatformStatus } from '@deriv/api-v2'; -import { render, screen } from '@testing-library/react'; +import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useModal } from '../../../../../../components/ModalProvider'; -import { ClientVerificationModal, MT5PasswordModal, TradingPlatformStatusModal } from '../../../../modals'; +import { JurisdictionModal, MT5PasswordModal, TradingPlatformStatusModal } from '../../../../modals'; import AvailableMT5AccountsList from '../AvailableMT5AccountsList'; jest.mock('@deriv/api-v2', () => ({ @@ -42,38 +42,16 @@ describe('AvailableMT5AccountsList', () => { }); }); - const nonRegulatedAccount = { + const defaultAccount = { market_type: 'synthetic', platform: 'mt5', product: 'swap_free', shortcode: 'svg', }; - const regulatedVerifiedAccount = { - client_kyc_status: { - poi_status: 'verified', - valid_tin: 1, - }, - market_type: 'synthetic', - platform: 'mt5', - product: 'swap_free', - shortcode: 'svg', - }; - - const regulatedUnverifiedAccount = { - client_kyc_status: { - poi_status: 'none', - valid_tin: 0, - }, - market_type: 'synthetic', - platform: 'mt5', - product: 'financial', - shortcode: 'bvi', - }; - it('renders default content for available mt5 account', () => { // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); expect(screen.getByTestId('dt_wallets_trading_account_card')).toBeInTheDocument(); expect(screen.getByText('Standard')).toBeInTheDocument(); @@ -81,13 +59,14 @@ describe('AvailableMT5AccountsList', () => { it('handles button click when platform status is active for real wallet account', () => { // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); - // @ts-expect-error - since this is a mock, we only need partial properties of the account - expect(mockShow).toHaveBeenCalledWith(); + expect(mockShow).toHaveBeenCalledWith( + + ); expect(mockSetModalState).toHaveBeenCalledWith('marketType', 'synthetic'); expect(mockSetModalState).toHaveBeenCalledWith('selectedJurisdiction', 'svg'); }); @@ -98,7 +77,7 @@ describe('AvailableMT5AccountsList', () => { }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); @@ -111,7 +90,7 @@ describe('AvailableMT5AccountsList', () => { getPlatformStatus: jest.fn(() => 'unavailable'), }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); @@ -119,12 +98,25 @@ describe('AvailableMT5AccountsList', () => { expect(mockShow).toHaveBeenCalledWith(); }); + it('shows JurisdictionModal by default when account is undefined', () => { + (useActiveWalletAccount as jest.Mock).mockReturnValue({ + data: undefined, + }); + // @ts-expect-error - since this is a mock, we only need partial properties of the account + render(); + + const button = screen.getByTestId('dt_wallets_trading_account_card'); + userEvent.click(button); + + expect(mockShow).toHaveBeenCalledWith(); + }); + it('shows TradingPlatformStatusModal with isServerMaintenance when platform status is maintenance', () => { (useTradingPlatformStatus as jest.Mock).mockReturnValue({ getPlatformStatus: jest.fn(() => 'maintenance'), }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); @@ -132,49 +124,70 @@ describe('AvailableMT5AccountsList', () => { expect(mockShow).toHaveBeenCalledWith(); }); - it('shows MT5PasswordModal for non-regulated real accounts if client is verified', () => { - (useActiveWalletAccount as jest.Mock).mockReturnValue({ - data: undefined, - }); + it('shows JurisdictionModal when product is neither swap-free nor zero-spread', () => { + const nonSwapAccount = { ...defaultAccount, product: 'ctrader' }; // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); - // @ts-expect-error - since this is a mock, we only need partial properties of the account - expect(mockShow).toHaveBeenCalledWith(); + expect(mockShow).toHaveBeenCalledWith(); }); - it('shows ClientVerificationModal for regulated real accounts if client is unverified', () => { - (useActiveWalletAccount as jest.Mock).mockReturnValue({ - data: { - is_virtual: false, - }, - }); + it('shows ClientVerification when product is zero-spread', async () => { + const zeroSpreadAccount = { ...defaultAccount, product: 'zero_spread' }; // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); + expect(screen.getByText('NEW')).toBeInTheDocument(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); - // @ts-expect-error - since this is a mock, we only need partial properties of the account - expect(mockShow).toHaveBeenCalledWith(); + await waitFor(() => { + expect(mockShow).toHaveBeenCalled(); + }); }); - it('shows MT5PasswordModal for demo accounts for verified clients', () => { + it('handles virtual wallet accounts correctly', () => { (useActiveWalletAccount as jest.Mock).mockReturnValue({ - data: { - is_virtual: true, - }, + data: { is_virtual: true }, }); // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); + render(); const button = screen.getByTestId('dt_wallets_trading_account_card'); userEvent.click(button); + expect(mockShow).toHaveBeenCalledWith( + + ); + }); + + it('shows MT5PasswordModal after ClientVerification completion', async () => { + const zeroSpreadAccount = { ...defaultAccount, product: 'zero_spread' }; // @ts-expect-error - since this is a mock, we only need partial properties of the account - expect(mockShow).toHaveBeenCalledWith(); + render(); + + const button = screen.getByTestId('dt_wallets_trading_account_card'); + userEvent.click(button); + + await waitFor(() => { + expect(mockShow).toHaveBeenCalled(); + }); + + const lastCall = mockShow.mock.calls[mockShow.mock.calls.length - 1][0]; + // eslint-disable-next-line testing-library/no-node-access + const { onCompletion } = lastCall.props.children.props; //required to access the function of lazy-loaded ClientVerification + + act(() => { + onCompletion(); + }); + + await waitFor(() => { + expect(mockShow).toHaveBeenCalledWith( + + ); + }); }); }); diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.scss b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.scss deleted file mode 100644 index 305e021bca3e..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.scss +++ /dev/null @@ -1,22 +0,0 @@ -.wallets-client-verification-modal { - width: 100%; - min-width: 44rem; - height: 100%; - padding: 2.4rem; - display: flex; - flex-direction: column; - align-items: center; - gap: 2.4rem; - - &__description { - max-width: 36rem; - - @include mobile-or-tablet-screen { - max-width: 100%; - } - } - - @include mobile-or-tablet-screen { - min-width: 100%; - } -} diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.tsx b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.tsx deleted file mode 100644 index 856da8dee040..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/ClientVerificationModal.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { DerivLightUploadPoiIcon } from '@deriv/quill-icons'; -import { Localize, useTranslations } from '@deriv-com/translations'; -import { Text, useDevice } from '@deriv-com/ui'; -import { ModalStepWrapper } from '../../../../components'; -import { getMarketTypeDetails, MARKET_TYPE } from '../../constants'; -import { TModifiedMT5Account } from '../../types'; -import { DocumentsList } from './components'; -import './ClientVerificationModal.scss'; - -type TClientVerificationModal = { - account: TModifiedMT5Account; -}; - -const ClientVerificationModal: React.FC = ({ account }) => { - const { localize } = useTranslations(); - const { isMobile } = useDevice(); - const { title } = getMarketTypeDetails(localize, account.product)[account.market_type || MARKET_TYPE.ALL]; - - return ( - -
- - - {account.is_added ? ( - - ) : ( - - )} - - -
-
- ); -}; - -export default ClientVerificationModal; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.scss b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.scss deleted file mode 100644 index 264c1f00b394..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.scss +++ /dev/null @@ -1,7 +0,0 @@ -.wallets-documents-list { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - gap: 1.6rem; -} diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.tsx b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.tsx deleted file mode 100644 index 44d2f2f4cf98..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/DocumentsList.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { TModifiedMT5Account } from 'src/features/cfd/types'; -import { useTranslations } from '@deriv-com/translations'; -import { ClientVerificationStatusBadge } from '../../../../components'; -import { getClientVerification } from '../../../../utils'; -import { DocumentTile } from './components'; -import './DocumentsList.scss'; - -type TDocumentsListProps = { - account: TModifiedMT5Account; -}; - -type TStatusBadgeProps = Record; - -const statusBadge: TStatusBadgeProps = { - expired: , - none: <>, - pending: , - rejected: , - suspected: , - verified: , -}; - -const DocumentsList: React.FC = ({ account }) => { - const history = useHistory(); - const { localize } = useTranslations(); - const { hasPoaStatus, hasPoiStatus, hasTinStatus, isPoaRequired, isPoiRequired, isTinRequired, statuses } = - getClientVerification(account); - - return ( -
- {hasPoiStatus && ( - history.push('/account/proof-of-identity')} - title={localize('Proof of identity')} - /> - )} - {hasPoaStatus && ( - history.push('/account/proof-of-address')} - title={localize('Proof of address')} - /> - )} - {hasTinStatus && isTinRequired && ( - history.push('/account/personal-details')} - title={localize('Personal details')} - /> - )} -
- ); -}; - -export default DocumentsList; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/__tests__/DocumentsList.spec.tsx b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/__tests__/DocumentsList.spec.tsx deleted file mode 100644 index cec52fd1c3f7..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/__tests__/DocumentsList.spec.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import DocumentsList from '../DocumentsList'; - -const mockHistoryPush = jest.fn(); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useHistory: jest.fn(() => ({ - push: mockHistoryPush, - })), -})); - -jest.mock('../../../../../components', () => ({ - ...jest.requireActual('../../../../../components'), - ClientVerificationStatusBadge: jest.fn(({ variant }) => variant), -})); - -jest.mock('../components', () => ({ - ...jest.requireActual('../components'), - DocumentTile: jest.fn(({ badge, isDisabled, onClick, title }) => ( - - )), -})); - -describe('', () => { - it('poi tile is not rendered', () => { - render( - - ); - - expect(screen.queryByText('Proof of identity')).not.toBeInTheDocument(); - }); - - it('poi tile is not rendered', () => { - render( - - ); - - expect(screen.queryByText('Proof of address')).not.toBeInTheDocument(); - }); - - it('personal details tile is not rendered', () => { - render( - - ); - - expect(screen.queryByText('Personal details')).not.toBeInTheDocument(); - }); - - it('on click poi tile redirects to correct page', async () => { - render( - - ); - - const poiTile = screen.getByText('Proof of identity'); - userEvent.click(poiTile); - - await waitFor(() => { - expect(mockHistoryPush).toBeCalledWith('/account/proof-of-identity'); - }); - }); - - it('on click poa tile redirects to correct page', async () => { - render( - - ); - - const poaTile = screen.getByText('Proof of address'); - userEvent.click(poaTile); - - await waitFor(() => { - expect(mockHistoryPush).toBeCalledWith('/account/proof-of-address'); - }); - }); - - it('on click personal details tile redirects to correct page', async () => { - render( - - ); - - const personalDetailsTile = screen.getByText('Personal details'); - userEvent.click(personalDetailsTile); - - await waitFor(() => { - expect(mockHistoryPush).toBeCalledWith('/account/personal-details'); - }); - }); - - it('renders poi tile with correct badge', () => { - render( - - ); - - const poiTile = screen.getByText('Proof of identity'); - - expect(within(poiTile).getByText('verified')).toBeInTheDocument; - }); - - it('renders poa tile with correct badge', () => { - render( - - ); - - const poaTile = screen.getByText('Proof of address'); - - expect(within(poaTile).getByText('verified')).toBeInTheDocument; - }); -}); diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.scss b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.scss deleted file mode 100644 index e1b8aab8fd48..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.scss +++ /dev/null @@ -1,28 +0,0 @@ -.wallets-document-tile { - width: 100%; - height: 5.6rem; - padding-inline: 1.6rem; - border: none; - border-radius: 8px; - background: #f6f7f8; - display: flex; - align-items: center; - justify-content: space-between; - cursor: pointer; - - &:disabled { - cursor: not-allowed; - } - - &__status { - display: flex; - align-items: center; - gap: 0.8rem; - } - - &__chevron { - &--disabled { - fill: #d6d6d6; - } - } -} diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.tsx b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.tsx deleted file mode 100644 index 1a3395392faf..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/DocumentTile.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { LabelPairedChevronRightMdRegularIcon } from '@deriv/quill-icons'; -import { Text } from '@deriv-com/ui'; -import './DocumentTile.scss'; - -type TDocumentTileProps = { - badge?: JSX.Element; - disabled?: boolean; - onClick: VoidFunction; - title: string; -}; - -const DocumentTile: React.FC = ({ badge, disabled, onClick, title }) => { - return ( - - ); -}; - -export default DocumentTile; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/index.ts b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/index.ts deleted file mode 100644 index e6b33e8515a5..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/DocumentTile/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DocumentTile } from './DocumentTile'; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/index.ts b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/index.ts deleted file mode 100644 index 76c67643d998..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DocumentTile'; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/index.ts b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/index.ts deleted file mode 100644 index d23ca16a13b6..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/DocumentsList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DocumentsList } from './DocumentsList'; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/index.ts b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/index.ts deleted file mode 100644 index a711e93c8860..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DocumentsList'; diff --git a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/index.ts b/packages/wallets/src/features/cfd/modals/ClientVerificationModal/index.ts deleted file mode 100644 index 7625bdeff6b4..000000000000 --- a/packages/wallets/src/features/cfd/modals/ClientVerificationModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ClientVerificationModal } from './ClientVerificationModal'; diff --git a/packages/wallets/src/features/cfd/modals/JurisdictionModal/JurisdictionModal.tsx b/packages/wallets/src/features/cfd/modals/JurisdictionModal/JurisdictionModal.tsx index 1698de810892..e32bbbd5f544 100644 --- a/packages/wallets/src/features/cfd/modals/JurisdictionModal/JurisdictionModal.tsx +++ b/packages/wallets/src/features/cfd/modals/JurisdictionModal/JurisdictionModal.tsx @@ -5,8 +5,10 @@ import { Button, Loader, useDevice } from '@deriv-com/ui'; import { ModalStepWrapper } from '../../../../components/Base'; import { useModal } from '../../../../components/ModalProvider'; import { DynamicLeverageContext } from '../../components/DynamicLeverageContext'; +import { PlatformDetails } from '../../constants'; import { DynamicLeverageScreen, DynamicLeverageTitle } from '../../screens/DynamicLeverage'; import { JurisdictionScreen } from '../../screens/Jurisdiction'; +import { MT5PasswordModal } from '..'; import './JurisdictionModal.scss'; const LazyVerification = lazy( @@ -21,19 +23,32 @@ const JurisdictionModal = () => { const [isDynamicLeverageVisible, setIsDynamicLeverageVisible] = useState(false); const [isCheckBoxChecked, setIsCheckBoxChecked] = useState(false); - const { setModalState, show } = useModal(); + const { getModalState, setModalState, show } = useModal(); const { isLoading } = useAvailableMT5Accounts(); const { isDesktop } = useDevice(); const { localize } = useTranslations(); + const marketType = getModalState('marketType') ?? 'all'; + const platform = getModalState('platform') ?? PlatformDetails.mt5.platform; + const toggleDynamicLeverage = useCallback(() => { setIsDynamicLeverageVisible(!isDynamicLeverageVisible); }, [isDynamicLeverageVisible, setIsDynamicLeverageVisible]); const JurisdictionFlow = () => { + const [showMt5PasswordModal, setShowMt5PasswordModal] = useState(false); + if (selectedJurisdiction === 'svg' || showMt5PasswordModal) { + return ; + } + return ( }> - + { + setShowMt5PasswordModal(true); + }} + selectedJurisdiction={selectedJurisdiction} + /> ); }; diff --git a/packages/wallets/src/features/cfd/modals/MT5AccountAdded/MT5AccountAdded.tsx b/packages/wallets/src/features/cfd/modals/MT5AccountAdded/MT5AccountAdded.tsx index 2552375f2083..076b42a78b02 100644 --- a/packages/wallets/src/features/cfd/modals/MT5AccountAdded/MT5AccountAdded.tsx +++ b/packages/wallets/src/features/cfd/modals/MT5AccountAdded/MT5AccountAdded.tsx @@ -31,8 +31,8 @@ const MT5AccountAdded: FC = ({ account, marketType, platform, product }) const history = useHistory(); const { isDesktop } = useDevice(); - const { localize } = useTranslations(); const { getModalState, hide } = useModal(); + const { localize } = useTranslations(); const addedAccount = mt5Accounts?.find(acc => acc.login === account?.login); @@ -97,7 +97,7 @@ const MT5AccountAdded: FC = ({ account, marketType, platform, product })
); }, - [hide, buttonSize, history, addedAccount?.loginid] + [hide, isDesktop, history, addedAccount?.loginid] ); const renderSuccessDescription = useMemo(() => { diff --git a/packages/wallets/src/features/cfd/modals/MT5PasswordModal/MT5PasswordModal.tsx b/packages/wallets/src/features/cfd/modals/MT5PasswordModal/MT5PasswordModal.tsx index 25cdb52233fd..1624b1d04d7d 100644 --- a/packages/wallets/src/features/cfd/modals/MT5PasswordModal/MT5PasswordModal.tsx +++ b/packages/wallets/src/features/cfd/modals/MT5PasswordModal/MT5PasswordModal.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { useAccountStatus, + useActiveWalletAccount, useAvailableMT5Accounts, useCreateMT5Account, useMT5AccountsList, @@ -9,23 +10,25 @@ import { useVerifyEmail, } from '@deriv/api-v2'; import { Localize, useTranslations } from '@deriv-com/translations'; -import { Button, Loader, useDevice } from '@deriv-com/ui'; +import { Button, useDevice, Loader } from '@deriv-com/ui'; import { SentEmailContent, WalletError } from '../../../../components'; import { ModalStepWrapper, ModalWrapper } from '../../../../components/Base'; import { useModal } from '../../../../components/ModalProvider'; +import { THooks, TMarketTypes, TPlatforms } from '../../../../types'; import { platformPasswordResetRedirectLink } from '../../../../utils/cfd'; import { validPassword, validPasswordMT5 } from '../../../../utils/password-validation'; -import { CFD_PLATFORMS, JURISDICTION, MARKET_TYPE, PlatformDetails } from '../../constants'; +import { CFD_PLATFORMS, JURISDICTION, MARKET_TYPE, PlatformDetails, PRODUCT } from '../../constants'; import { CreatePassword, CreatePasswordMT5, EnterPassword, MT5ResetPasswordModal } from '../../screens'; -import { TAvailableMT5Account } from '../../types'; import MT5AccountAdded from '../MT5AccountAdded/MT5AccountAdded'; import { PasswordLimitExceededModal } from '../PasswordLimitExceededModal'; import { MT5PasswordModalFooter, SuccessModalFooter } from './MT5PasswordModalFooters'; import './MT5PasswordModal.scss'; type TProps = { - account: TAvailableMT5Account; isVirtual?: boolean; + marketType: TMarketTypes.SortedMT5Accounts; + platform: TPlatforms.All; + product?: THooks.AvailableMT5Accounts['product']; }; export type TPlatformPasswordChange = { @@ -33,11 +36,8 @@ export type TPlatformPasswordChange = { newPassword: string; }; -const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { - const [isTncChecked, setIsTncChecked] = useState( - // tnc is automatically checked for real SVG accounts and all demo accounts - (account as TAvailableMT5Account).shortcode === JURISDICTION.SVG || isVirtual - ); +const MT5PasswordModal: React.FC = ({ isVirtual, marketType, platform, product }) => { + const [isTncChecked, setIsTncChecked] = useState(!(product === PRODUCT.ZEROSPREAD && !isVirtual)); const { data: createMT5AccountData, error: createMT5AccountError, @@ -52,6 +52,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { mutateAsync: tradingPasswordChangeMutateAsync, } = useTradingPlatformPasswordChange(); const { data: accountStatusData, isLoading: accountStatusLoading } = useAccountStatus(); + const { data: activeWalletData } = useActiveWalletAccount(); const { data: availableMT5AccountsData } = useAvailableMT5Accounts(); const { error: emailVerificationError, @@ -68,14 +69,11 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { const [password, setPassword] = useState(''); - const marketType = account.market_type ?? 'synthetic'; - const platform = account.platform; - const product = account.product; - const isMT5PasswordNotSet = accountStatusData?.is_mt5_password_not_set; const hasMT5Account = mt5AccountsData?.find(account => account.login); + const isDemo = activeWalletData?.is_virtual; const { platform: mt5Platform, title: mt5Title } = PlatformDetails.mt5; - const selectedJurisdiction = isVirtual ? JURISDICTION.SVG : getModalState('selectedJurisdiction'); + const selectedJurisdiction = isDemo ? JURISDICTION.SVG : getModalState('selectedJurisdiction'); const isLoading = accountStatusLoading || createMT5AccountLoading || tradingPlatformPasswordChangeLoading; @@ -91,7 +89,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { // ================================= const accountType = marketType === MARKET_TYPE.SYNTHETIC ? 'gaming' : marketType; - const categoryAccountType = isVirtual ? 'demo' : accountType; + const categoryAccountType = isDemo ? 'demo' : accountType; if (isMT5PasswordNotSet) { await tradingPasswordChangeMutateAsync({ @@ -109,7 +107,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { email: settingsData?.email ?? '', leverage: availableMT5AccountsData?.find(acc => acc.market_type === marketType)?.leverage ?? 500, mainPassword: password, - ...(selectedJurisdiction && !isVirtual ? { company: selectedJurisdiction } : {}), + ...(selectedJurisdiction && !isDemo ? { company: selectedJurisdiction } : {}), ...(marketType === MARKET_TYPE.FINANCIAL && { mt5_account_type: MARKET_TYPE.FINANCIAL }), ...(selectedJurisdiction && (selectedJurisdiction !== JURISDICTION.LABUAN @@ -133,7 +131,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { }, [ availableMT5AccountsData, createMT5AccountMutate, - isVirtual, + isDemo, isMT5PasswordNotSet, marketType, mt5Platform, @@ -156,12 +154,12 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { emailVerificationMutate({ type: 'trading_platform_mt5_password_reset', url_parameters: { - redirect_to: platformPasswordResetRedirectLink(CFD_PLATFORMS.MT5, isVirtual), + redirect_to: platformPasswordResetRedirectLink(CFD_PLATFORMS.MT5, activeWalletData?.is_virtual), }, verify_email: email, }); } - }, [email, emailVerificationMutate, isVirtual]); + }, [activeWalletData?.is_virtual, email, emailVerificationMutate]); const onSubmitPasswordChange = useCallback( ({ currentPassword, newPassword }: TPlatformPasswordChange) => { @@ -177,7 +175,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { const renderTitle = useCallback(() => { const accountAction = hasMT5Account ? localize('Add') : localize('Create'); - const accountType = isVirtual ? localize('demo') : localize('real'); + const accountType = isDemo ? localize('demo') : localize('real'); return updateMT5Password ? localize('{{mt5Title}} latest password requirements', { mt5Title }) @@ -186,13 +184,13 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { accountType, mt5Title, }); - }, [hasMT5Account, isVirtual, localize, mt5Title, updateMT5Password]); + }, [hasMT5Account, isDemo, localize, mt5Title, updateMT5Password]); const renderFooter = useCallback(() => { if (createMT5AccountSuccess) return (
- +
); @@ -237,7 +235,7 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { }, [ createMT5AccountLoading, createMT5AccountSuccess, - isVirtual, + isDemo, isDesktop, isMT5PasswordNotSet, mt5Title, @@ -265,7 +263,6 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { if (isMT5PasswordNotSet && platform === CFD_PLATFORMS.MT5) return ( = ({ account, isVirtual = false }) => { }} password={password} platform={mt5Platform} + product={product} /> ); @@ -291,10 +289,9 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { return ( setPassword(e.target.value)} @@ -310,18 +307,18 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { ); }, [ isMT5PasswordNotSet, - platform, tradingPlatformPasswordChangeLoading, createMT5AccountLoading, + isTncChecked, onSubmit, password, mt5Platform, - account, - isTncChecked, - isVirtual, - product, updateMT5Password, tradingPasswordChangeError, + platform, + isVirtual, + product, + activeWalletData?.is_virtual, onSubmitPasswordChange, marketType, localize, @@ -331,10 +328,6 @@ const MT5PasswordModal: React.FC = ({ account, isVirtual = false }) => { isLoading, ]); - if (accountStatusLoading) { - return ; - } - if (emailVerificationStatus === 'error') { return ( = ({ account, isVirtual = false }) => { ); } - if (createMT5AccountSuccess) { + if (createMT5AccountSuccess && !isMT5PasswordNotSet) { return ( void; password: string; platform: TPlatforms.All; + product?: THooks.AvailableMT5Accounts['product']; }; const CreatePasswordMT5: React.FC = ({ - account, isLoading, isTncChecked, isVirtual, @@ -32,6 +30,7 @@ const CreatePasswordMT5: React.FC = ({ onTncChange, password, platform, + product, }) => { const { isDesktop } = useDevice(); const { localize } = useTranslations(); @@ -62,9 +61,13 @@ const CreatePasswordMT5: React.FC = ({ onChange={onPasswordChange} password={password} /> - {!isVirtual && } - {!isVirtual && account.shortcode !== 'svg' && ( - + {product === PRODUCT.ZEROSPREAD && !isVirtual && ( + )}
diff --git a/packages/wallets/src/features/cfd/screens/EnterPassword/EnterPassword.tsx b/packages/wallets/src/features/cfd/screens/EnterPassword/EnterPassword.tsx index 2c270fbd048d..d48df9ae70e7 100644 --- a/packages/wallets/src/features/cfd/screens/EnterPassword/EnterPassword.tsx +++ b/packages/wallets/src/features/cfd/screens/EnterPassword/EnterPassword.tsx @@ -5,14 +5,11 @@ import { Button, Text, useDevice } from '@deriv-com/ui'; import { WalletPasswordFieldLazy } from '../../../../components/Base'; import { THooks, TMarketTypes, TPlatforms } from '../../../../types'; import { validPassword, validPasswordMT5 } from '../../../../utils/password-validation'; -import { CFD_PLATFORMS, getMarketTypeDetails, JURISDICTION, PlatformDetails } from '../../constants'; -import { TAvailableMT5Account } from '../../types'; -import { MT5LicenceMessage, MT5PasswordModalTnc } from '../components'; +import { CFDPasswordModalTnc } from '../../components/CFDPasswordModalTnc'; +import { CFD_PLATFORMS, getMarketTypeDetails, PlatformDetails, PRODUCT } from '../../constants'; import './EnterPassword.scss'; -// Note: this component requires a proper refactor to remove props for keys available under the `account` prop type TProps = { - account?: TAvailableMT5Account; isForgotPasswordLoading?: boolean; isLoading?: boolean; isTncChecked?: boolean; @@ -31,7 +28,6 @@ type TProps = { }; const EnterPassword: React.FC = ({ - account, isForgotPasswordLoading, isLoading, isTncChecked = true, @@ -104,9 +100,13 @@ const EnterPassword: React.FC = ({ {passwordErrorHints} )} - {account && !isVirtual && } - {account && account.shortcode !== JURISDICTION.SVG && platform === CFD_PLATFORMS.MT5 && !isVirtual && ( - onTncChange?.()} /> + {product === PRODUCT.ZEROSPREAD && !isVirtual && ( + onTncChange?.()} + platform={platform} + product={product} + /> )}
{isDesktop && ( diff --git a/packages/wallets/src/features/cfd/screens/EnterPassword/__test__/EnterPassword.spec.tsx b/packages/wallets/src/features/cfd/screens/EnterPassword/__test__/EnterPassword.spec.tsx index c5fe094d0dda..fbdb92674752 100644 --- a/packages/wallets/src/features/cfd/screens/EnterPassword/__test__/EnterPassword.spec.tsx +++ b/packages/wallets/src/features/cfd/screens/EnterPassword/__test__/EnterPassword.spec.tsx @@ -1,18 +1,12 @@ import React from 'react'; import { useActiveWalletAccount } from '@deriv/api-v2'; -import { cleanup, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MARKET_TYPE, PlatformDetails } from '../../../constants'; import EnterPassword from '../EnterPassword'; jest.mock('@deriv/api-v2'); -jest.mock('../../components', () => ({ - ...jest.requireActual('../../components'), - MT5LicenceMessage: jest.fn(() =>
MT5LicenceMessage
), - MT5PasswordModalTnc: jest.fn(() =>
MT5PasswordModalTnc
), -})); - describe('EnterPassword', () => { const mockUseActiveWalletAccount = useActiveWalletAccount as jest.Mock; @@ -20,8 +14,6 @@ describe('EnterPassword', () => { mockUseActiveWalletAccount.mockReturnValue({ data: { is_virtual: false } }); }); - afterEach(cleanup); - const title = `Enter your ${PlatformDetails.mt5.title} password`; const shortPassword = 'abcd'; const validPassword = 'Abcd1234!'; @@ -90,12 +82,6 @@ describe('EnterPassword', () => { expect(addAccountButton).toBeDisabled(); }); - it('disables the "Add account" button when tnc is not checked', () => { - renderComponent({ isTncChecked: false }); - const addAccountButton = screen.getByRole('button', { name: 'Add account' }); - expect(addAccountButton).toBeDisabled(); - }); - it('shows password error hints when passwordError is true', () => { renderComponent({ passwordError: true }); expect( @@ -104,29 +90,4 @@ describe('EnterPassword', () => { ) ).toBeInTheDocument(); }); - - it('shows the mt5 licence message component for real MT5 accounts', () => { - renderComponent({ account: { shortcode: 'svg' } }); - - expect(screen.getByText('MT5LicenceMessage')).toBeInTheDocument(); - }); - - it('hides the mt5 licence message for virtual accounts', () => { - mockUseActiveWalletAccount.mockReturnValue({ data: { is_virtual: true } }); - renderComponent(); - - expect(screen.queryByText('MT5LicenceMessage')).not.toBeInTheDocument(); - }); - - it('shows the mt5 tnc checkbox for regulated real accounts', () => { - renderComponent({ account: { shortcode: 'bvi' } }); - - expect(screen.getByText('MT5PasswordModalTnc')).toBeInTheDocument(); - }); - - it('hides the mt5 tnc checkbox for non-regulated real accounts', () => { - renderComponent({ account: { shortcode: 'svg' } }); - - expect(screen.queryByText('MT5PasswordModalTnc')).not.toBeInTheDocument(); - }); }); diff --git a/packages/wallets/src/features/cfd/screens/MT5TradeScreen/MT5TradeScreen.tsx b/packages/wallets/src/features/cfd/screens/MT5TradeScreen/MT5TradeScreen.tsx index f5efb6cc09e9..9c03bccd69c2 100644 --- a/packages/wallets/src/features/cfd/screens/MT5TradeScreen/MT5TradeScreen.tsx +++ b/packages/wallets/src/features/cfd/screens/MT5TradeScreen/MT5TradeScreen.tsx @@ -8,7 +8,6 @@ import { WalletBadge, WalletListCardBadge } from '../../../../components'; import { useModal } from '../../../../components/ModalProvider'; import { THooks } from '../../../../types'; import { CFD_PLATFORMS, getMarketTypeDetails, getServiceMaintenanceMessages, PlatformDetails } from '../../constants'; -import { TAddedMT5Account } from '../../types'; import MT5DesktopRedirectOption from './MT5TradeLink/MT5DesktopRedirectOption'; import MT5MobileRedirectOption from './MT5TradeLink/MT5MobileRedirectOption'; import { MT5TradeDetailsItem } from './MT5TradeDetailsItem'; @@ -16,7 +15,7 @@ import { MT5TradeLink } from './MT5TradeLink'; import './MT5TradeScreen.scss'; type MT5TradeScreenProps = { - mt5Account?: TAddedMT5Account; + mt5Account?: THooks.MT5AccountsList; }; const MT5TradeScreen: FC = ({ mt5Account }) => { diff --git a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.scss b/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.scss deleted file mode 100644 index 10eefbe6dcd5..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.scss +++ /dev/null @@ -1,5 +0,0 @@ -.wallets-mt5-licence-message { - @include mobile-or-tablet-screen { - margin-top: auto; - } -} diff --git a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.tsx b/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.tsx deleted file mode 100644 index 8b68d3233535..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/MT5LicenceMessage.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { Localize, useTranslations } from '@deriv-com/translations'; -import { InlineMessage, Text, useDevice } from '@deriv-com/ui'; -import { getMarketTypeDetails, JURISDICTION, MARKET_TYPE, PlatformDetails } from '../../../constants'; -import { TAvailableMT5Account } from '../../../types'; -import './MT5LicenceMessage.scss'; - -type TMT5LicenseMessageProps = { - account: TAvailableMT5Account; -}; - -const MT5LicenseMessage: React.FC = ({ account }) => { - const { isDesktop } = useDevice(); - const { localize } = useTranslations(); - const isSvg = account.shortcode === JURISDICTION.SVG; - - return ( - - - {isSvg ? ( - // TODO: remove this hardcoded logic for the company number for SVG once BE provides company_number key for non-regulated accounts - - ) : ( - - )} - - - ); -}; - -export default MT5LicenseMessage; diff --git a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/__tests__/MT5LicenceMessage.spec.tsx b/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/__tests__/MT5LicenceMessage.spec.tsx deleted file mode 100644 index d0d40201f9ca..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/__tests__/MT5LicenceMessage.spec.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import MT5LicenseMessage from '../MT5LicenceMessage'; - -jest.mock('@deriv-com/ui', () => ({ - ...jest.requireActual('@deriv-com/ui'), - useDevice: jest.fn(() => ({ isDesktop: false })), -})); - -const mockRegulatedAccount = { - licence_number: 'mock_licence_number', - market_type: 'financial', - name: 'mock_company_name', - product: 'financial', - regulatory_authority: 'mock_regulatory_authority', - shortcode: 'bvi', -}; - -const mockNonRegulatedAccount = { - market_type: 'all', - name: 'mock_company_name', - product: 'swap_free', - shortcode: 'svg', -}; - -describe('', () => { - it('displays correct message for regulated account', () => { - // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); - - expect( - screen.getByText( - 'You are adding your Deriv MT5 Financial account under mock_company_name, regulated by the mock_regulatory_authority (licence no. mock_licence_number).' - ) - ); - }); - - it('displays correct message for non-regulated account', () => { - // @ts-expect-error - since this is a mock, we only need partial properties of the account - render(); - - expect( - screen.getByText( - 'You are adding your Deriv MT5 Swap-Free account under mock_company_name (company no. 273 LLC 2020).' - ) - ); - }); -}); diff --git a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/index.ts b/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/index.ts deleted file mode 100644 index c939c664e13b..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5LicenceMessage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as MT5LicenceMessage } from './MT5LicenceMessage'; diff --git a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.scss b/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.scss deleted file mode 100644 index b6fb09376e7e..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.scss +++ /dev/null @@ -1,5 +0,0 @@ -.wallets-mt5-modal-tnc { - display: flex; - flex-direction: column; - gap: 1.6rem; -} diff --git a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.tsx b/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.tsx deleted file mode 100644 index 507b9bb26e54..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/MT5PasswordModalTnc.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { Localize } from '@deriv-com/translations'; -import { Checkbox, Text, useDevice } from '@deriv-com/ui'; -import { WalletLink } from '../../../../../components/Base'; -import { useModal } from '../../../../../components/ModalProvider'; -import { companyNamesAndUrls } from '../../../constants'; -import './MT5PasswordModalTnc.scss'; - -export type TMT5PasswordModalTncProps = { - checked: boolean; - onChange: () => void; -}; - -const MT5PasswordModalTnc = ({ checked, onChange }: TMT5PasswordModalTncProps) => { - const { isDesktop } = useDevice(); - const { getModalState } = useModal(); - const selectedJurisdiction = getModalState('selectedJurisdiction'); - // TODO: replace the company name with the information provided by the trading_platform_account_available API's BE response - const selectedCompany = companyNamesAndUrls[selectedJurisdiction as keyof typeof companyNamesAndUrls]; - - return ( -
- - ]} - i18n_default_text="I confirm and accept {{company}}'s <0>terms and conditions" - values={{ - company: selectedCompany.name, - }} - /> - - } - name='mt5-tnc-checkbox' - onChange={onChange} - /> -
- ); -}; - -export default MT5PasswordModalTnc; diff --git a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/index.ts b/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/index.ts deleted file mode 100644 index fe47194df6f4..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/MT5PasswordModalTnc/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as MT5PasswordModalTnc } from './MT5PasswordModalTnc'; diff --git a/packages/wallets/src/features/cfd/screens/components/index.ts b/packages/wallets/src/features/cfd/screens/components/index.ts deleted file mode 100644 index 14f20a9a7f16..000000000000 --- a/packages/wallets/src/features/cfd/screens/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './MT5LicenceMessage'; -export * from './MT5PasswordModalTnc'; diff --git a/packages/wallets/src/features/cfd/types.ts b/packages/wallets/src/features/cfd/types.ts deleted file mode 100644 index 32aaf0d18d0d..000000000000 --- a/packages/wallets/src/features/cfd/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable camelcase */ -/* - TODO: Remove these types once API types for client_kyc_status is available for mt5_login_list and trading_platform_available_accounts from BE -*/ -import { THooks } from '../../types'; - -type TStatuses = 'expired' | 'none' | 'pending' | 'rejected' | 'suspected' | 'verified'; - -export type TModifiedMT5Account = THooks.SortedMT5Accounts & { - client_kyc_status: { - poa_status: TStatuses; - poi_status: TStatuses; - valid_tin: 0 | 1; - }; - licence_number: string; - regulatory_authority: string; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ObjectWithKeyInUnion = T extends any ? (K extends keyof T ? T : never) : never; - -export type TAvailableMT5Account = ObjectWithKeyInUnion; -export type TAddedMT5Account = ObjectWithKeyInUnion; diff --git a/packages/wallets/src/features/cfd/utils/index.ts b/packages/wallets/src/features/cfd/utils/index.ts deleted file mode 100644 index 04bca77e0dec..000000000000 --- a/packages/wallets/src/features/cfd/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './utils'; diff --git a/packages/wallets/src/features/cfd/utils/utils.ts b/packages/wallets/src/features/cfd/utils/utils.ts deleted file mode 100644 index e650e20011e0..000000000000 --- a/packages/wallets/src/features/cfd/utils/utils.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TAddedMT5Account, TAvailableMT5Account } from '../types'; - -const requiredDocumentStatuses = ['expired', 'none', 'rejected', 'suspected']; - -export const getClientVerification = (account: TAddedMT5Account | TAvailableMT5Account) => { - const hasClientKycStatus = 'client_kyc_status' in account; - const documentStatuses = account.client_kyc_status; - - const hasPoiStatus = hasClientKycStatus && 'poi_status' in documentStatuses; - const hasPoaStatus = hasClientKycStatus && 'poa_status' in documentStatuses; - const hasTinStatus = hasClientKycStatus && 'valid_tin' in documentStatuses; - - const isPoiRequired = hasPoiStatus && requiredDocumentStatuses.includes(documentStatuses.poi_status); - const isPoaRequired = hasPoaStatus && requiredDocumentStatuses.includes(documentStatuses.poa_status); - const isTinRequired = hasTinStatus && !documentStatuses.valid_tin; - - return { - hasClientKycStatus, - hasPoaStatus, - hasPoiStatus, - hasTinStatus, - isPoaRequired, - isPoiRequired, - isTinRequired, - isVerificationRequired: isPoiRequired || isPoaRequired || isTinRequired, - statuses: documentStatuses, - }; -};