diff --git a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/input-group.spec.tsx b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/input-group.spec.tsx deleted file mode 100644 index 9ec13bf665f8..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/input-group.spec.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import InputGroup from '../input-group'; - -describe('InputGroup', () => { - const children = 'This is children'; - it('should render InputGroup with children passed inside', () => { - render({children}); - expect(screen.getByText(/This is children/)).toBeInTheDocument(); - }); - - it('should have children_class passed into the component', () => { - render({children}); - const children_text = screen.getByText(/This is children/); - expect(children_text).toHaveClass('children_class'); - }); -}); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details-form.spec.tsx b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details-form.spec.tsx deleted file mode 100644 index c17720713bf3..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/personal-details-form.spec.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import React from 'react'; -import { cleanup, render, waitFor, screen } from '@testing-library/react'; -import { createBrowserHistory } from 'history'; -import { Router } from 'react-router'; -import { APIProvider } from '@deriv/api'; -import userEvent from '@testing-library/user-event'; -import { StoreProvider, mockStore } from '@deriv/stores'; -import PersonalDetailsForm from '../personal-details-form'; -import { useGrowthbookGetFeatureValue, useResidenceList } from '@deriv/hooks'; - -afterAll(cleanup); -jest.mock('@deriv/components', () => ({ - ...jest.requireActual('@deriv/components'), - Loading: () =>
Loading
, -})); - -jest.mock('@deriv/shared/src/services/ws-methods', () => ({ - WS: { - send: jest.fn().mockResolvedValue({ time: 1620000000 }), - wait: (...payload: []) => Promise.resolve([...payload]), - }, - useWS: () => undefined, -})); - -const residence_list = [ - { - text: 'Text', - value: 'value', - }, -]; - -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - useStatesList: jest.fn(() => ({ data: residence_list, isLoading: false })), - useResidenceList: jest.fn(() => ({ data: residence_list, isLoading: false })), - useGrowthbookGetFeatureValue: jest.fn(), -})); - -describe('', () => { - const history = createBrowserHistory(); - - const mock_store = mockStore({ - client: { - account_settings: { - first_name: 'John', - place_of_birth: 'Thailand', - citizen: 'Thailand', - email_consent: 1, - phone_number_verification: { - verified: 0, - }, - }, - }, - }); - - const renderComponent = (modified_store = mock_store) => { - return render( - - - - - - - - ); - }; - - beforeEach(() => { - (useGrowthbookGetFeatureValue as jest.Mock).mockReturnValue([true, true]); - }); - - it('should render successfully', async () => { - renderComponent(); - expect(screen.queryByText(/Loading/)).not.toBeInTheDocument(); - await waitFor(() => { - expect(screen.getByText(/Ensure your information is correct./i)).toBeInTheDocument(); - }); - }); - - it('should render all the personal details fields', () => { - renderComponent(); - const fields = [ - 'First name*', - 'Last name*', - 'Date of birth*', - 'Country of residence*', - 'Phone number*', - 'First line of address*', - 'Second line of address (optional)', - 'Town/City*', - 'State/Province (optional)', - 'Postal/ZIP code', - ]; - fields.forEach(value => { - expect(screen.getByText(value)).toBeInTheDocument(); - }); - }); - - it('should have "required" validation errors on required form fields', async () => { - renderComponent(); - - await waitFor(async () => { - const first_name = screen.getByTestId('dt_first_name'); - await userEvent.clear(first_name); - expect(screen.getByText(/First name is required./)).toBeInTheDocument(); - }); - }); - - it('should display error for the regex validation, for First name when unacceptable characters are entered', async () => { - renderComponent(); - - await waitFor(async () => { - const first_name = screen.getByTestId('dt_first_name'); - await userEvent.type(first_name, 'test 3'); - expect(screen.getByText('Letters, spaces, periods, hyphens, apostrophes only.')).toBeInTheDocument(); - }); - }); - - it('should not display error for the regex validation, for First name when acceptable characters are entered', async () => { - renderComponent(); - await waitFor(async () => { - const first_name = screen.getByTestId('dt_first_name'); - await userEvent.type(first_name, "test-with' chars."); - expect(screen.queryByText('Letters, spaces, periods, hyphens, apostrophes only.')).not.toBeInTheDocument(); - }); - }); - - it('should render professional client if support_professional_client is true with not verified account', () => { - mock_store.client.current_landing_company.support_professional_client = 'true'; - renderComponent(); - const professional_client_text = [ - /Professional Client/, - /By default, all Deriv.com clients are retail clients but anyone can request to be treated as a professional client./, - /A professional client receives a lower degree of client protection due to the following./, - /We presume that you possess the experience, knowledge, and expertise to make your own investment decisions and properly assess the risk involved./, - /We’re not obliged to conduct an appropriateness test, nor provide you with any risk warnings./, - /You’ll need to authenticate your account before requesting to become a professional client./, - /Authenticate my account/, - ]; - professional_client_text.forEach(value => { - expect(screen.getByText(value)).toBeInTheDocument(); - }); - const auth_text = screen.getByText(/Authenticate my account/); - const auth_link = auth_text.getAttribute('href'); - expect(auth_link).toBe('/account/proof-of-identity'); - }); - - it('should render POI auth link', () => { - mock_store.client.current_landing_company.support_professional_client = 'true'; - renderComponent(); - const auth_text = screen.getByText(/Authenticate my account/); - const auth_link = auth_text.getAttribute('href'); - expect(auth_link).toBe('/account/proof-of-identity'); - }); - - it('should render POA auth link', () => { - mock_store.client.current_landing_company.support_professional_client = 'true'; - mock_store.client.authentication_status.identity_status = 'verified'; - renderComponent(); - const auth_text = screen.getByText(/Authenticate my account/); - const auth_link = auth_text.getAttribute('href'); - expect(auth_link).toBe('/account/proof-of-address'); - }); - - it('should render professional client if support_professional_client is true with verified account', () => { - mock_store.client.current_landing_company.support_professional_client = 'true'; - mock_store.client.authentication_status.document_status = 'verified'; - mock_store.client.authentication_status.identity_status = 'verified'; - renderComponent(); - expect( - screen.getByRole('checkbox', { name: /I would like to be treated as a professional client./ }) - ).toBeInTheDocument(); - }); - - it('should update user profile after clicking on Save changes', async () => { - renderComponent(); - const first_name = screen.getByTestId('dt_first_name') as HTMLInputElement; - expect(first_name.value).toBe('John'); - await userEvent.clear(first_name); - await userEvent.type(first_name, 'James'); - const save_changes_button = screen.getByRole('button', { name: /Save changes/ }); - await userEvent.click(save_changes_button); - expect(first_name.value).toBe('James'); - }); - - it('should only display country of residence if isVirtual is true', () => { - mock_store.client.is_virtual = true; - renderComponent(); - const exceptional_fields = [ - 'First name*', - 'Last name*', - 'Place of birth', - 'Date of birth*', - 'Citizenship', - 'Phone number*', - 'First line of address*', - 'Second line of address (optional)', - 'Town/City*', - 'State/Province (optional)', - 'Postal/ZIP code', - ]; - exceptional_fields.forEach(value => { - expect(screen.queryByText(value)).not.toBeInTheDocument(); - }); - expect(screen.getByText('Country of residence*')).toBeInTheDocument(); - }); - - it('should display loader while fetching data', () => { - (useResidenceList as jest.Mock).mockImplementation(() => ({ data: [], isLoading: true })); - renderComponent(); - expect(screen.getByText(/Loading/)).toBeInTheDocument(); - }); -}); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/validation.spec.tsx b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/validation.spec.tsx deleted file mode 100644 index 8059cd55d641..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/validation.spec.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { - getPersonalDetailsInitialValues, - getPersonalDetailsValidationSchema, - makeSettingsRequest, -} from '../validation'; - -describe('getPersonalDetailsValidationSchema', () => { - const valid_data = { - first_name: 'John', - last_name: 'Doe', - phone: '+123456789', - address_line_1: 'Kuala Lumpur', - address_city: 'Kuala Lumpur', - citizen: 'Malaysian', - tax_identification_number: '123123123', - tax_residence: 'Germany', - employment_status: 'Employed', - date_of_birth: '1990-01-01', - tax_identification_confirm: true, - }; - - const invalid_data = { - first_name: 'John', - last_name: 'Doe123', - phone: 'wrong', - address_line_1: '', - address_city: '', - citizen: '', - }; - - it('should validate a valid input for non-eu users', async () => { - const validationSchema = getPersonalDetailsValidationSchema(); - const isValid = await validationSchema.isValid(valid_data); - expect(isValid).toBe(true); - }); - - it('should not validate an invalid input for non-eu users', async () => { - const validationSchema = getPersonalDetailsValidationSchema(); - try { - await validationSchema.isValid(invalid_data); - } catch (error) { - // @ts-expect-error [TODO]: Fix type for error - expect(error.errors.length).toBeGreaterThan(0); - } - }); - - it('should validate a valid input for eu users', async () => { - const validationSchema = getPersonalDetailsValidationSchema(); - const isValid = await validationSchema.isValid(valid_data); - expect(isValid).toBe(true); - }); - - it('should return empty object for virtual account', () => { - const validationSchema = getPersonalDetailsValidationSchema(true); - expect(validationSchema.fields).toEqual({}); - }); -}); - -const account_settings = { - first_name: 'John', - last_name: 'Doe', - place_of_birth: 'co', - address_state: 'state_test', -}; -const mock_residence_list = [ - { value: 'id', text: 'Indonesia' }, - { value: 'co', text: 'Colombia' }, -]; -const mock_state_list = [ - { - text: 'State Test', - value: 'state_test', - }, -]; - -describe('getPersonalDetailsInitialValues', () => { - it('should return correct initial values', () => { - const initial_values = getPersonalDetailsInitialValues(account_settings, mock_residence_list, mock_state_list); - - expect(initial_values.first_name).toBe('John'); - expect(initial_values.last_name).toBe('Doe'); - expect(initial_values.place_of_birth).toBe('Colombia'); - expect(initial_values.address_state).toBe('State Test'); - }); - - it('should return default values for virtual account', () => { - const mock_settings = { - ...account_settings, - residence: 'id', - }; - - const initial_values = getPersonalDetailsInitialValues( - mock_settings, - mock_residence_list, - mock_state_list, - true - ); - - expect(initial_values).toEqual({ - email_consent: 0, - residence: 'id', - }); - }); - - it('should return correct initial values when isCountryCodeDropdownEnabled is true', () => { - const isCountryCodeDropdownEnabled = true; - const initial_values = getPersonalDetailsInitialValues( - account_settings, - mock_residence_list, - mock_state_list, - false, - '60', - true, - isCountryCodeDropdownEnabled - ); - expect(initial_values.first_name).toBe('John'); - expect(initial_values.last_name).toBe('Doe'); - expect(initial_values.place_of_birth).toBe('Colombia'); - expect(initial_values.address_state).toBe('State Test'); - expect(initial_values.calling_country_code).toBe('60'); - }); -}); - -describe('makeSettingsRequest', () => { - it('should return correct request object for virtual user', () => { - const mock_settings = { - ...account_settings, - email_consent: '1', - }; - - const result = makeSettingsRequest(mock_settings as any, mock_residence_list, mock_state_list, true); - expect(result).toEqual({ email_consent: '1' }); - }); - - it('should return correct request object for non-virtual user', () => { - const mock_settings = { - ...account_settings, - tax_residence: 'Indonesia', - tax_identification_number: '123', - residence: 'Indonesia', - }; - const result = makeSettingsRequest(mock_settings, mock_residence_list, mock_state_list, false); - expect(result).toEqual({ - first_name: 'John', - last_name: 'Doe', - place_of_birth: '', - address_state: '', - tax_identification_number: '123', - tax_residence: 'id', - }); - }); -}); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/verify-button.spec.tsx b/packages/account/src/Sections/Profile/PersonalDetails/__tests__/verify-button.spec.tsx deleted file mode 100644 index 6e3f5ff09539..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/__tests__/verify-button.spec.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React from 'react'; -import { Router } from 'react-router'; -import { createBrowserHistory } from 'history'; -import { screen, render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { usePhoneNumberVerificationSetTimer, useVerifyEmail } from '@deriv/hooks'; -import { routes } from '@deriv/shared'; -import { StoreProvider, mockStore } from '@deriv/stores'; -import { VerifyButton } from '../verify-button'; -import { GetSettings, ResidenceList, StatesList } from '@deriv/api-types'; - -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - usePhoneNumberVerificationSetTimer: jest.fn(), - useVerifyEmail: jest.fn(() => ({ - sendPhoneNumberVerifyEmail: jest.fn(), - WS: {}, - error: null, - })), - useSettings: jest.fn(() => ({ - refetch: jest.fn(), - mutation: { - mutateAsync: jest.fn(() => Promise.resolve()), - isLoading: false, - }, - })), -})); - -const mockAccountSettings: GetSettings = { - immutable_fields: ['place_of_birth'], - place_of_birth: 'UK', - tax_residence: 'UK', - tax_identification_number: '12345', - account_opening_reason: 'Hedging', -}; - -const mockResidenceList: ResidenceList = [ - { value: 'UK', text: 'United Kingdom' }, - { value: 'US', text: 'United States' }, -]; - -const mockStatesList: StatesList = []; - -describe('VerifyButton', () => { - beforeEach(() => { - (usePhoneNumberVerificationSetTimer as jest.Mock).mockReturnValue({ next_otp_request: '' }); - }); - const history = createBrowserHistory(); - const mock_store = mockStore({ - client: { - account_settings: { - phone_number_verification: { - verified: 0, - }, - }, - }, - }); - let mock_next_email_otp_request_timer = 0; - const mock_set_status = jest.fn(); - - const renderWithRouter = () => { - return render( - - - - - - ); - }; - - beforeEach(() => { - mock_next_email_otp_request_timer = 0; - }); - - it('should render Verify Button', () => { - renderWithRouter(); - expect(screen.getByText('Verify')).toBeInTheDocument(); - }); - - it('should redirect user to phone-verification page when clicked on Verify Button', () => { - (useVerifyEmail as jest.Mock).mockReturnValue({ - sendPhoneNumberVerifyEmail: jest.fn(), - WS: { - isSuccess: true, - }, - }); - renderWithRouter(); - const verifyButton = screen.getByText('Verify'); - userEvent.click(verifyButton); - expect(history.location.pathname).toBe(routes.phone_verification); - }); - - it('should setStatus with error returned by WS', () => { - (useVerifyEmail as jest.Mock).mockReturnValue({ - sendPhoneNumberVerifyEmail: jest.fn(), - WS: { - isSuccess: false, - }, - error: { - message: 'Phone Taken', - code: 'PhoneNumberTaken', - }, - }); - renderWithRouter(); - const verifyButton = screen.getByText('Verify'); - userEvent.click(verifyButton); - expect(mock_set_status).toBeCalledWith({ msg: 'Phone Taken', code: 'PhoneNumberTaken' }); - }); - - it('should render Verify Button with timer if next_otp_request has value', () => { - mock_next_email_otp_request_timer = 2; - renderWithRouter(); - expect(screen.getByText('Verify in 2s')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Verify in 2s' })).toBeDisabled(); - }); - - it('should render Verified text', () => { - if (mock_store.client.account_settings.phone_number_verification) - mock_store.client.account_settings.phone_number_verification.verified = 1; - renderWithRouter(); - expect(screen.getByText('Verified')).toBeInTheDocument(); - }); -}); diff --git a/packages/account/src/Sections/Profile/PersonalDetails/constants.ts b/packages/account/src/Sections/Profile/PersonalDetails/constants.ts deleted file mode 100644 index 66c820634ae2..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { localize } from '@deriv/translations'; - -export const account_opening_reason_list = [ - { text: localize('Speculative'), value: 'Speculative' }, - { text: localize('Income Earning'), value: 'Income Earning' }, - { text: localize('Hedging'), value: 'Hedging' }, -]; diff --git a/packages/account/src/Sections/Profile/PersonalDetails/index.ts b/packages/account/src/Sections/Profile/PersonalDetails/index.ts deleted file mode 100644 index d1629a6633b7..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import PersonalDetails from './personal-details-form'; - -export default PersonalDetails; diff --git a/packages/account/src/Sections/Profile/PersonalDetails/input-group.tsx b/packages/account/src/Sections/Profile/PersonalDetails/input-group.tsx deleted file mode 100644 index 71b65bdad568..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/input-group.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { PropsWithChildren } from 'react'; - -type TInputGroup = { - className?: string; -}; - -const InputGroup = ({ children, className }: PropsWithChildren) => ( -
-
{children}
-
-); - -export default InputGroup; diff --git a/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.scss b/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.scss deleted file mode 100644 index 0086f5618c83..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.scss +++ /dev/null @@ -1,5 +0,0 @@ -.employment-tin-section { - @include desktop-screen { - margin: 2.4rem 0; - } -} diff --git a/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.tsx b/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.tsx deleted file mode 100644 index c281249449c9..000000000000 --- a/packages/account/src/Sections/Profile/PersonalDetails/personal-details-form.tsx +++ /dev/null @@ -1,901 +0,0 @@ -import { ChangeEvent, Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { useHistory } from 'react-router'; -import clsx from 'clsx'; -import { Form, Formik, FormikHelpers } from 'formik'; - -import { useInvalidateQuery } from '@deriv/api'; -import { - Button, - Checkbox, - FormSubmitErrorMessage, - HintBox, - Input, - Loading, - OpenLiveChatLink, - Text, -} from '@deriv/components'; -import { - useGetPhoneNumberList, - useGrowthbookGetFeatureValue, - useIsPhoneNumberVerified, - usePhoneNumberVerificationSetTimer, - useResidenceList, - useStatesList, - useTinValidations, -} from '@deriv/hooks'; -import { AUTH_STATUS_CODES, getBrandWebsiteName, routes, WS } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; -import { Localize, localize } from '@deriv/translations'; -import { useDevice } from '@deriv-com/ui'; - -import FormBody from '../../../Components/form-body'; -import FormFooter from '../../../Components/form-footer'; -import FormSubHeader from '../../../Components/form-sub-header'; -import { DateOfBirthField } from '../../../Components/forms/form-fields'; -import AccountOpeningReasonField from '../../../Components/forms/form-fields/account-opening-reason'; -import FormSelectField from '../../../Components/forms/form-select-field'; -import LeaveConfirm from '../../../Components/leave-confirm'; -import LoadErrorMessage from '../../../Components/load-error-message'; -import POAAddressMismatchHintBox from '../../../Components/poa-address-mismatch-hint-box'; -import EmploymentTaxDetailsContainer from '../../../Containers/employment-tax-details-container'; -import { isFieldImmutable } from '../../../Helpers/utils'; -import { useScrollElementToTop } from '../../../hooks'; -import { PersonalDetailsValueTypes } from '../../../Types'; - -import { account_opening_reason_list } from './constants'; -import InputGroup from './input-group'; -import { getPersonalDetailsInitialValues, getPersonalDetailsValidationSchema, makeSettingsRequest } from './validation'; -import { VerifyButton } from './verify-button'; - -import './personal-details-form.scss'; - -type TRestState = { - show_form: boolean; - api_error?: string; -}; - -const PersonalDetailsForm = observer(() => { - const { isDesktop } = useDevice(); - const [is_loading, setIsLoading] = useState(false); - const [is_btn_loading, setIsBtnLoading] = useState(false); - const [is_submit_success, setIsSubmitSuccess] = useState(false); - const invalidate = useInvalidateQuery(); - const history = useHistory(); - const [isPhoneNumberVerificationEnabled, isPhoneNumberVerificationLoaded] = useGrowthbookGetFeatureValue({ - featureFlag: 'phone_number_verification', - }); - const [isCountryCodeDropdownEnabled, isCountryCodeLoaded] = useGrowthbookGetFeatureValue({ - featureFlag: 'enable_country_code_dropdown', - }); - - const { next_email_otp_request_timer, is_email_otp_timer_loading } = usePhoneNumberVerificationSetTimer(); - - const { tin_validation_config, mutate } = useTinValidations(); - - const scrollToTop = useScrollElementToTop(); - - const { - client, - ui, - notifications, - common: { is_language_changing }, - } = useStore(); - const { is_phone_number_verified } = useIsPhoneNumberVerified(); - - const { - account_settings, - account_status, - authentication_status, - is_virtual, - current_landing_company, - updateAccountStatus, - fetchAccountSettings, - residence, - is_svg, - is_mf_account, - } = client; - - const { field_ref_to_focus, setFieldRefToFocus } = ui; - - const { data: residence_list, isLoading: is_loading_residence_list } = useResidenceList(); - - const { - is_global_sms_available, - is_global_whatsapp_available, - legacy_core_countries_list, - selected_phone_code, - selected_country_list, - updatePhoneSettings, - } = useGetPhoneNumberList(); - - const { data: states_list, isLoading: is_loading_state_list } = useStatesList(residence); - - const { - refreshNotifications, - showPOAAddressMismatchSuccessNotification, - showPOAAddressMismatchFailureNotification, - } = notifications; - - const has_poa_address_mismatch = account_status?.status?.includes('poa_address_mismatch'); - const [rest_state, setRestState] = useState({ - show_form: true, - }); - - const notification_timeout = useRef(); - const scroll_div_ref = useRef(null); - - const [start_on_submit_timeout, setStartOnSubmitTimeout] = useState<{ - is_timeout_started: boolean; - timeout_callback: () => void; - }>({ - is_timeout_started: false, - timeout_callback: () => null, - }); - - useEffect(() => { - fetchAccountSettings(); - }, [fetchAccountSettings]); - - const should_show_loader = - is_loading_state_list || - is_loading || - is_loading_residence_list || - !isPhoneNumberVerificationLoaded || - !isCountryCodeLoaded; - - useEffect(() => { - const init = async () => { - try { - // Order of API calls is important - await WS.wait('get_settings'); - await invalidate('residence_list'); - await invalidate('states_list'); - } finally { - setIsLoading(false); - } - }; - if (is_language_changing) { - setIsLoading(true); - init(); - } - }, [invalidate, is_language_changing]); - - const checkForInitialCarriersSupported = () => { - const is_sms_carrier_available = - selected_country_list?.carriers && - (selected_country_list?.carriers as string[]).includes('sms') && - is_global_sms_available; - - const is_whatsapp_carrier_available = - selected_country_list?.carriers && - (selected_country_list?.carriers as string[]).includes('whatsapp') && - is_global_whatsapp_available; - - return is_sms_carrier_available || is_whatsapp_carrier_available; - }; - - const hintMessage = () => { - if (isPhoneNumberVerificationEnabled) { - if (is_phone_number_verified) { - return ( - , - ]} - /> - ); - } - } else { - return null; - } - }; - - const onSubmit = async ( - values: PersonalDetailsValueTypes, - { setStatus, setSubmitting }: FormikHelpers - ) => { - setStatus({ msg: '' }); - const request = makeSettingsRequest({ ...values }, residence_list, states_list, is_virtual); - setIsBtnLoading(true); - const data = await WS.authorized.setSettings(request); - - if (data.error) { - setStatus({ msg: data.error.message, code: data.error.code }); - setIsBtnLoading(false); - setSubmitting(false); - } else { - // Adding a delay to show the notification after the page reload - notification_timeout.current = setTimeout(() => { - if (data.set_settings.notification) { - showPOAAddressMismatchSuccessNotification(); - } else if (has_poa_address_mismatch) { - showPOAAddressMismatchFailureNotification(); - } - }, 2000); - - // force request to update settings cache since settings have been updated - const response = await WS.authorized.storage.getSettings(); - if (response.error) { - setRestState(prev_state => ({ ...prev_state, api_error: response.error.message })); - return; - } - // Fetches the status of the account after update - updatePhoneSettings(); - updateAccountStatus(); - refreshNotifications(); - setIsBtnLoading(false); - setIsSubmitSuccess(true); - setStartOnSubmitTimeout({ - is_timeout_started: true, - timeout_callback: () => { - setSubmitting(false); - }, - }); - // redirection back based on 'from' param in query string - const url_query_string = window.location.search; - const url_params = new URLSearchParams(url_query_string); - if (url_params.get('from')) { - const from = url_params.get('from') as keyof typeof routes; - history.push(routes[from]); - } - } - }; - - useEffect(() => () => clearTimeout(notification_timeout.current), []); - - useEffect(() => { - let timeout_id: NodeJS.Timeout; - if (start_on_submit_timeout.is_timeout_started) { - timeout_id = setTimeout(() => { - setIsSubmitSuccess(false); - setStartOnSubmitTimeout({ - is_timeout_started: false, - timeout_callback: () => setIsSubmitSuccess(false), - }); - }, 3000); - } - - return () => { - clearTimeout(timeout_id); - }; - }, [start_on_submit_timeout.is_timeout_started]); - - const showForm = (show_form: boolean) => setRestState({ show_form }); - - const isFieldDisabled = (name: string): boolean => { - return !!account_settings?.immutable_fields?.includes(name); - }; - - const employment_tax_editable_fields = useMemo(() => { - const fields_to_disable = ['employment_status', 'tax_identification_number'].filter(field => - isFieldImmutable(field, account_settings?.immutable_fields) - ); - /* - [TODO]: Will be removed once BE enables tax_residence in immutable_fields - If Tax_residence value is present in response, then it must not be editable - */ - if (!account_settings?.tax_residence) { - fields_to_disable.push('tax_residence'); - } - return fields_to_disable; - }, [account_settings?.immutable_fields, account_settings?.tax_residence]); - - const { api_error, show_form } = rest_state; - const loadTimer = useRef(); - - // To facilitate scrolling to the field that is to be focused - useLayoutEffect(() => { - if (field_ref_to_focus && !should_show_loader && !api_error) { - loadTimer.current = setTimeout(() => { - const parentRef = isDesktop - ? document.querySelector('.account-form__personal-details .dc-themed-scrollbars') - : document.querySelector('.account__scrollbars_container--grid-layout'); - const targetRef = document.getElementById(field_ref_to_focus) as HTMLElement; - const offset = 24; // 24 is the padding of the container - scrollToTop(parentRef as HTMLElement, targetRef, offset); - }, 0); - } - return () => { - if (field_ref_to_focus) { - clearTimeout(loadTimer.current); - } - }; - }, [field_ref_to_focus, isDesktop, should_show_loader, api_error, scrollToTop, setFieldRefToFocus]); - - useEffect(() => { - return () => { - setFieldRefToFocus(null); - }; - }, [setFieldRefToFocus]); - - if (api_error) return ; - - if (should_show_loader) { - return ; - } - - const is_poa_verified = authentication_status?.document_status === AUTH_STATUS_CODES.VERIFIED; - const is_poi_verified = authentication_status?.identity_status === AUTH_STATUS_CODES.VERIFIED; - - const is_account_verified = is_poa_verified && is_poi_verified; - - const stripped_phone_number = isCountryCodeDropdownEnabled - ? account_settings.phone?.replace(/\D/g, '') - : `+${account_settings.phone?.replace(/\D/g, '')}`; - - //Generate Redirection Link to user based on verification status - const getRedirectionLink = () => { - if (!is_poi_verified) { - return '/account/proof-of-identity'; - } else if (!is_poa_verified) { - return '/account/proof-of-address'; - } - return undefined; - }; - - const is_tin_auto_set = Boolean(account_settings?.tin_skipped); - - const is_employment_status_tin_mandatory = Boolean(account_status?.status?.includes('mt5_additional_kyc_required')); - - const PersonalDetailSchema = getPersonalDetailsValidationSchema( - is_virtual, - is_svg, - tin_validation_config, - is_tin_auto_set, - account_settings?.immutable_fields, - is_employment_status_tin_mandatory, - isCountryCodeDropdownEnabled - ); - const displayErrorMessage = (status: { code: string; msg: string }) => { - if (status?.code === 'PhoneNumberTaken') { - return ( - ]} - /> - } - text_color='loss-danger' - weight='none' - /> - ); - } - return ; - }; - - const initialValues = getPersonalDetailsInitialValues( - account_settings, - residence_list, - states_list, - is_virtual, - selected_phone_code, - checkForInitialCarriersSupported(), - isCountryCodeDropdownEnabled - ); - - return ( - - {({ - values, - errors, - setStatus, - status, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - isValid, - setFieldValue, - setFieldTouched, - dirty, - }) => ( - - - {show_form && ( -
- - - {!is_virtual && ( - - {isDesktop ? ( - - - - - ) : ( - -
- -
-
- -
-
- )} - {'place_of_birth' in values && ( -
- -
- )} -
- -
- -
- -
-
- )} -
- -
- {!is_virtual && ( - -
-
-
- {isCountryCodeDropdownEnabled && ( - { - setFieldValue( - 'calling_country_code', - country_list.value, - true - ); - const is_sms_carrier_available = - //@ts-expect-error carriers is not defined in TListItem type - country_list.carriers && - //@ts-expect-error carriers is not defined in TListItem type - (country_list.carriers as string[]).includes( - 'sms' - ) && - is_global_sms_available; - const is_whatsapp_carrier_available = - //@ts-expect-error carriers is not defined in TListItem type - country_list.carriers && - //@ts-expect-error carriers is not defined in TListItem type - (country_list.carriers as string[]).includes( - 'whatsapp' - ) && - is_global_whatsapp_available; - setFieldValue( - 'is_carriers_available', - is_sms_carrier_available || - is_whatsapp_carrier_available, - true - ); - }} - disabled={ - isFieldDisabled('calling_country_code') || - !!next_email_otp_request_timer || - is_email_otp_timer_loading - } - /> - )} - ) => { - let phone_number = e.target.value.replace(/\D/g, ''); - if (!isCountryCodeDropdownEnabled) { - phone_number = - phone_number.length === 0 - ? '+' - : `+${phone_number}`; - } - setFieldValue('phone', phone_number, true); - setStatus(''); - }} - onBlur={handleBlur} - required - error={errors.phone} - disabled={ - isFieldDisabled('phone') || - !!next_email_otp_request_timer || - is_email_otp_timer_loading - } - data-testid='dt_phone' - /> -
- {isPhoneNumberVerificationEnabled && ( - - )} -
- {is_phone_number_verified && ( -
- - {hintMessage()} - -
- )} -
- -
- )} - {!is_virtual && ( -
- - - {has_poa_address_mismatch && } - -
-
- -
-
- -
-
- -
-
- {states_list.length ? ( - - ) : ( - - )} -
-
- -
-
-
- )} - {!!current_landing_company?.support_professional_client && ( - -
-
- -
-
- - - - - - - - - - - - -
- {is_account_verified ? ( - { - setFieldValue( - 'request_professional_status', - values.request_professional_status ? 0 : 1 - ); - setFieldTouched('request_professional_status', true, true); - }} - label={localize( - 'I would like to be treated as a professional client.' - )} - id='request_professional_status' - disabled={ - is_virtual || !!account_settings.request_professional_status - } - greyDisabled - /> - ) : ( - - , - ]} - /> - - } - is_info - is_inline - /> - )} -
-
-
- - )} - - -
- { - setFieldValue('email_consent', values.email_consent ? 0 : 1); - setFieldTouched('email_consent', true, true); - }} - label={localize('Get updates about Deriv products, services and events.')} - id='email_consent' - defaultChecked={!!values.email_consent} - disabled={isFieldDisabled('email_consent') && !is_virtual} - /> -
-
- - - {status?.msg && displayErrorMessage(status)} - {!is_virtual && !(isSubmitting || is_submit_success || status?.msg) && ( - - - - )} - -
- ); - } -); diff --git a/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx b/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx deleted file mode 100644 index 9e7aa1dea411..000000000000 --- a/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { useDepositLocked, useCurrentCurrencyConfig } from '@deriv/hooks'; -import { mockStore } from '@deriv/stores'; -import CashierProviders from '../../../cashier-providers'; -import Deposit from '../deposit'; -import { createBrowserHistory } from 'history'; -import { Router } from 'react-router'; - -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - useDepositLocked: jest.fn(() => false), - useCashierLocked: jest.fn(() => false), - useIsSystemMaintenance: jest.fn(() => false), - useCurrentCurrencyConfig: jest.fn(() => ({ - platform: { cashier: ['doughflow'], ramp: [] }, - })), -})); - -jest.mock('../../../modules', () => ({ - ...jest.requireActual('@deriv/hooks'), - DepositFiatModule: () =>
DepositFiatModule
, - DepositCryptoModule: () =>
DepositCryptoModule
, -})); - -jest.mock('Components/transactions-crypto-history', () => { - const TransactionsCryptoHistory = () =>
TransactionsCryptoHistory
; - return TransactionsCryptoHistory; -}); - -jest.mock('../deposit-locked', () => { - const DepositLocked = () =>
DepositLocked
; - return DepositLocked; -}); - -describe('', () => { - let mockRootStore: ReturnType; - beforeEach(() => { - (useDepositLocked as jest.Mock).mockReturnValue(false); - mockRootStore = mockStore({ - client: { - is_authorize: true, - }, - modules: { - cashier: { - iframe: {}, - transaction_history: { - is_transactions_crypto_visible: false, - }, - general_store: { - is_crypto: false, - is_deposit: false, - }, - }, - }, - traders_hub: { is_low_risk_cr_eu_real: false }, - }); - }); - - const renderDeposit = () => { - render(, { - wrapper: ({ children }) => ( - - {children} - - ), - }); - }; - - it('should render component', () => { - (useDepositLocked as jest.Mock).mockReturnValue(true); - - renderDeposit(); - - expect(screen.getByText('DepositLocked')).toBeInTheDocument(); - }); - - it('should render component', () => { - mockRootStore.modules.cashier.transaction_history.is_transactions_crypto_visible = true; - - renderDeposit(); - - expect(screen.getByText('TransactionsCryptoHistory')).toBeInTheDocument(); - }); - - it('renders `DepositFiatModule` if cashier platform provider equals to `doughflow`', () => { - const mock_root_store = mockStore({ - modules: { - cashier: { - transaction_history: { - is_transactions_crypto_visible: false, - }, - general_store: { - is_deposit: true, - }, - }, - }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - }); - - it('renders `DepositCryptoModule` if cashier platform provider equals to `crypto`', () => { - const mock_root_store = mockStore({ - modules: { - cashier: { - transaction_history: { - is_transactions_crypto_visible: false, - }, - general_store: { - is_deposit: true, - setIsDeposit: jest.fn(), - }, - }, - }, - }); - - (useCurrentCurrencyConfig as jest.Mock).mockReturnValue({ - platform: { cashier: ['crypto'], ramp: ['banxa'] }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - }); -}); diff --git a/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx b/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx deleted file mode 100644 index 945f8d4ec909..000000000000 --- a/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { Checklist } from '@deriv/components'; -import DepositLocked from '../deposit-locked'; -import { mockStore } from '@deriv/stores'; -import CashierProviders from '../../../../cashier-providers'; -import { useIsTNCNeeded } from '@deriv/hooks'; - -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - useIsTNCNeeded: jest.fn(), -})); - -jest.mock('Components/cashier-locked', () => { - const CashierLocked = () => ( -
-
- We were unable to verify your information automatically. To enable this function, you must complete the - following: -
-
Complete the financial assessment form
-
- ); - return CashierLocked; -}); - -describe('', () => { - it('should show the proof of identity document verification message', () => { - (useIsTNCNeeded as jest.Mock).mockReturnValue(false); - - const mock_root_store = mockStore({ - client: { - account_status: { - cashier_validation: [], - authentication: { - identity: { - status: 'pending', - }, - document: { - status: 'none', - }, - needs_verification: ['identity'], - }, - }, - is_financial_information_incomplete: false, - is_trading_experience_incomplete: false, - is_financial_account: false, - }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - - expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); - expect(screen.getByText('Check proof of identity document verification status')).toBeInTheDocument(); - }); - - it('should show the proof of address document verification message', () => { - (useIsTNCNeeded as jest.Mock).mockReturnValue(false); - - const mock_root_store = mockStore({ - client: { - account_status: { - cashier_validation: [], - authentication: { - identity: { - status: 'none', - }, - document: { - status: 'pending', - }, - needs_verification: ['document'], - }, - }, - is_financial_information_incomplete: false, - is_trading_experience_incomplete: false, - is_financial_account: false, - }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - - expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); - expect(screen.getByText('Check proof of address document verification status')).toBeInTheDocument(); - }); - - it('should show the terms and conditions accept button', () => { - (useIsTNCNeeded as jest.Mock).mockReturnValue(true); - - const mock_root_store = mockStore({ - client: { - account_status: { - cashier_validation: [], - authentication: {}, - }, - is_financial_information_incomplete: false, - is_trading_experience_incomplete: false, - is_financial_account: false, - }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - - expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); - expect( - screen.getByRole('button', { - name: /I accept/i, - }) - ).toBeInTheDocument(); - }); - - it('should show the financial assessment completion message', () => { - (useIsTNCNeeded as jest.Mock).mockReturnValue(false); - - const mock_root_store = mockStore({ - client: { - account_status: { - cashier_validation: [], - authentication: {}, - }, - is_financial_information_incomplete: true, - is_trading_experience_incomplete: false, - is_financial_account: false, - }, - }); - - render(, { - wrapper: ({ children }) => {children}, - }); - - expect( - screen.getByText( - 'We were unable to verify your information automatically. To enable this function, you must complete the following:' - ) - ).toBeInTheDocument(); - expect(screen.getByText('Complete the financial assessment form')).toBeInTheDocument(); - }); - - it('should trigger click on the checklist item', () => { - const onClick = jest.fn(); - const items = [ - { - content: 'Check proof of identity document verification status', - status: 'action', - onClick, - }, - ]; - render(); - const btn = screen.getByTestId('dt_checklist_item_status_action'); - - fireEvent.click(btn); - expect(onClick).toHaveBeenCalled(); - }); -}); diff --git a/packages/cashier/src/pages/deposit/deposit-locked/deposit-locked.tsx b/packages/cashier/src/pages/deposit/deposit-locked/deposit-locked.tsx deleted file mode 100644 index d2d1757e91a0..000000000000 --- a/packages/cashier/src/pages/deposit/deposit-locked/deposit-locked.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { Icon, Checklist, StaticUrl, Text } from '@deriv/components'; -import { useIsTNCNeeded } from '@deriv/hooks'; -import { Localize, localize } from '@deriv/translations'; -import { routes, WS } from '@deriv/shared'; -import { useStore, observer } from '@deriv/stores'; -import CashierLocked from '../../../components/cashier-locked'; - -type TItems = { - button_text?: string; - onClick: () => void; - status: string; - is_disabled?: boolean; - content: string | JSX.Element; -}; - -const DepositLocked = observer(() => { - const { client } = useStore(); - const { - account_status, - is_financial_account, - is_financial_information_incomplete, - is_trading_experience_incomplete, - is_virtual, - updateAccountStatus, - } = client; - - // handle authentication locked - const identity = account_status?.authentication?.identity; - const document = account_status?.authentication?.document; - const needs_verification = account_status?.authentication?.needs_verification; - const is_poi_needed = needs_verification?.includes('identity'); - const is_poa_needed = needs_verification?.includes('document'); - const has_poi_submitted = identity?.status !== 'none'; - const has_poa_submitted = document?.status !== 'none'; - const history = useHistory(); - const is_tnc_needed = useIsTNCNeeded(); - - // handle TnC - const acceptTnc = async () => { - await WS.tncApproval(); - await WS.getSettings(); - - if (!is_virtual && !account_status?.status?.includes('deposit_attempt')) { - await updateAccountStatus(); - } - }; - - // handle all deposits lock status - const items: TItems[] = [ - ...(is_poi_needed && has_poi_submitted - ? [ - { - content: localize('Check proof of identity document verification status'), - status: 'action', - onClick: () => history.push(routes.proof_of_identity), - }, - ] - : []), - ...(is_poa_needed && has_poa_submitted - ? [ - { - content: localize('Check proof of address document verification status'), - status: 'action', - onClick: () => history.push(routes.proof_of_address), - }, - ] - : []), - ...(is_tnc_needed - ? [ - { - content: ( - , - ]} - /> - ), - status: 'button-action', - onClick: () => acceptTnc(), - button_text: localize('I accept'), - }, - ] - : []), - ...(is_financial_account && (is_financial_information_incomplete || is_trading_experience_incomplete) - ? [ - { - content: localize('Complete the financial assessment form'), - status: 'action', - onClick: () => history.push(routes.financial_assessment), - }, - ] - : []), - ]; - return ( - <> - {items.length ? ( -
- - - {localize('Deposits are locked')} - - - - {localize('To enable this feature you must complete the following:')} - - -
- ) : ( - - )} - - ); -}); - -export default DepositLocked; diff --git a/packages/cashier/src/pages/deposit/deposit-locked/index.ts b/packages/cashier/src/pages/deposit/deposit-locked/index.ts deleted file mode 100644 index 9ec6d97c43e1..000000000000 --- a/packages/cashier/src/pages/deposit/deposit-locked/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import DepositLocked from './deposit-locked'; - -export default DepositLocked; diff --git a/packages/cashier/src/pages/deposit/deposit.tsx b/packages/cashier/src/pages/deposit/deposit.tsx deleted file mode 100644 index 87628153c668..000000000000 --- a/packages/cashier/src/pages/deposit/deposit.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { useDepositLocked, useCurrentCurrencyConfig } from '@deriv/hooks'; -import { observer, useStore } from '@deriv/stores'; -import { useCashierStore } from '../../stores/useCashierStores'; -import { CashierOnboardingModule } from '../../modules/cashier-onboarding'; -import PageContainer from '../../components/page-container'; -import DepositFiatModule from '../../modules/deposit-fiat/deposit-fiat'; -import DepositCryptoModule from '../../modules/deposit-crypto/deposit-crypto'; -import TransactionsCryptoHistory from '../../components/transactions-crypto-history'; -import DepositLocked from './deposit-locked'; - -const Deposit = observer(() => { - const { traders_hub } = useStore(); - const is_deposit_locked = useDepositLocked(); - const currency_config = useCurrentCurrencyConfig(); - const { transaction_history, general_store } = useCashierStore(); - const { is_low_risk_cr_eu_real } = traders_hub; - const { is_transactions_crypto_visible } = transaction_history; - const { is_deposit } = general_store; - - if (is_deposit_locked) - return ( - }> - - - ); - - if (is_transactions_crypto_visible) - return ( - - - - ); - - if (currency_config && (is_deposit || is_low_risk_cr_eu_real)) { - const is_crypto_provider = currency_config.platform.cashier.includes('crypto'); - - return is_crypto_provider ? : ; - } - - return ; -}); - -export default Deposit; diff --git a/packages/cashier/src/pages/deposit/index.ts b/packages/cashier/src/pages/deposit/index.ts deleted file mode 100644 index e0fe48bfe75c..000000000000 --- a/packages/cashier/src/pages/deposit/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Deposit from './deposit'; - -export default Deposit;