From dee185988b871c1096370a114f2cc6082dbd565f Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Mon, 9 Oct 2023 14:43:25 +0400 Subject: [PATCH 1/6] refactor: split components --- packages/account/src/App.tsx | 1 - .../__tests__/icon-message-list.spec.tsx | 121 +++++++++++------- .../icon-message-list/icon-message-list.tsx | 82 +++--------- .../icon-message-list/list-item.tsx | 21 +++ .../icon-message-list/maximum-list.tsx | 44 +++++++ packages/account/src/Types/common.type.ts | 4 + 6 files changed, 162 insertions(+), 111 deletions(-) create mode 100644 packages/account/src/Components/icon-message-list/list-item.tsx create mode 100644 packages/account/src/Components/icon-message-list/maximum-list.tsx diff --git a/packages/account/src/App.tsx b/packages/account/src/App.tsx index b263467137df..43a09b6f7fff 100644 --- a/packages/account/src/App.tsx +++ b/packages/account/src/App.tsx @@ -4,7 +4,6 @@ import ResetTradingPassword from './Containers/reset-trading-password'; import { APIProvider } from '@deriv/api'; import { StoreProvider } from '@deriv/stores'; import { TCoreStores } from '@deriv/stores/types'; -import { APIProvider } from '@deriv/api'; // TODO: add correct types for WS after implementing them type TAppProps = { diff --git a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx index 2591e61a77ff..db5b2a1b2333 100644 --- a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx +++ b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { Icon } from '@deriv/components'; import IconMessageList from '../icon-message-list'; +import { StoreProvider, mockStore } from '@deriv/stores'; jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -12,64 +13,96 @@ jest.mock('@deriv/components', () => { }); describe('', () => { const messages_list = ['Sample Text1', 'Sample Text2', 'Sample Text3', 'Sample Text4']; + + const mock_props = { + icon: , + message: 'Lorem Ipsom', + message_list: messages_list, + onContinue: jest.fn(), + }; + + const store_config = mockStore({}); + + const renderComponent = ({ props = mock_props, store = store_config }) => + render( + + + + ); + it('should render IconMessageList component', () => { - render(); + // render(); + renderComponent({}); expect(screen.getByTestId('dt_icon_message_list')).toBeInTheDocument(); }); - it('should render icon passed to the component', () => { - render(} />); - expect(screen.getByTestId('mocked_icon')).toBeInTheDocument(); - }); + // it('should render icon passed to the component', () => { + // // render(); + // const new_props = { + // ...mock_props, + // messages_list: ['Sample Text1'], + // }; + // renderComponent({ props: new_props }); + // expect(screen.getByTestId('mocked_icon')).toBeInTheDocument(); + // }); + it('should show message passed to the component', () => { - render(); + // render(); + renderComponent({}); expect(screen.getByText(/lorem ipsom/i)).toBeInTheDocument(); }); - it('when the length of message_list is less than 3, it should show messages with icons ', () => { - render(); - expect(screen.getByText(/Sample Text1/i)).toBeInTheDocument(); - expect(screen.getByText(/Sample Text2/i)).toBeInTheDocument(); - expect(screen.getAllByText(/icclosecircle/i).length).toBe(2); - }); - it('should show first 3 msgs and show_more_btn when the message_list is more than 3', () => { - render(); - expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); - expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); - expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); - expect(screen.queryByText(/sample text4/i)).not.toBeInTheDocument(); - expect( - screen.getByRole('button', { - name: /show more/i, - }) - ).toBeInTheDocument(); - expect( - screen.queryByRole('button', { - name: /show less/i, - }) - ).not.toBeInTheDocument(); - }); + // it('when the length of message_list is less than 3, it should show messages with icons ', () => { + // render(); + // expect(screen.getByText(/Sample Text1/i)).toBeInTheDocument(); + // expect(screen.getByText(/Sample Text2/i)).toBeInTheDocument(); + // expect(screen.getAllByText(/icclosecircle/i).length).toBe(2); + // }); - it('should show all messages and show_less_btn when show_more btn is clicked', () => { - render(); + // it('should show first 3 msgs and show_more_btn when the message_list is more than 3', () => { + // render(); + // expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); + // expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); + // expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); + // expect(screen.queryByText(/sample text4/i)).not.toBeInTheDocument(); + // expect( + // screen.getByRole('button', { + // name: /show more/i, + // }) + // ).toBeInTheDocument(); + // expect( + // screen.queryByRole('button', { + // name: /show less/i, + // }) + // ).not.toBeInTheDocument(); + // }); - const show_more_btn = screen.getByRole('button', { - name: /show more/i, - }); + // it('should show all messages and show_less_btn when show_more btn is clicked', () => { + // render(); - fireEvent.click(show_more_btn); - expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); - expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); - expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); - expect(screen.getByText(/sample text4/i)).toBeInTheDocument(); - expect(screen.queryByRole('button', { name: /show less/i })).toBeInTheDocument(); - expect(screen.queryByRole('button', { name: /show more/i })).not.toBeInTheDocument(); - }); + // const show_more_btn = screen.getByRole('button', { + // name: /show more/i, + // }); + + // fireEvent.click(show_more_btn); + // expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); + // expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); + // expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); + // expect(screen.getByText(/sample text4/i)).toBeInTheDocument(); + // expect(screen.queryByRole('button', { name: /show less/i })).toBeInTheDocument(); + // expect(screen.queryByRole('button', { name: /show more/i })).not.toBeInTheDocument(); + // }); it('should show continue_btn if OnContinue is passed', () => { const onContinuefn = jest.fn(); - render(); - const upload_btn = screen.queryByRole('button', { name: /upload document/i }); + const new_props = { + ...mock_props, + messages_list: ['Sample Text1'], + onContinue: onContinuefn, + }; + renderComponent({ props: new_props }); + // render(); + const upload_btn = screen.queryByRole('button', { name: /verify again/i }); expect(upload_btn).toBeInTheDocument(); fireEvent.click(upload_btn); expect(onContinuefn).toHaveBeenCalled(); diff --git a/packages/account/src/Components/icon-message-list/icon-message-list.tsx b/packages/account/src/Components/icon-message-list/icon-message-list.tsx index 495c9e6a34e8..810eb83c4813 100644 --- a/packages/account/src/Components/icon-message-list/icon-message-list.tsx +++ b/packages/account/src/Components/icon-message-list/icon-message-list.tsx @@ -1,44 +1,28 @@ import React from 'react'; import classNames from 'classnames'; -import { localize } from '@deriv/translations'; -import { Div100vhContainer, Text, Button, Icon, ThemedScrollbars } from '@deriv/components'; -import { isDesktop, isMobile } from '@deriv/shared'; - -type TListItem = { - text?: string; -}; - -type TMessage_list = { - message_list?: string[]; -}; +import { Localize } from '@deriv/translations'; +import { Div100vhContainer, Text, Button, ThemedScrollbars } from '@deriv/components'; +import ListItem from './list-item'; +import MaximumList from './maximum-list'; +import { TMessage_list } from '../../Types'; +import { observer, useStore } from '@deriv/stores'; type TIconMessageList = TMessage_list & { - className: string; + className?: string; icon: React.ReactElement; message: string; onContinue: () => void; }; -const ListItem = ({ text }: TListItem) => ( -
-
- -
-
- - {text} - -
-
-); - -const IconMessageList = ({ className, icon, message, message_list = [], onContinue }: Partial) => { +const IconMessageList = observer(({ className, icon, message, message_list = [], onContinue }: TIconMessageList) => { + const { ui } = useStore(); + const { is_mobile, is_desktop } = ui; const has_maximum_list = message_list.length > 3; return ( - +
+ > + + )}
); -}; - -const MaximumList = ({ message_list }: TMessage_list) => { - const [show_more, setShowMore] = React.useState(false); - const maximum_list = message_list.slice(0, 3); - - return show_more ? ( - - {message_list.map(text => ( - - ))} - + + ) : ( + + {maximum_list.map(text => ( + + ))} + + + ); +}; + +export default MaximumList; diff --git a/packages/account/src/Types/common.type.ts b/packages/account/src/Types/common.type.ts index 525eec63ac43..aee5ce6db1e9 100644 --- a/packages/account/src/Types/common.type.ts +++ b/packages/account/src/Types/common.type.ts @@ -162,3 +162,7 @@ export type TServerError = { details?: { [key: string]: string }; fields?: string[]; }; + +export type TMessage_list = { + message_list: string[]; +}; From 2e5b8f6448bc3f4c228336867a2bd0f59959c900 Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Wed, 11 Oct 2023 18:51:40 +0400 Subject: [PATCH 2/6] feat: incorporated idv-error codes --- .../idv-failed/__tests__/idv-failed.spec.tsx | 22 +++---- .../poi/idv-status/idv-failed/idv-failed.tsx | 30 +++++---- .../__tests__/idv-submit-complete.spec.tsx | 6 +- .../idv-submit-complete.tsx | 10 +-- .../poi-country-selector.tsx | 6 +- packages/account/src/Helpers/utils.tsx | 8 +-- .../proof-of-identity-submission.jsx | 4 +- .../src/utils/constants/idv-failure-codes.ts | 61 +++++++++++++++++-- .../src/utils/helpers/format-response.ts | 30 ++++----- 9 files changed, 115 insertions(+), 62 deletions(-) diff --git a/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx index a6dea68dd7af..6de1c4c5a8c1 100644 --- a/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import IdvFailed from '../idv-failed'; -import { idv_error_statuses } from '@deriv/shared'; +import { IDV_ERROR_STATUS } from '@deriv/shared'; import { StoreProvider, mockStore } from '@deriv/stores'; jest.mock('@deriv/shared', () => ({ @@ -39,7 +39,7 @@ describe('', () => { account_settings: { citizen: 'gh', }, - mismatch_status: idv_error_statuses.poi_name_mismatch, + mismatch_status: IDV_ERROR_STATUS.NameMismatch.code, latest_status: {}, }; @@ -56,48 +56,48 @@ describe('', () => { renderComponent({}); await waitFor(() => { - expect(screen.getByTestId(idv_error_statuses.poi_name_mismatch)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.NameMismatch.code)).toBeInTheDocument(); expect(screen.getByText('PersonalDetailsForm')).toBeInTheDocument(); }); }); it('should render IDVfailed component with dob mismatch message', async () => { - const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_dob_mismatch }; + const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.DobMismatch.code }; renderComponent({ props: new_props }); await waitFor(() => { - expect(screen.getByTestId(idv_error_statuses.poi_dob_mismatch)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.DobMismatch.code)).toBeInTheDocument(); expect(screen.queryByText('IDVForm')).not.toBeInTheDocument(); }); }); it('should render IDVfailed component with name & DOB mismatch message', async () => { - const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_name_dob_mismatch }; + const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.NameDOBMismatch.code }; renderComponent({ props: new_props }); await waitFor(() => { - expect(screen.getByTestId(idv_error_statuses.poi_name_dob_mismatch)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.NameDOBMismatch.code)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Update profile/i })).toBeInTheDocument(); }); }); it('should render IDVfailed component with expired message', async () => { - const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_expired }; + const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.Expired.code }; renderComponent({ props: new_props }); await waitFor(() => { - expect(screen.getByTestId(idv_error_statuses.poi_expired)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.Expired.code)).toBeInTheDocument(); expect(screen.getByText('IDVForm')).toBeInTheDocument(); expect(screen.getByText('PersonalDetailsForm')).toBeInTheDocument(); }); }); it('should render IDVfailed component with verification failed message', async () => { - const new_props = { ...mock_props, mismatch_status: idv_error_statuses.poi_failed }; + const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.Failed.code }; renderComponent({ props: new_props }); await waitFor(() => { - expect(screen.getByTestId(idv_error_statuses.poi_failed)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.Failed.code)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Verify/i })).toBeInTheDocument(); }); }); diff --git a/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx b/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx index 86835ad2e6dd..30bd664bec3f 100644 --- a/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx @@ -11,7 +11,7 @@ import { Button, DesktopWrapper, HintBox, Loading, Text } from '@deriv/component import { filterObjProperties, getIDVNotApplicableOption, - idv_error_statuses, + IDV_ERROR_STATUS, isEmptyObject, isMobile, removeEmptyPropertiesFromObject, @@ -77,7 +77,7 @@ const IdvFailed = ({ residence_list, account_settings, handleSubmit, - mismatch_status = idv_error_statuses.poi_failed, + mismatch_status = IDV_ERROR_STATUS.Failed.code as TIDVErrorStatus, latest_status, selected_country, }: TIdvFailed) => { @@ -100,7 +100,7 @@ const IdvFailed = ({ }); const is_document_upload_required = React.useMemo( - () => [idv_error_statuses.poi_expired, idv_error_statuses.poi_failed].includes(mismatch_status), + () => [IDV_ERROR_STATUS.Expired.code, IDV_ERROR_STATUS.Failed.code].includes(mismatch_status), [mismatch_status] ); @@ -118,12 +118,23 @@ const IdvFailed = ({ const IDV_NOT_APPLICABLE_OPTION = React.useMemo(() => getIDVNotApplicableOption(), []); + const renderErrorMessage = (mismatch_status: TIDVErrorStatus) => { + switch (mismatch_status) { + case IDV_ERROR_STATUS.Expired.code: + return 'Your identity document has expired.'; + case IDV_ERROR_STATUS.Failed.code: + return 'We were unable to verify the identity document with the details provided.'; + default: + return IDV_ERROR_STATUS[mismatch_status].message; + } + }; + const generateIDVError = React.useCallback(() => { const document_name = is_document_upload_required ? 'identity document' : getIDVDocumentType(latest_status, chosen_country); switch (mismatch_status) { - case idv_error_statuses.poi_name_dob_mismatch: + case IDV_ERROR_STATUS.NameDOBMismatch.code: return { required_fields: ['first_name', 'last_name', 'date_of_birth'], side_note_image: , @@ -141,7 +152,7 @@ const IdvFailed = ({ /> ), }; - case idv_error_statuses.poi_name_mismatch: + case IDV_ERROR_STATUS.NameMismatch.code: return { required_fields: ['first_name', 'last_name'], side_note_image: , @@ -159,7 +170,7 @@ const IdvFailed = ({ /> ), }; - case idv_error_statuses.poi_dob_mismatch: + case IDV_ERROR_STATUS.DobMismatch.code: return { required_fields: ['date_of_birth'], side_note_image: , @@ -192,16 +203,13 @@ const IdvFailed = ({ ), }; } - }, [latest_status, mismatch_status, chosen_country]); + }, [is_document_upload_required, latest_status, chosen_country, mismatch_status]); React.useEffect(() => { const initializeFormValues = async (required_fields: string[]) => { diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx index 5e5dcf0b70dc..3d264fb1a749 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; -import { idv_error_statuses } from '@deriv/shared'; +import { IDV_ERROR_STATUS } from '@deriv/shared'; import IdvSubmitComplete from '../idv-submit-complete'; import { StoreProvider, mockStore } from '@deriv/stores'; @@ -91,7 +91,7 @@ describe('', () => { const new_props: TIdvSubmitCompleteProps = { ...mock_props, - mismatch_status: idv_error_statuses.poi_name_dob_mismatch, + mismatch_status: IDV_ERROR_STATUS.NameDOBMismatch.code, }; renderComponent({ props: new_props, store_config: new_store }); @@ -119,7 +119,7 @@ describe('', () => { const new_props: TIdvSubmitCompleteProps = { ...mock_props, - mismatch_status: idv_error_statuses.poi_expired, + mismatch_status: IDV_ERROR_STATUS.Expired.code, }; renderComponent({ props: new_props, store_config: new_store }); diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx index 1db9a21d7e4b..28392bf14fd1 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Text } from '@deriv/components'; -import { idv_error_statuses, TIDVErrorStatus } from '@deriv/shared'; +import { TIDVErrorStatus, IDV_ERROR_STATUS } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import { Localize } from '@deriv/translations'; import IdvDocumentPending from 'Assets/ic-idv-document-pending.svg'; @@ -19,12 +19,12 @@ const IdvSubmitComplete = observer( const { is_already_attempted } = client; const is_mismatch_error = - mismatch_status === idv_error_statuses.poi_name_dob_mismatch || - mismatch_status === idv_error_statuses.poi_dob_mismatch || - mismatch_status === idv_error_statuses.poi_name_mismatch; + mismatch_status === IDV_ERROR_STATUS.NameDOBMismatch.code || + mismatch_status === IDV_ERROR_STATUS.DobMismatch.code || + mismatch_status === IDV_ERROR_STATUS.NameMismatch.code; const is_expired_or_failed_error = - mismatch_status === idv_error_statuses.poi_expired || mismatch_status === idv_error_statuses.poi_failed; + mismatch_status === IDV_ERROR_STATUS.Expired.code || mismatch_status === IDV_ERROR_STATUS.Failed.code; const getHeaderText = () => { if (is_already_attempted) { diff --git a/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx b/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx index 6b8ffec4f25d..332f542972ee 100644 --- a/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx +++ b/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx @@ -2,7 +2,7 @@ import React from 'react'; import classNames from 'classnames'; import { Formik, Field, FormikErrors, FormikValues, FormikHelpers } from 'formik'; import { Autocomplete, Button, DesktopWrapper, HintBox, MobileWrapper, Text, SelectNative } from '@deriv/components'; -import { idv_error_statuses, isMobile, TIDVErrorStatus } from '@deriv/shared'; +import { IDV_ERROR_STATUS, isMobile, TIDVErrorStatus } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; import FormFooter from 'Components/form-footer'; @@ -62,10 +62,10 @@ const CountrySelector = ({ }, [residence_list]); let failed_message: JSX.Element | null = null; - if (mismatch_status === idv_error_statuses.poi_expired) { + if (mismatch_status === IDV_ERROR_STATUS.Expired.code) { failed_message = ; } - if (mismatch_status === idv_error_statuses.poi_failed) { + if (mismatch_status === IDV_ERROR_STATUS.Failed.code) { failed_message = ( ); diff --git a/packages/account/src/Helpers/utils.tsx b/packages/account/src/Helpers/utils.tsx index 27789858ac48..2d501746e05e 100644 --- a/packages/account/src/Helpers/utils.tsx +++ b/packages/account/src/Helpers/utils.tsx @@ -7,7 +7,7 @@ import { validLength, validName, getIDVNotApplicableOption, - idv_error_statuses, + IDV_ERROR_STATUS, } from '@deriv/shared'; import { ResidenceList, GetSettings, GetAccountStatus } from '@deriv/api-types'; import { FormikValues } from 'formik'; @@ -257,12 +257,12 @@ export const validate = (errors: Record, values: T) => { }; }; -type TIDVErrorStatus = typeof idv_error_statuses[keyof typeof idv_error_statuses]; +type TIDVErrorStatus = keyof typeof IDV_ERROR_STATUS; export const verifyFields = (status: TIDVErrorStatus) => { switch (status) { - case idv_error_statuses.poi_dob_mismatch: + case IDV_ERROR_STATUS.DobMismatch.code: return ['date_of_birth']; - case idv_error_statuses.poi_name_mismatch: + case IDV_ERROR_STATUS.NameMismatch.code: return ['first_name', 'last_name']; default: return ['first_name', 'last_name', 'date_of_birth']; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx index b2daa3b6cc97..380cbaa0925e 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { formatIDVError, WS, idv_error_statuses } from '@deriv/shared'; +import { formatIDVError, WS, IDV_ERROR_STATUS } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import CountrySelector from 'Components/poi/poi-country-selector'; import IdvDocumentSubmit from 'Components/poi/idv-document-submit'; @@ -120,7 +120,7 @@ const POISubmission = observer( setIdentityService(identity_last_attempt); } else if ( mismatch_status && - ![idv_error_statuses.poi_expired, idv_error_statuses.poi_failed].includes(mismatch_status) && + ![IDV_ERROR_STATUS.Expired.code, IDV_ERROR_STATUS.Failed.code].includes(mismatch_status) && idv.submissions_left > 0 ) { setSubmissionService(service_code.idv); diff --git a/packages/shared/src/utils/constants/idv-failure-codes.ts b/packages/shared/src/utils/constants/idv-failure-codes.ts index 5df21e24c9d5..aa4116504698 100644 --- a/packages/shared/src/utils/constants/idv-failure-codes.ts +++ b/packages/shared/src/utils/constants/idv-failure-codes.ts @@ -1,7 +1,56 @@ -export const idv_error_statuses = Object.freeze({ - poi_name_dob_mismatch: 'POI_NAME_DOB_MISMATCH', - poi_dob_mismatch: 'POI_DOB_MISMATCH', - poi_name_mismatch: 'POI_NAME_MISMATCH', - poi_expired: 'POI_EXPIRED', - poi_failed: 'POI_FAILED', +export const IDV_ERROR_STATUS = Object.freeze({ + DobMismatch: { + code: 'DobMismatch', + message: "The date of birth retrieved from your document doesn't match your profile.", + }, + DocumentRejected: { code: 'DocumentRejected', message: 'Document was rejected by the provider' }, + EmptyStatus: { + code: 'EmptyStatus', + message: 'The verification status was empty, rejected for lack of information.', + }, + Expired: { code: 'Expired', message: "The document's validity has been expired." }, + InformationLack: { + code: 'InformationLack', + message: 'The verification is passed but the personal info is not available to compare.', + }, + MalformedJson: { + code: 'MalformedJson', + message: 'The verification status is not available, provider says: Malformed JSON.', + }, + NameMismatch: { + code: 'NameMismatch', + message: "The name retrieved from your document doesn't match your profile.", + }, + RejectedByProvider: { + code: 'RejectedByProvider', + message: 'The document was rejected by the Provider.', + }, + Underage: { + code: 'Underage', + message: "You're under legal age.", + }, + Deceased: { + code: 'Deceased', + message: "The document's owner is deceased.", + }, + Failed: { + code: 'Failed', + message: 'We were unable to verify the identity document with the details provided.', + }, + NameDOBMismatch: { + code: 'NameDOBMismatch', + message: '', + }, +}); + +export const Onfido_ERROR_STATUS = Object.freeze({ + AgeValidationMinimumAcceptedAge: { + code: 'AgeValidationMinimumAcceptedAge', + message: + "Your age in the document you provided appears to be below 18 years. We're only allowed to offer our services to clients above 18 years old, so we'll need to close your account. If you have a balance in your account, contact us via live chat and we'll help to withdraw your funds before your account is closed.", + }, + CompromisedDocument: { + code: 'CompromisedDocument', + message: 'Your document failed our verification checks.', + }, }); diff --git a/packages/shared/src/utils/helpers/format-response.ts b/packages/shared/src/utils/helpers/format-response.ts index 7149b8d706f5..2ec3af878dd2 100644 --- a/packages/shared/src/utils/helpers/format-response.ts +++ b/packages/shared/src/utils/helpers/format-response.ts @@ -3,7 +3,7 @@ import { STATUS_CODES, getUnsupportedContracts } from '../constants'; import { getSymbolDisplayName, TActiveSymbols } from './active-symbols'; import { getMarketInformation } from './market-underlying'; import { TContractInfo } from '../contract'; -import { idv_error_statuses } from '../constants/idv-failure-codes'; +import { IDV_ERROR_STATUS } from '../constants/idv-failure-codes'; type TIsUnSupportedContract = { contract_type?: string; @@ -43,7 +43,7 @@ export const formatPortfolioPosition = ( }; }; -export type TIDVErrorStatus = typeof idv_error_statuses[keyof typeof idv_error_statuses]; +export type TIDVErrorStatus = keyof typeof IDV_ERROR_STATUS; //formatIDVError is parsing errors messages from BE (strings) and returns error codes for using it on FE export const formatIDVError = (errors: string[], status_code: string) => { @@ -52,26 +52,22 @@ export const formatIDVError = (errors: string[], status_code: string) => { */ if (errors.length === 0 && (status_code === STATUS_CODES.NONE || status_code === STATUS_CODES.VERIFIED)) return null; - const error_keys: Record = { - name: 'POI_NAME_MISMATCH', - birth: 'POI_DOB_MISMATCH', - rejected: 'POI_FAILED', - }; if (status_code === STATUS_CODES.EXPIRED) { - return 'POI_EXPIRED'; + return IDV_ERROR_STATUS.Expired.code; } - const status: TIDVErrorStatus[] = []; + const status: Array = []; errors.forEach(error => { - const error_regex = RegExp(/(name|birth|rejected)/i).exec(error); - if (error_regex) { - status.push(error_keys[error_regex[0].toLowerCase()]); + const error_key: TIDVErrorStatus = IDV_ERROR_STATUS[error as keyof typeof IDV_ERROR_STATUS] + .code as TIDVErrorStatus; + if (error_key) { + status.push(error_key); } }); - return status.includes(error_keys.name) && - status.includes(error_keys.birth) && - !status.includes(error_keys.rejected) - ? 'POI_NAME_DOB_MISMATCH' - : status[0] ?? 'POI_FAILED'; + return status.includes(IDV_ERROR_STATUS.NameMismatch.code as TIDVErrorStatus) && + status.includes(IDV_ERROR_STATUS.DobMismatch.code as TIDVErrorStatus) && + !status.includes(IDV_ERROR_STATUS.Failed.code as TIDVErrorStatus) + ? IDV_ERROR_STATUS.NameDOBMismatch.code + : status[0] ?? IDV_ERROR_STATUS.Failed.code; }; export const isVerificationServiceSupported = ( From c830be63f92dad14c65655696bce547cdfabd9b4 Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Sat, 14 Oct 2023 13:50:40 +0400 Subject: [PATCH 3/6] fix: incorporated Onfido error messages --- .../__tests__/icon-message-list.spec.tsx | 82 ++----- .../__tests__/list-item.spec.tsx | 16 ++ .../icon-message-list/icon-message-list.tsx | 29 ++- .../icon-message-list/list-item.tsx | 22 +- .../icon-message-list/maximum-list.tsx | 44 ---- .../__tests__/rejected-reasons.spec.tsx | 20 +- .../rejected-reasons/{index.js => index.ts} | 0 .../src/Helpers/__tests__/utils.spec.tsx | 6 +- packages/account/src/Styles/account.scss | 20 +- .../src/utils/constants/idv-failure-codes.ts | 214 +++++++++++++++++- .../src/utils/helpers/format-response.ts | 7 +- 11 files changed, 312 insertions(+), 148 deletions(-) create mode 100644 packages/account/src/Components/icon-message-list/__tests__/list-item.spec.tsx delete mode 100644 packages/account/src/Components/icon-message-list/maximum-list.tsx rename packages/account/src/Components/poi/status/rejected-reasons/{index.js => index.ts} (100%) diff --git a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx index db5b2a1b2333..465efe7a968a 100644 --- a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx +++ b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx @@ -1,8 +1,10 @@ import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Icon } from '@deriv/components'; -import IconMessageList from '../icon-message-list'; +import { ONFIDO_ERROR_STATUS } from '@deriv/shared'; import { StoreProvider, mockStore } from '@deriv/stores'; +import IconMessageList from '../icon-message-list'; jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -12,16 +14,16 @@ jest.mock('@deriv/components', () => { }; }); describe('', () => { - const messages_list = ['Sample Text1', 'Sample Text2', 'Sample Text3', 'Sample Text4']; + const messages_list = ['DataComparisonDocumentNumbers', 'CompromisedDocument', 'VisualAuthenticityFonts']; - const mock_props = { + const mock_props: React.ComponentProps = { icon: , message: 'Lorem Ipsom', message_list: messages_list, onContinue: jest.fn(), }; - const store_config = mockStore({}); + const store_config = mockStore({ ui: { is_desktop: true } }); const renderComponent = ({ props = mock_props, store = store_config }) => render( @@ -31,80 +33,42 @@ describe('', () => { ); it('should render IconMessageList component', () => { - // render(); renderComponent({}); expect(screen.getByTestId('dt_icon_message_list')).toBeInTheDocument(); }); - // it('should render icon passed to the component', () => { - // // render(); - // const new_props = { - // ...mock_props, - // messages_list: ['Sample Text1'], - // }; - // renderComponent({ props: new_props }); - // expect(screen.getByTestId('mocked_icon')).toBeInTheDocument(); - // }); + it('should render icon passed to the component', () => { + renderComponent({}); + expect(screen.getByText('sampleIcon')).toBeInTheDocument(); + expect(screen.getByText('IcCloseCircle')).toBeInTheDocument(); + }); it('should show message passed to the component', () => { - // render(); renderComponent({}); expect(screen.getByText(/lorem ipsom/i)).toBeInTheDocument(); }); - // it('when the length of message_list is less than 3, it should show messages with icons ', () => { - // render(); - // expect(screen.getByText(/Sample Text1/i)).toBeInTheDocument(); - // expect(screen.getByText(/Sample Text2/i)).toBeInTheDocument(); - // expect(screen.getAllByText(/icclosecircle/i).length).toBe(2); - // }); - - // it('should show first 3 msgs and show_more_btn when the message_list is more than 3', () => { - // render(); - // expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); - // expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); - // expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); - // expect(screen.queryByText(/sample text4/i)).not.toBeInTheDocument(); - // expect( - // screen.getByRole('button', { - // name: /show more/i, - // }) - // ).toBeInTheDocument(); - // expect( - // screen.queryByRole('button', { - // name: /show less/i, - // }) - // ).not.toBeInTheDocument(); - // }); - - // it('should show all messages and show_less_btn when show_more btn is clicked', () => { - // render(); - - // const show_more_btn = screen.getByRole('button', { - // name: /show more/i, - // }); - - // fireEvent.click(show_more_btn); - // expect(screen.getByText(/sample text1/i)).toBeInTheDocument(); - // expect(screen.getByText(/sample text2/i)).toBeInTheDocument(); - // expect(screen.getByText(/sample text3/i)).toBeInTheDocument(); - // expect(screen.getByText(/sample text4/i)).toBeInTheDocument(); - // expect(screen.queryByRole('button', { name: /show less/i })).toBeInTheDocument(); - // expect(screen.queryByRole('button', { name: /show more/i })).not.toBeInTheDocument(); - // }); + it('should render the messages based on Onfido Error codes', () => { + const new_props = { + ...mock_props, + message_list: ['DataComparisonDocumentNumbers', 'CompromisedDocument'], + }; + renderComponent({ props: new_props }); + expect(screen.getByText(ONFIDO_ERROR_STATUS.DataComparisonDocumentNumbers.message)).toBeInTheDocument(); + expect(screen.getByText(ONFIDO_ERROR_STATUS.CompromisedDocument.message)).toBeInTheDocument(); + }); it('should show continue_btn if OnContinue is passed', () => { const onContinuefn = jest.fn(); const new_props = { ...mock_props, - messages_list: ['Sample Text1'], + messages_list: ['DataComparisonDocumentNumbers'], onContinue: onContinuefn, }; renderComponent({ props: new_props }); - // render(); const upload_btn = screen.queryByRole('button', { name: /verify again/i }); expect(upload_btn).toBeInTheDocument(); - fireEvent.click(upload_btn); + userEvent.click(upload_btn); expect(onContinuefn).toHaveBeenCalled(); }); }); diff --git a/packages/account/src/Components/icon-message-list/__tests__/list-item.spec.tsx b/packages/account/src/Components/icon-message-list/__tests__/list-item.spec.tsx new file mode 100644 index 000000000000..bfa8b691de04 --- /dev/null +++ b/packages/account/src/Components/icon-message-list/__tests__/list-item.spec.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ListItem from '../list-item'; + +describe('', () => { + it('should render ListItem component', () => { + render(); + expect(screen.getByText('test')).toBeInTheDocument(); + }); + + it('should render ListItem component with index', () => { + render(); + expect(screen.getByText('1.')).toBeInTheDocument(); + expect(screen.getByText('test')).toBeInTheDocument(); + }); +}); diff --git a/packages/account/src/Components/icon-message-list/icon-message-list.tsx b/packages/account/src/Components/icon-message-list/icon-message-list.tsx index 810eb83c4813..038d58845c25 100644 --- a/packages/account/src/Components/icon-message-list/icon-message-list.tsx +++ b/packages/account/src/Components/icon-message-list/icon-message-list.tsx @@ -1,23 +1,23 @@ import React from 'react'; import classNames from 'classnames'; import { Localize } from '@deriv/translations'; -import { Div100vhContainer, Text, Button, ThemedScrollbars } from '@deriv/components'; +import { Div100vhContainer, Text, Button, ThemedScrollbars, Icon } from '@deriv/components'; import ListItem from './list-item'; -import MaximumList from './maximum-list'; import { TMessage_list } from '../../Types'; import { observer, useStore } from '@deriv/stores'; +import { formatOnfidoError } from '@deriv/shared'; type TIconMessageList = TMessage_list & { className?: string; icon: React.ReactElement; message: string; + message_list: Array; onContinue: () => void; }; const IconMessageList = observer(({ className, icon, message, message_list = [], onContinue }: TIconMessageList) => { const { ui } = useStore(); const { is_mobile, is_desktop } = ui; - const has_maximum_list = message_list.length > 3; return ( - {has_maximum_list ? ( - - ) : ( - message_list.map((text, idx) => ) - )} +
+
+ +
+
+ {message_list.length < 2 ? ( + + ) : ( + message_list.map((text, idx) => ( + + )) + )} +
+
)} {onContinue && ( diff --git a/packages/account/src/Components/icon-message-list/list-item.tsx b/packages/account/src/Components/icon-message-list/list-item.tsx index 26c08c857641..9dfcca7ee1cb 100644 --- a/packages/account/src/Components/icon-message-list/list-item.tsx +++ b/packages/account/src/Components/icon-message-list/list-item.tsx @@ -1,20 +1,22 @@ import React from 'react'; -import { Icon, Text } from '@deriv/components'; +import { Text } from '@deriv/components'; +import { Localize } from '@deriv/translations'; type TListItem = { - text?: string; + text: string; + index?: number; }; -const ListItem = ({ text }: TListItem) => ( -
-
- -
-
+const ListItem = ({ text, index }: TListItem) => ( +
+ {index && ( - {text} + {' '} -
+ )} + + +
); diff --git a/packages/account/src/Components/icon-message-list/maximum-list.tsx b/packages/account/src/Components/icon-message-list/maximum-list.tsx deleted file mode 100644 index 907e6c243e20..000000000000 --- a/packages/account/src/Components/icon-message-list/maximum-list.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { Button } from '@deriv/components'; -import { Localize } from '@deriv/translations'; -import ListItem from './list-item'; -import { TMessage_list } from '../../Types'; - -const MaximumList = ({ message_list }: TMessage_list) => { - const [show_more, setShowMore] = React.useState(false); - const maximum_list = message_list.slice(0, 3); - - return show_more ? ( - - {message_list.map(text => ( - - ))} - - - ) : ( - - {maximum_list.map(text => ( - - ))} - - - ); -}; - -export default MaximumList; diff --git a/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx b/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx index 00eba299dfe8..309766e723f6 100644 --- a/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx +++ b/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx @@ -1,22 +1,28 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; +import { ONFIDO_ERROR_STATUS } from '@deriv/shared'; +import { StoreProvider, mockStore } from '@deriv/stores'; import { RejectedReasons } from '../rejected-reasons'; describe('', () => { it('should render RejectedReasons with rejected reasons and trigger continue ', () => { const mockHandleRequireSubmission = jest.fn(); - const rejected_reasons = ['Reason 1', 'Reason 2']; + const rejected_reasons = ['DataValidationExpiryDate', 'DataValidationDocumentExpiration']; + + const store_config = mockStore({}); render( - + + + ); expect(screen.getByTestId('dt_icon_message_list')).toBeInTheDocument(); - expect(screen.getByText('Reason 1')).toBeInTheDocument(); - expect(screen.getByText('Reason 2')).toBeInTheDocument(); + expect(screen.getByText(ONFIDO_ERROR_STATUS.DataValidationExpiryDate.message)).toBeInTheDocument(); + expect(screen.getByText(ONFIDO_ERROR_STATUS.DataValidationDocumentExpiration.message)).toBeInTheDocument(); const btn = screen.getByRole('button'); fireEvent.click(btn); diff --git a/packages/account/src/Components/poi/status/rejected-reasons/index.js b/packages/account/src/Components/poi/status/rejected-reasons/index.ts similarity index 100% rename from packages/account/src/Components/poi/status/rejected-reasons/index.js rename to packages/account/src/Components/poi/status/rejected-reasons/index.ts diff --git a/packages/account/src/Helpers/__tests__/utils.spec.tsx b/packages/account/src/Helpers/__tests__/utils.spec.tsx index 1dac595729c6..ed7d9f2b35f4 100644 --- a/packages/account/src/Helpers/__tests__/utils.spec.tsx +++ b/packages/account/src/Helpers/__tests__/utils.spec.tsx @@ -251,14 +251,14 @@ describe('getOnfidoSupportedLocaleCode', () => { describe('verifyFields', () => { it('should return date field in the list when the error is date of birth', () => { - expect(verifyFields('POI_DOB_MISMATCH')).toEqual(['date_of_birth']); + expect(verifyFields('DobMismatch')).toEqual(['date_of_birth']); }); it('should return first and last name in the list when the error is name', () => { - expect(verifyFields('POI_NAME_MISMATCH')).toEqual(['first_name', 'last_name']); + expect(verifyFields('NameMismatch')).toEqual(['first_name', 'last_name']); }); it('should return first name, last name and dob in the list when the the error is regarding rejection', () => { - expect(verifyFields('POI_FAILED')).toEqual(['first_name', 'last_name', 'date_of_birth']); + expect(verifyFields('Expired')).toEqual(['first_name', 'last_name', 'date_of_birth']); }); }); diff --git a/packages/account/src/Styles/account.scss b/packages/account/src/Styles/account.scss index 4f8d3898978b..cc3cad8807cf 100644 --- a/packages/account/src/Styles/account.scss +++ b/packages/account/src/Styles/account.scss @@ -200,7 +200,6 @@ $MIN_HEIGHT_FLOATING: calc( overflow: hidden; } - &-form { overflow: hidden; height: 100%; @@ -622,6 +621,11 @@ $MIN_HEIGHT_FLOATING: calc( max-width: 50.6rem; display: flex; flex-direction: column; + border-radius: 0.8rem; + background: $color-status-error; + padding: 0.8rem; + align-items: flex-start; + gap: 0.8rem; } &-icon { @@ -660,6 +664,10 @@ $MIN_HEIGHT_FLOATING: calc( align-items: center; flex-direction: column; + @include mobile { + justify-content: center; + } + .dc-btn { height: 4rem; margin-top: 3.2rem; @@ -675,15 +683,9 @@ $MIN_HEIGHT_FLOATING: calc( &-icon { width: 12.8rem; height: 12.8rem; - margin-top: 8rem; - @include mobile { - margin-top: 4rem; - - /* iPhone SE screen height fixes due to UI space restrictions */ - @media only screen and (max-height: 480px) { - margin-top: 3.2rem; - } + @include desktop { + margin-top: 8rem; } } diff --git a/packages/shared/src/utils/constants/idv-failure-codes.ts b/packages/shared/src/utils/constants/idv-failure-codes.ts index aa4116504698..c34a1406c921 100644 --- a/packages/shared/src/utils/constants/idv-failure-codes.ts +++ b/packages/shared/src/utils/constants/idv-failure-codes.ts @@ -1,14 +1,14 @@ export const IDV_ERROR_STATUS = Object.freeze({ DobMismatch: { code: 'DobMismatch', - message: "The date of birth retrieved from your document doesn't match your profile.", + message: 'The date of birth retrieved from your document doesn’t match your profile.', }, DocumentRejected: { code: 'DocumentRejected', message: 'Document was rejected by the provider' }, EmptyStatus: { code: 'EmptyStatus', message: 'The verification status was empty, rejected for lack of information.', }, - Expired: { code: 'Expired', message: "The document's validity has been expired." }, + Expired: { code: 'Expired', message: 'The document’s validity has been expired.' }, InformationLack: { code: 'InformationLack', message: 'The verification is passed but the personal info is not available to compare.', @@ -19,7 +19,7 @@ export const IDV_ERROR_STATUS = Object.freeze({ }, NameMismatch: { code: 'NameMismatch', - message: "The name retrieved from your document doesn't match your profile.", + message: 'The name retrieved from your document doesn’t match your profile.', }, RejectedByProvider: { code: 'RejectedByProvider', @@ -27,11 +27,11 @@ export const IDV_ERROR_STATUS = Object.freeze({ }, Underage: { code: 'Underage', - message: "You're under legal age.", + message: 'You’re under legal age.', }, Deceased: { code: 'Deceased', - message: "The document's owner is deceased.", + message: 'The document’s owner is deceased.', }, Failed: { code: 'Failed', @@ -43,14 +43,214 @@ export const IDV_ERROR_STATUS = Object.freeze({ }, }); -export const Onfido_ERROR_STATUS = Object.freeze({ +export const ONFIDO_ERROR_STATUS = Object.freeze({ AgeValidationMinimumAcceptedAge: { code: 'AgeValidationMinimumAcceptedAge', message: - "Your age in the document you provided appears to be below 18 years. We're only allowed to offer our services to clients above 18 years old, so we'll need to close your account. If you have a balance in your account, contact us via live chat and we'll help to withdraw your funds before your account is closed.", + 'Your age in the document you provided appears to be below 18 years. We’re only allowed to offer our services to clients above 18 years old, so we’ll need to close your account. If you have a balance in your account, contact us via live chat and we’ll help to withdraw your funds before your account is closed.', }, CompromisedDocument: { code: 'CompromisedDocument', message: 'Your document failed our verification checks.', }, + DataComparisonDateOfBirth: { + code: 'DataComparisonDateOfBirth', + message: 'The date of birth on your document doesn’t match your profile.', + }, + DataComparisonDateOfExpiry: { + code: 'DataComparisonDateOfExpiry', + message: 'Your document has expired.', + }, + DataComparisonDocumentNumbers: { + code: 'DataComparisonDocumentNumbers', + message: 'Your document appears to be invalid.', + }, + DataComparisonDocumentType: { + code: 'DataComparisonDocumentType', + message: 'Your document appears to be invalid.', + }, + DataComparisonIssuingCountry: { + code: 'DataComparisonIssuingCountry', + message: 'Your document appears to be invalid.', + }, + DataComparisonName: { + code: 'DataComparisonName', + message: 'The name on your document doesn’t match your profile.', + }, + DataValidationDateOfBirth: { + code: 'DataValidationDateOfBirth', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + DataValidationDocumentExpiration: { + code: 'DataValidationDocumentExpiration', + message: 'Your document has expired.', + }, + DataValidationDocumentNumbers: { + code: 'DataValidationDocumentNumbers', + message: 'Some details in your document appear to be invalid, missing, or unclear.', + }, + DataValidationExpiryDate: { + code: 'DataValidationExpiryDate', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + DataValidationMrz: { + code: 'DataValidationMrz', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + DataValidationNoDocumentNumbers: { + code: 'DataValidationNoDocumentNumbers', + message: 'The serial number of your document couldn’t be verified.', + }, + ImageIntegrityColourPicture: { + code: 'ImageIntegrityColourPicture', + message: 'Your document appears to be in black and white. Please upload a colour photo of your document.', + }, + ImageIntegrityConclusiveDocumentQuality: { + code: 'ImageIntegrityConclusiveDocumentQuality', + message: 'Your document appears to be invalid.', + }, + ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures: { + code: 'ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + ImageIntegrityConclusiveDocumentQualityCornerRemoved: { + code: 'ImageIntegrityConclusiveDocumentQualityCornerRemoved', + message: 'Your document appears to be damaged or cropped.', + }, + ImageIntegrityConclusiveDocumentQualityDigitalDocument: { + code: 'ImageIntegrityConclusiveDocumentQualityDigitalDocument', + message: 'Your document appears to be a digital document.', + }, + ImageIntegrityConclusiveDocumentQualityMissingBack: { + code: 'ImageIntegrityConclusiveDocumentQualityMissingBack', + message: + 'The back of your document appears to be missing. Please include both sides of your identity document.', + }, + ImageIntegrityConclusiveDocumentQualityObscuredDataPoints: { + code: 'ImageIntegrityConclusiveDocumentQualityObscuredDataPoints', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures: { + code: 'ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures', + message: 'Some details on your document appear to be invalid, missing, or unclear.', + }, + ImageIntegrityConclusiveDocumentQualityPuncturedDocument: { + code: 'ImageIntegrityConclusiveDocumentQualityPuncturedDocument', + message: 'Your document appears to be damaged or cropped.', + }, + ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay: { + code: 'ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay', + message: 'Your document contains markings or text that should not be on your document.', + }, + ImageIntegrityImageQuality: { + code: 'ImageIntegrityImageQuality', + message: + 'The image quality of your document is too low. Please provide a hi-res photo of your identity document.', + }, + ImageIntegrityImageQualityBlurredPhoto: { + code: 'ImageIntegrityImageQualityBlurredPhoto', + message: + "We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there's enough light where you are and that your entire face is in the frame.", + }, + ImageIntegrityImageQualityCoveredPhoto: { + code: 'ImageIntegrityImageQualityCoveredPhoto', + message: + 'We’re unable to verify the document you provided because some details appear to be missing. Please try again or provide another document.', + }, + ImageIntegrityImageQualityCutOffDocument: { + code: 'ImageIntegrityImageQualityCutOffDocument', + message: + 'We’re unable to verify the document you provided because it appears to be damaged. Please try again or upload another document.', + }, + ImageIntegrityImageQualityDamagedDocument: { + code: 'ImageIntegrityImageQualityDamagedDocument', + message: + 'We’re unable to verify the document you provided because it appears to be damaged. Please try again or upload another document.', + }, + ImageIntegrityImageQualityDarkPhoto: { + code: 'ImageIntegrityImageQualityDarkPhoto', + message: + 'We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there’s enough light where you are and that your entire face is in the frame.', + }, + ImageIntegrityImageQualityGlareOnPhoto: { + code: 'ImageIntegrityImageQualityGlareOnPhoto', + message: + 'We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there’s enough light where you are and that your entire face is in the frame.', + }, + ImageIntegrityImageQualityIncorrectSide: { + code: 'ImageIntegrityImageQualityIncorrectSide', + message: + 'The front of your document appears to be missing. Please provide both sides of your identity document.', + }, + ImageIntegrityImageQualityNoDocumentInImage: { + code: 'ImageIntegrityImageQualityNoDocumentInImage', + message: + 'We’re unable to verify the document you provided because it appears to be a blank image. Please try again or upload another document.', + }, + ImageIntegrityImageQualityOtherPhotoIssue: { + code: 'ImageIntegrityImageQualityOtherPhotoIssue', + message: + 'We’re unable to verify the document you provided because some details appear to be missing. Please try again or provide another document.', + }, + ImageIntegrityImageQualityTwoDocumentsUploaded: { + code: 'ImageIntegrityImageQualityTwoDocumentsUploaded', + message: + 'The document you provided appears to be two different types. Please try again or provide another document.', + }, + ImageIntegritySupportedDocument: { + code: 'ImageIntegritySupportedDocument', + message: + 'The document you provided is not supported for your country. Please provide a supported document for your country.', + }, + SelfieRejected: { + code: 'SelfieRejected', + message: + 'We’re unable to verify the selfie you provided as it does not match the required criteria. Please provide a photo that closely resembles the document photo provided.', + }, + VisualAuthenticityDigitalTampering: { + code: 'VisualAuthenticityDigitalTampering', + message: 'Your document appears to be invalid.', + }, + VisualAuthenticityFaceDetection: { + code: 'VisualAuthenticityFaceDetection', + message: 'Your document appears to be invalid.', + }, + VisualAuthenticityFonts: { + code: 'VisualAuthenticityFonts', + message: 'Your document appears to be invalid.', + }, + VisualAuthenticityOriginalDocumentPresent: { + code: 'VisualAuthenticityOriginalDocumentPresent', + message: + 'Your document appears to be a scanned copy that contains markings or text that shouldn’t be on your document.', + }, + VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper: { + code: 'VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper', + message: 'Your document appears to be a printed copy.', + }, + VisualAuthenticityOriginalDocumentPresentPhotoOfScreen: { + code: 'VisualAuthenticityOriginalDocumentPresentPhotoOfScreen', + message: 'Your document appears to be a photo of a device screen.', + }, + VisualAuthenticityOriginalDocumentPresentScan: { + code: 'VisualAuthenticityOriginalDocumentPresentScan', + message: + 'We’re unable to verify the document you provided because it contains markings or text that should not be on your document. Please provide a clear photo or a scan of your original identity document.', + }, + VisualAuthenticityOriginalDocumentPresentScreenshot: { + code: 'VisualAuthenticityOriginalDocumentPresentScreenshot', + message: 'Your document appears to be a screenshot.', + }, + VisualAuthenticityPictureFaceIntegrity: { + code: 'VisualAuthenticityPictureFaceIntegrity', + message: 'Your document appears to be invalid.', + }, + VisualAuthenticitySecurityFeatures: { + code: 'VisualAuthenticitySecurityFeatures', + message: 'Your document appears to be invalid.', + }, + VisualAuthenticityTemplate: { + code: 'VisualAuthenticityTemplate', + message: 'Your document appears to be invalid.', + }, }); diff --git a/packages/shared/src/utils/helpers/format-response.ts b/packages/shared/src/utils/helpers/format-response.ts index 2ec3af878dd2..0fc2b4d9bd43 100644 --- a/packages/shared/src/utils/helpers/format-response.ts +++ b/packages/shared/src/utils/helpers/format-response.ts @@ -3,7 +3,7 @@ import { STATUS_CODES, getUnsupportedContracts } from '../constants'; import { getSymbolDisplayName, TActiveSymbols } from './active-symbols'; import { getMarketInformation } from './market-underlying'; import { TContractInfo } from '../contract'; -import { IDV_ERROR_STATUS } from '../constants/idv-failure-codes'; +import { IDV_ERROR_STATUS, ONFIDO_ERROR_STATUS } from '../constants/idv-failure-codes'; type TIsUnSupportedContract = { contract_type?: string; @@ -44,6 +44,7 @@ export const formatPortfolioPosition = ( }; export type TIDVErrorStatus = keyof typeof IDV_ERROR_STATUS; +export type TOnfidoErrorStatus = keyof typeof ONFIDO_ERROR_STATUS; //formatIDVError is parsing errors messages from BE (strings) and returns error codes for using it on FE export const formatIDVError = (errors: string[], status_code: string) => { @@ -70,6 +71,10 @@ export const formatIDVError = (errors: string[], status_code: string) => { : status[0] ?? IDV_ERROR_STATUS.Failed.code; }; +export const formatOnfidoError = (error: string) => { + return ONFIDO_ERROR_STATUS[error as keyof typeof ONFIDO_ERROR_STATUS]?.message ?? ''; +}; + export const isVerificationServiceSupported = ( residence_list: ResidenceList, account_settings: GetSettings, From 95268698b354913cb9afd7c1eaf227899d42e3af Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Sat, 14 Oct 2023 14:06:36 +0400 Subject: [PATCH 4/6] chore: removed dead code --- .../src/Components/icon-message-list/icon-message-list.tsx | 7 +++---- packages/account/src/Types/common.type.ts | 4 ---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/account/src/Components/icon-message-list/icon-message-list.tsx b/packages/account/src/Components/icon-message-list/icon-message-list.tsx index 038d58845c25..63ddb137b5b3 100644 --- a/packages/account/src/Components/icon-message-list/icon-message-list.tsx +++ b/packages/account/src/Components/icon-message-list/icon-message-list.tsx @@ -2,12 +2,11 @@ import React from 'react'; import classNames from 'classnames'; import { Localize } from '@deriv/translations'; import { Div100vhContainer, Text, Button, ThemedScrollbars, Icon } from '@deriv/components'; -import ListItem from './list-item'; -import { TMessage_list } from '../../Types'; -import { observer, useStore } from '@deriv/stores'; import { formatOnfidoError } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; +import ListItem from './list-item'; -type TIconMessageList = TMessage_list & { +type TIconMessageList = { className?: string; icon: React.ReactElement; message: string; diff --git a/packages/account/src/Types/common.type.ts b/packages/account/src/Types/common.type.ts index 32012cf1e37a..bed0f1f14f7e 100644 --- a/packages/account/src/Types/common.type.ts +++ b/packages/account/src/Types/common.type.ts @@ -161,7 +161,3 @@ export type TServerError = { details?: { [key: string]: string }; fields?: string[]; }; - -export type TMessage_list = { - message_list: string[]; -}; From c9ace64b40ad4838a000743e7f342b269aef8f48 Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Mon, 16 Oct 2023 12:48:33 +0400 Subject: [PATCH 5/6] fix: incorporated review comments --- .../__tests__/icon-message-list.spec.tsx | 4 +- .../icon-message-list/list-item.tsx | 4 +- .../idv-failed/__tests__/idv-failed.spec.tsx | 4 +- .../poi/idv-status/idv-failed/idv-failed.tsx | 106 +----- .../__tests__/idv-submit-complete.spec.tsx | 2 +- .../idv-submit-complete.tsx | 2 +- .../__tests__/poi-country-selector.spec.tsx | 11 +- .../__tests__/rejected-reasons.spec.tsx | 6 +- .../Configs/__test__/poi-error-config.spec.ts | 18 ++ .../account/src/Configs/poi-error-config.tsx | 102 +++++- packages/account/src/Helpers/utils.tsx | 6 +- packages/account/src/Types/onfido.type.ts | 7 +- .../src/utils/constants/idv-failure-codes.ts | 256 --------------- packages/shared/src/utils/constants/index.ts | 2 +- .../src/utils/constants/poi-failure-codes.tsx | 305 ++++++++++++++++++ .../src/utils/helpers/format-response.ts | 4 +- 16 files changed, 455 insertions(+), 384 deletions(-) create mode 100644 packages/account/src/Configs/__test__/poi-error-config.spec.ts delete mode 100644 packages/shared/src/utils/constants/idv-failure-codes.ts create mode 100644 packages/shared/src/utils/constants/poi-failure-codes.tsx diff --git a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx index 465efe7a968a..1e35ec7187dc 100644 --- a/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx +++ b/packages/account/src/Components/icon-message-list/__tests__/icon-message-list.spec.tsx @@ -54,8 +54,8 @@ describe('', () => { message_list: ['DataComparisonDocumentNumbers', 'CompromisedDocument'], }; renderComponent({ props: new_props }); - expect(screen.getByText(ONFIDO_ERROR_STATUS.DataComparisonDocumentNumbers.message)).toBeInTheDocument(); - expect(screen.getByText(ONFIDO_ERROR_STATUS.CompromisedDocument.message)).toBeInTheDocument(); + expect(screen.getByText('Your document appears to be invalid.')).toBeInTheDocument(); + expect(screen.getByText('Your document failed our verification checks.')).toBeInTheDocument(); }); it('should show continue_btn if OnContinue is passed', () => { diff --git a/packages/account/src/Components/icon-message-list/list-item.tsx b/packages/account/src/Components/icon-message-list/list-item.tsx index 9dfcca7ee1cb..017d533f2cb6 100644 --- a/packages/account/src/Components/icon-message-list/list-item.tsx +++ b/packages/account/src/Components/icon-message-list/list-item.tsx @@ -3,7 +3,7 @@ import { Text } from '@deriv/components'; import { Localize } from '@deriv/translations'; type TListItem = { - text: string; + text: JSX.Element; index?: number; }; @@ -15,7 +15,7 @@ const ListItem = ({ text, index }: TListItem) => ( )} - + {text}
); diff --git a/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx index 6de1c4c5a8c1..aa1b198e5d2c 100644 --- a/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-failed/__tests__/idv-failed.spec.tsx @@ -72,11 +72,11 @@ describe('', () => { }); it('should render IDVfailed component with name & DOB mismatch message', async () => { - const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.NameDOBMismatch.code }; + const new_props = { ...mock_props, mismatch_status: IDV_ERROR_STATUS.NameDobMismatch.code }; renderComponent({ props: new_props }); await waitFor(() => { - expect(screen.getByTestId(IDV_ERROR_STATUS.NameDOBMismatch.code)).toBeInTheDocument(); + expect(screen.getByTestId(IDV_ERROR_STATUS.NameDobMismatch.code)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Update profile/i })).toBeInTheDocument(); }); }); diff --git a/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx b/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx index 185df167a653..4291be57dc39 100644 --- a/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-failed/idv-failed.tsx @@ -14,9 +14,8 @@ import { toMoment, WS, } from '@deriv/shared'; +import { useStore } from '@deriv/stores'; import { Localize, localize } from '@deriv/translations'; -import PoiNameExample from '../../../../Assets/ic-poi-name-example.svg'; -import PoiDobExample from '../../../../Assets/ic-poi-dob-example.svg'; import PoiNameDobExample from '../../../../Assets/ic-poi-name-dob-example.svg'; import FormBody from '../../../form-body'; import IDVForm from '../../../forms/idv-form'; @@ -24,7 +23,6 @@ import FormFooter from '../../../form-footer'; import FormSubHeader from '../../../form-sub-header'; import PersonalDetailsForm from '../../../forms/personal-details-form.jsx'; import { - getIDVDocumentType, isAdditionalDocumentValid, isDocumentNumberValid, isDocumentTypeValid, @@ -37,11 +35,11 @@ import { GENERIC_ERROR_MESSAGE, DUPLICATE_ACCOUNT_ERROR_MESSAGE, CLAIMED_DOCUMENT_ERROR_MESSAGE, + generateIDVError, } from '../../../../Configs/poi-error-config'; import { API_ERROR_CODES } from '../../../../Constants/api-error-codes'; import { TIDVFormValues, TPersonalDetailsForm } from '../../../../Types'; import LoadErrorMessage from '../../../load-error-message'; -import { useStore } from '@deriv/stores'; type TRestState = { api_error: string; @@ -117,99 +115,6 @@ const IdvFailed = ({ const IDV_NOT_APPLICABLE_OPTION = React.useMemo(() => getIDVNotApplicableOption(), []); - const renderErrorMessage = (mismatch_status: TIDVErrorStatus) => { - switch (mismatch_status) { - case IDV_ERROR_STATUS.Expired.code: - return 'Your identity document has expired.'; - case IDV_ERROR_STATUS.Failed.code: - return 'We were unable to verify the identity document with the details provided.'; - default: - return IDV_ERROR_STATUS[mismatch_status].message; - } - }; - - const generateIDVError = React.useCallback(() => { - const document_name = is_document_upload_required - ? 'identity document' - : getIDVDocumentType(latest_status, chosen_country); - switch (mismatch_status) { - case IDV_ERROR_STATUS.NameDOBMismatch.code: - return { - required_fields: ['first_name', 'last_name', 'date_of_birth'], - side_note_image: , - inline_note_text: ( - ]} - values={{ document_name }} - /> - ), - failure_message: ( - ]} - /> - ), - }; - case IDV_ERROR_STATUS.NameMismatch.code: - return { - required_fields: ['first_name', 'last_name'], - side_note_image: , - inline_note_text: ( - ]} - values={{ document_name }} - /> - ), - failure_message: ( - ]} - /> - ), - }; - case IDV_ERROR_STATUS.DobMismatch.code: - return { - required_fields: ['date_of_birth'], - side_note_image: , - inline_note_text: ( - ]} - values={{ document_name }} - /> - ), - failure_message: ( - ]} - /> - ), - }; - default: - return { - required_fields: ['first_name', 'last_name', 'date_of_birth'], - side_note_image: , - inline_note_text: ( - ]} - values={{ document_name }} - /> - ), - failure_message: ( - - ), - }; - } - }, [is_document_upload_required, latest_status, chosen_country, mismatch_status]); - React.useEffect(() => { const initializeFormValues = async (required_fields: string[]) => { await WS.wait('get_settings'); @@ -241,7 +146,12 @@ const IdvFailed = ({ setIsAlreadyAttempted(true); - const error_config = generateIDVError(); + const error_config = generateIDVError( + is_document_upload_required, + latest_status, + chosen_country, + mismatch_status + ); setIdvFailure(error_config); initializeFormValues(error_config?.required_fields ?? []).catch(e => { setRestState({ diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx index 3d264fb1a749..a47569a48320 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx @@ -91,7 +91,7 @@ describe('', () => { const new_props: TIdvSubmitCompleteProps = { ...mock_props, - mismatch_status: IDV_ERROR_STATUS.NameDOBMismatch.code, + mismatch_status: IDV_ERROR_STATUS.NameDobMismatch.code, }; renderComponent({ props: new_props, store_config: new_store }); diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx index 90cde349804c..216fa1d7be5e 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx @@ -20,7 +20,7 @@ const IdvSubmitComplete = observer( const { is_already_attempted } = client; const is_mismatch_error = - mismatch_status === IDV_ERROR_STATUS.NameDOBMismatch.code || + mismatch_status === IDV_ERROR_STATUS.NameDobMismatch.code || mismatch_status === IDV_ERROR_STATUS.DobMismatch.code || mismatch_status === IDV_ERROR_STATUS.NameMismatch.code; diff --git a/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx b/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx index ea461f0926d2..51fda536ea8e 100644 --- a/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx +++ b/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx @@ -27,20 +27,12 @@ describe('', () => { let mock_props: React.ComponentProps = { handleSelectionNext: jest.fn(), is_from_external: false, - // residence_list: [{ value: '', text: '' }], - // selected_country: '', }; beforeEach(() => { mock_props = { handleSelectionNext: jest.fn(), is_from_external: false, - // residence_list: [ - // { value: 'Country 1', text: 'Country 1' }, - // { value: 'Country 2', text: 'Country 2' }, - // { value: 'Country 3', text: 'Country 3' }, - // ], - // selected_country: '', }; }); @@ -98,7 +90,7 @@ describe('', () => { }); it('should render error status and country selector when error is verification failed or expired', () => { - mock_props.mismatch_status = 'POI_FAILED'; + mock_props.mismatch_status = 'Failed'; renderComponent({ props: mock_props }); @@ -114,7 +106,6 @@ describe('', () => { it('should trigger selection functions and next button', async () => { (isDesktop as jest.Mock).mockReturnValue(false); (isMobile as jest.Mock).mockReturnValue(true); - // mock_props.selected_country = 'Country 2'; const new_poi_context_config = { ...poi_context_config, diff --git a/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx b/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx index 309766e723f6..0e3f30334f7a 100644 --- a/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx +++ b/packages/account/src/Components/poi/status/rejected-reasons/__tests__/rejected-reasons.spec.tsx @@ -21,8 +21,10 @@ describe('', () => { ); expect(screen.getByTestId('dt_icon_message_list')).toBeInTheDocument(); - expect(screen.getByText(ONFIDO_ERROR_STATUS.DataValidationExpiryDate.message)).toBeInTheDocument(); - expect(screen.getByText(ONFIDO_ERROR_STATUS.DataValidationDocumentExpiration.message)).toBeInTheDocument(); + expect( + screen.getByText('Some details on your document appear to be invalid, missing, or unclear.') + ).toBeInTheDocument(); + expect(screen.getByText('Your document has expired.')).toBeInTheDocument(); const btn = screen.getByRole('button'); fireEvent.click(btn); diff --git a/packages/account/src/Configs/__test__/poi-error-config.spec.ts b/packages/account/src/Configs/__test__/poi-error-config.spec.ts new file mode 100644 index 000000000000..e3a0e36c1acb --- /dev/null +++ b/packages/account/src/Configs/__test__/poi-error-config.spec.ts @@ -0,0 +1,18 @@ +import { generateIDVError } from '../poi-error-config'; + +describe('generateIDVError', () => { + it('should return Name & DOB mismatch error config', () => { + const error = generateIDVError(false, {}, { value: 'Country 1', text: 'Country 1' }, 'NameDobMismatch'); + expect(error.required_fields).toEqual(['first_name', 'last_name', 'date_of_birth']); + }); + + it('should return Name mismatch error config', () => { + const error = generateIDVError(false, {}, { value: 'Country 1', text: 'Country 1' }, 'NameMismatch'); + expect(error.required_fields).toEqual(['first_name', 'last_name']); + }); + + it('should return DOB mismatch error config', () => { + const error = generateIDVError(false, {}, { value: 'Country 1', text: 'Country 1' }, 'DobMismatch'); + expect(error.required_fields).toEqual(['date_of_birth']); + }); +}); diff --git a/packages/account/src/Configs/poi-error-config.tsx b/packages/account/src/Configs/poi-error-config.tsx index 9dc5be193344..1e1fd5d97503 100644 --- a/packages/account/src/Configs/poi-error-config.tsx +++ b/packages/account/src/Configs/poi-error-config.tsx @@ -1,5 +1,11 @@ -import { Localize } from '@deriv/translations'; import React from 'react'; +import { GetAccountStatus, ResidenceList } from '@deriv/api-types'; +import { IDV_ERROR_STATUS, TIDVErrorStatus } from '@deriv/shared'; +import { Localize } from '@deriv/translations'; +import PoiNameDobExample from '../Assets/poi-name-dob-example.svg'; +import PoiNameExample from '../Assets/poi-name-example.svg'; +import PoiDobExample from '../Assets/poi-dob-example.svg'; +import { getIDVDocumentType } from 'Helpers/utils'; export const GENERIC_ERROR_MESSAGE = ( @@ -18,3 +24,97 @@ export const CLAIMED_DOCUMENT_ERROR_MESSAGE = ( components={[ window.LC_API.open_chat_window()} />]} /> ); + +const renderErrorMessage = (mismatch_status: TIDVErrorStatus) => { + switch (mismatch_status) { + case IDV_ERROR_STATUS.Expired.code: + return ; + case IDV_ERROR_STATUS.Failed.code: + return ( + + ); + default: + return IDV_ERROR_STATUS[mismatch_status]?.message; + } +}; + +export const generateIDVError = ( + is_document_upload_required: boolean, + latest_status: DeepRequired['authentication']['attempts']['latest'], + chosen_country: DeepRequired, + mismatch_status: keyof typeof IDV_ERROR_STATUS +) => { + const document_name = is_document_upload_required + ? 'identity document' + : getIDVDocumentType(latest_status, chosen_country); + + switch (mismatch_status) { + case IDV_ERROR_STATUS.NameDobMismatch.code: + return { + required_fields: ['first_name', 'last_name', 'date_of_birth'], + side_note_image: , + inline_note_text: ( + ]} + values={{ document_name }} + /> + ), + failure_message: ( + ]} + /> + ), + }; + case IDV_ERROR_STATUS.NameMismatch.code: + return { + required_fields: ['first_name', 'last_name'], + side_note_image: , + inline_note_text: ( + ]} + values={{ document_name }} + /> + ), + failure_message: ( + ]} + /> + ), + }; + case IDV_ERROR_STATUS.DobMismatch.code: + return { + required_fields: ['date_of_birth'], + side_note_image: , + inline_note_text: ( + ]} + values={{ document_name }} + /> + ), + failure_message: ( + ]} + /> + ), + }; + default: + return { + required_fields: ['first_name', 'last_name', 'date_of_birth'], + side_note_image: , + inline_note_text: ( + ]} + values={{ document_name }} + /> + ), + failure_message: renderErrorMessage(mismatch_status), + }; + } +}; diff --git a/packages/account/src/Helpers/utils.tsx b/packages/account/src/Helpers/utils.tsx index da32c456eed1..cb8fd234ad34 100644 --- a/packages/account/src/Helpers/utils.tsx +++ b/packages/account/src/Helpers/utils.tsx @@ -1,6 +1,7 @@ import React from 'react'; +import { FormikValues } from 'formik'; import countries from 'i18n-iso-countries'; -import { Localize, localize } from '@deriv/translations'; +import { ResidenceList, GetAccountStatus } from '@deriv/api-types'; import { filterObjProperties, toMoment, @@ -9,8 +10,7 @@ import { getIDVNotApplicableOption, IDV_ERROR_STATUS, } from '@deriv/shared'; -import { ResidenceList, GetAccountStatus } from '@deriv/api-types'; -import { FormikValues } from 'formik'; +import { Localize, localize } from '@deriv/translations'; import { getIDVDocuments } from '../Constants/idv-document-config'; import { TServerError } from '../Types'; import { LANGUAGE_CODES } from '../Constants/onfido'; diff --git a/packages/account/src/Types/onfido.type.ts b/packages/account/src/Types/onfido.type.ts index 8211b549d9b3..010e9475546c 100644 --- a/packages/account/src/Types/onfido.type.ts +++ b/packages/account/src/Types/onfido.type.ts @@ -1,5 +1,6 @@ -// Generated by dts-bundle-generator v5.5.0 - +/** + * Copied the types from onfido-sdk npm lib to reduce the bundle size. Onfido functionality is being used via CDN + */ export declare type SupportedLanguages = | 'bg' | 'da' @@ -167,7 +168,7 @@ export declare type StepOptionDocument = { disableCrossDevice?: boolean; photoCaptureFallback?: never; } & CaptureOptions; -export declare type StepOptionPoA = {}; +export declare type StepOptionPoA = Record; export declare type StepOptionFace = { forceCrossDevice?: never; disableCrossDevice?: boolean; diff --git a/packages/shared/src/utils/constants/idv-failure-codes.ts b/packages/shared/src/utils/constants/idv-failure-codes.ts deleted file mode 100644 index c34a1406c921..000000000000 --- a/packages/shared/src/utils/constants/idv-failure-codes.ts +++ /dev/null @@ -1,256 +0,0 @@ -export const IDV_ERROR_STATUS = Object.freeze({ - DobMismatch: { - code: 'DobMismatch', - message: 'The date of birth retrieved from your document doesn’t match your profile.', - }, - DocumentRejected: { code: 'DocumentRejected', message: 'Document was rejected by the provider' }, - EmptyStatus: { - code: 'EmptyStatus', - message: 'The verification status was empty, rejected for lack of information.', - }, - Expired: { code: 'Expired', message: 'The document’s validity has been expired.' }, - InformationLack: { - code: 'InformationLack', - message: 'The verification is passed but the personal info is not available to compare.', - }, - MalformedJson: { - code: 'MalformedJson', - message: 'The verification status is not available, provider says: Malformed JSON.', - }, - NameMismatch: { - code: 'NameMismatch', - message: 'The name retrieved from your document doesn’t match your profile.', - }, - RejectedByProvider: { - code: 'RejectedByProvider', - message: 'The document was rejected by the Provider.', - }, - Underage: { - code: 'Underage', - message: 'You’re under legal age.', - }, - Deceased: { - code: 'Deceased', - message: 'The document’s owner is deceased.', - }, - Failed: { - code: 'Failed', - message: 'We were unable to verify the identity document with the details provided.', - }, - NameDOBMismatch: { - code: 'NameDOBMismatch', - message: '', - }, -}); - -export const ONFIDO_ERROR_STATUS = Object.freeze({ - AgeValidationMinimumAcceptedAge: { - code: 'AgeValidationMinimumAcceptedAge', - message: - 'Your age in the document you provided appears to be below 18 years. We’re only allowed to offer our services to clients above 18 years old, so we’ll need to close your account. If you have a balance in your account, contact us via live chat and we’ll help to withdraw your funds before your account is closed.', - }, - CompromisedDocument: { - code: 'CompromisedDocument', - message: 'Your document failed our verification checks.', - }, - DataComparisonDateOfBirth: { - code: 'DataComparisonDateOfBirth', - message: 'The date of birth on your document doesn’t match your profile.', - }, - DataComparisonDateOfExpiry: { - code: 'DataComparisonDateOfExpiry', - message: 'Your document has expired.', - }, - DataComparisonDocumentNumbers: { - code: 'DataComparisonDocumentNumbers', - message: 'Your document appears to be invalid.', - }, - DataComparisonDocumentType: { - code: 'DataComparisonDocumentType', - message: 'Your document appears to be invalid.', - }, - DataComparisonIssuingCountry: { - code: 'DataComparisonIssuingCountry', - message: 'Your document appears to be invalid.', - }, - DataComparisonName: { - code: 'DataComparisonName', - message: 'The name on your document doesn’t match your profile.', - }, - DataValidationDateOfBirth: { - code: 'DataValidationDateOfBirth', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - DataValidationDocumentExpiration: { - code: 'DataValidationDocumentExpiration', - message: 'Your document has expired.', - }, - DataValidationDocumentNumbers: { - code: 'DataValidationDocumentNumbers', - message: 'Some details in your document appear to be invalid, missing, or unclear.', - }, - DataValidationExpiryDate: { - code: 'DataValidationExpiryDate', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - DataValidationMrz: { - code: 'DataValidationMrz', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - DataValidationNoDocumentNumbers: { - code: 'DataValidationNoDocumentNumbers', - message: 'The serial number of your document couldn’t be verified.', - }, - ImageIntegrityColourPicture: { - code: 'ImageIntegrityColourPicture', - message: 'Your document appears to be in black and white. Please upload a colour photo of your document.', - }, - ImageIntegrityConclusiveDocumentQuality: { - code: 'ImageIntegrityConclusiveDocumentQuality', - message: 'Your document appears to be invalid.', - }, - ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures: { - code: 'ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - ImageIntegrityConclusiveDocumentQualityCornerRemoved: { - code: 'ImageIntegrityConclusiveDocumentQualityCornerRemoved', - message: 'Your document appears to be damaged or cropped.', - }, - ImageIntegrityConclusiveDocumentQualityDigitalDocument: { - code: 'ImageIntegrityConclusiveDocumentQualityDigitalDocument', - message: 'Your document appears to be a digital document.', - }, - ImageIntegrityConclusiveDocumentQualityMissingBack: { - code: 'ImageIntegrityConclusiveDocumentQualityMissingBack', - message: - 'The back of your document appears to be missing. Please include both sides of your identity document.', - }, - ImageIntegrityConclusiveDocumentQualityObscuredDataPoints: { - code: 'ImageIntegrityConclusiveDocumentQualityObscuredDataPoints', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures: { - code: 'ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures', - message: 'Some details on your document appear to be invalid, missing, or unclear.', - }, - ImageIntegrityConclusiveDocumentQualityPuncturedDocument: { - code: 'ImageIntegrityConclusiveDocumentQualityPuncturedDocument', - message: 'Your document appears to be damaged or cropped.', - }, - ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay: { - code: 'ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay', - message: 'Your document contains markings or text that should not be on your document.', - }, - ImageIntegrityImageQuality: { - code: 'ImageIntegrityImageQuality', - message: - 'The image quality of your document is too low. Please provide a hi-res photo of your identity document.', - }, - ImageIntegrityImageQualityBlurredPhoto: { - code: 'ImageIntegrityImageQualityBlurredPhoto', - message: - "We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there's enough light where you are and that your entire face is in the frame.", - }, - ImageIntegrityImageQualityCoveredPhoto: { - code: 'ImageIntegrityImageQualityCoveredPhoto', - message: - 'We’re unable to verify the document you provided because some details appear to be missing. Please try again or provide another document.', - }, - ImageIntegrityImageQualityCutOffDocument: { - code: 'ImageIntegrityImageQualityCutOffDocument', - message: - 'We’re unable to verify the document you provided because it appears to be damaged. Please try again or upload another document.', - }, - ImageIntegrityImageQualityDamagedDocument: { - code: 'ImageIntegrityImageQualityDamagedDocument', - message: - 'We’re unable to verify the document you provided because it appears to be damaged. Please try again or upload another document.', - }, - ImageIntegrityImageQualityDarkPhoto: { - code: 'ImageIntegrityImageQualityDarkPhoto', - message: - 'We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there’s enough light where you are and that your entire face is in the frame.', - }, - ImageIntegrityImageQualityGlareOnPhoto: { - code: 'ImageIntegrityImageQualityGlareOnPhoto', - message: - 'We were unable to verify your selfie because it’s not clear. Please take a clearer photo and try again. Ensure that there’s enough light where you are and that your entire face is in the frame.', - }, - ImageIntegrityImageQualityIncorrectSide: { - code: 'ImageIntegrityImageQualityIncorrectSide', - message: - 'The front of your document appears to be missing. Please provide both sides of your identity document.', - }, - ImageIntegrityImageQualityNoDocumentInImage: { - code: 'ImageIntegrityImageQualityNoDocumentInImage', - message: - 'We’re unable to verify the document you provided because it appears to be a blank image. Please try again or upload another document.', - }, - ImageIntegrityImageQualityOtherPhotoIssue: { - code: 'ImageIntegrityImageQualityOtherPhotoIssue', - message: - 'We’re unable to verify the document you provided because some details appear to be missing. Please try again or provide another document.', - }, - ImageIntegrityImageQualityTwoDocumentsUploaded: { - code: 'ImageIntegrityImageQualityTwoDocumentsUploaded', - message: - 'The document you provided appears to be two different types. Please try again or provide another document.', - }, - ImageIntegritySupportedDocument: { - code: 'ImageIntegritySupportedDocument', - message: - 'The document you provided is not supported for your country. Please provide a supported document for your country.', - }, - SelfieRejected: { - code: 'SelfieRejected', - message: - 'We’re unable to verify the selfie you provided as it does not match the required criteria. Please provide a photo that closely resembles the document photo provided.', - }, - VisualAuthenticityDigitalTampering: { - code: 'VisualAuthenticityDigitalTampering', - message: 'Your document appears to be invalid.', - }, - VisualAuthenticityFaceDetection: { - code: 'VisualAuthenticityFaceDetection', - message: 'Your document appears to be invalid.', - }, - VisualAuthenticityFonts: { - code: 'VisualAuthenticityFonts', - message: 'Your document appears to be invalid.', - }, - VisualAuthenticityOriginalDocumentPresent: { - code: 'VisualAuthenticityOriginalDocumentPresent', - message: - 'Your document appears to be a scanned copy that contains markings or text that shouldn’t be on your document.', - }, - VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper: { - code: 'VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper', - message: 'Your document appears to be a printed copy.', - }, - VisualAuthenticityOriginalDocumentPresentPhotoOfScreen: { - code: 'VisualAuthenticityOriginalDocumentPresentPhotoOfScreen', - message: 'Your document appears to be a photo of a device screen.', - }, - VisualAuthenticityOriginalDocumentPresentScan: { - code: 'VisualAuthenticityOriginalDocumentPresentScan', - message: - 'We’re unable to verify the document you provided because it contains markings or text that should not be on your document. Please provide a clear photo or a scan of your original identity document.', - }, - VisualAuthenticityOriginalDocumentPresentScreenshot: { - code: 'VisualAuthenticityOriginalDocumentPresentScreenshot', - message: 'Your document appears to be a screenshot.', - }, - VisualAuthenticityPictureFaceIntegrity: { - code: 'VisualAuthenticityPictureFaceIntegrity', - message: 'Your document appears to be invalid.', - }, - VisualAuthenticitySecurityFeatures: { - code: 'VisualAuthenticitySecurityFeatures', - message: 'Your document appears to be invalid.', - }, - VisualAuthenticityTemplate: { - code: 'VisualAuthenticityTemplate', - message: 'Your document appears to be invalid.', - }, -}); diff --git a/packages/shared/src/utils/constants/index.ts b/packages/shared/src/utils/constants/index.ts index 101af38867a5..df71fa57cc2b 100644 --- a/packages/shared/src/utils/constants/index.ts +++ b/packages/shared/src/utils/constants/index.ts @@ -5,4 +5,4 @@ export * from './default-options'; export * from './jurisdictions-config'; export * from './signup_fields'; export * from './error'; -export * from './idv-failure-codes'; +export * from './poi-failure-codes'; diff --git a/packages/shared/src/utils/constants/poi-failure-codes.tsx b/packages/shared/src/utils/constants/poi-failure-codes.tsx new file mode 100644 index 000000000000..385085570fea --- /dev/null +++ b/packages/shared/src/utils/constants/poi-failure-codes.tsx @@ -0,0 +1,305 @@ +import React from 'react'; +import { Localize } from '@deriv/translations'; + +export const IDV_ERROR_STATUS = Object.freeze({ + DobMismatch: { + code: 'DobMismatch', + message: ( + + ), + }, + DocumentRejected: { + code: 'DocumentRejected', + message: , + }, + EmptyStatus: { + code: 'EmptyStatus', + message: , + }, + Expired: { code: 'Expired', message: }, + InformationLack: { + code: 'InformationLack', + message: ( + + ), + }, + MalformedJson: { + code: 'MalformedJson', + message: ( + + ), + }, + NameMismatch: { + code: 'NameMismatch', + message: , + }, + RejectedByProvider: { + code: 'RejectedByProvider', + message: , + }, + Underage: { + code: 'Underage', + message: , + }, + Deceased: { + code: 'Deceased', + message: , + }, + Failed: { + code: 'Failed', + message: ( + + ), + }, + NameDobMismatch: { + code: 'NameDobMismatch', + message: '', + }, +}); + +export const ONFIDO_ERROR_STATUS = Object.freeze({ + AgeValidationMinimumAcceptedAge: { + code: 'AgeValidationMinimumAcceptedAge', + message: ( + + ), + }, + CompromisedDocument: { + code: 'CompromisedDocument', + message: , + }, + DataComparisonDateOfBirth: { + code: 'DataComparisonDateOfBirth', + message: , + }, + DataComparisonDateOfExpiry: { + code: 'DataComparisonDateOfExpiry', + message: , + }, + DataComparisonDocumentNumbers: { + code: 'DataComparisonDocumentNumbers', + message: , + }, + DataComparisonDocumentType: { + code: 'DataComparisonDocumentType', + message: , + }, + DataComparisonIssuingCountry: { + code: 'DataComparisonIssuingCountry', + message: , + }, + DataComparisonName: { + code: 'DataComparisonName', + message: , + }, + DataValidationDateOfBirth: { + code: 'DataValidationDateOfBirth', + message: ( + + ), + }, + DataValidationDocumentExpiration: { + code: 'DataValidationDocumentExpiration', + message: , + }, + DataValidationDocumentNumbers: { + code: 'DataValidationDocumentNumbers', + message: ( + + ), + }, + DataValidationExpiryDate: { + code: 'DataValidationExpiryDate', + message: ( + + ), + }, + DataValidationMrz: { + code: 'DataValidationMrz', + message: ( + + ), + }, + DataValidationNoDocumentNumbers: { + code: 'DataValidationNoDocumentNumbers', + message: , + }, + ImageIntegrityColourPicture: { + code: 'ImageIntegrityColourPicture', + message: ( + + ), + }, + ImageIntegrityConclusiveDocumentQuality: { + code: 'ImageIntegrityConclusiveDocumentQuality', + message: , + }, + ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures: { + code: 'ImageIntegrityConclusiveDocumentQualityAbnormalDocumentFeatures', + message: ( + + ), + }, + ImageIntegrityConclusiveDocumentQualityCornerRemoved: { + code: 'ImageIntegrityConclusiveDocumentQualityCornerRemoved', + message: , + }, + ImageIntegrityConclusiveDocumentQualityDigitalDocument: { + code: 'ImageIntegrityConclusiveDocumentQualityDigitalDocument', + message: , + }, + ImageIntegrityConclusiveDocumentQualityMissingBack: { + code: 'ImageIntegrityConclusiveDocumentQualityMissingBack', + message: ( + + ), + }, + ImageIntegrityConclusiveDocumentQualityObscuredDataPoints: { + code: 'ImageIntegrityConclusiveDocumentQualityObscuredDataPoints', + message: ( + + ), + }, + ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures: { + code: 'ImageIntegrityConclusiveDocumentQualityObscuredSecurityFeatures', + message: ( + + ), + }, + ImageIntegrityConclusiveDocumentQualityPuncturedDocument: { + code: 'ImageIntegrityConclusiveDocumentQualityPuncturedDocument', + message: , + }, + ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay: { + code: 'ImageIntegrityConclusiveDocumentQualityWatermarksDigitalTextOverlay', + message: ( + + ), + }, + ImageIntegrityImageQuality: { + code: 'ImageIntegrityImageQuality', + message: ( + + ), + }, + ImageIntegrityImageQualityBlurredPhoto: { + code: 'ImageIntegrityImageQualityBlurredPhoto', + message: ( + + ), + }, + ImageIntegrityImageQualityCoveredPhoto: { + code: 'ImageIntegrityImageQualityCoveredPhoto', + message: ( + + ), + }, + ImageIntegrityImageQualityCutOffDocument: { + code: 'ImageIntegrityImageQualityCutOffDocument', + message: ( + + ), + }, + ImageIntegrityImageQualityDamagedDocument: { + code: 'ImageIntegrityImageQualityDamagedDocument', + message: ( + + ), + }, + ImageIntegrityImageQualityDarkPhoto: { + code: 'ImageIntegrityImageQualityDarkPhoto', + message: ( + + ), + }, + ImageIntegrityImageQualityGlareOnPhoto: { + code: 'ImageIntegrityImageQualityGlareOnPhoto', + message: ( + + ), + }, + ImageIntegrityImageQualityIncorrectSide: { + code: 'ImageIntegrityImageQualityIncorrectSide', + message: ( + + ), + }, + ImageIntegrityImageQualityNoDocumentInImage: { + code: 'ImageIntegrityImageQualityNoDocumentInImage', + message: ( + + ), + }, + ImageIntegrityImageQualityOtherPhotoIssue: { + code: 'ImageIntegrityImageQualityOtherPhotoIssue', + message: ( + + ), + }, + ImageIntegrityImageQualityTwoDocumentsUploaded: { + code: 'ImageIntegrityImageQualityTwoDocumentsUploaded', + message: ( + + ), + }, + ImageIntegritySupportedDocument: { + code: 'ImageIntegritySupportedDocument', + message: ( + + ), + }, + SelfieRejected: { + code: 'SelfieRejected', + message: ( + + ), + }, + VisualAuthenticityDigitalTampering: { + code: 'VisualAuthenticityDigitalTampering', + message: , + }, + VisualAuthenticityFaceDetection: { + code: 'VisualAuthenticityFaceDetection', + message: , + }, + VisualAuthenticityFonts: { + code: 'VisualAuthenticityFonts', + message: , + }, + VisualAuthenticityOriginalDocumentPresent: { + code: 'VisualAuthenticityOriginalDocumentPresent', + message: ( + + ), + }, + VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper: { + code: 'VisualAuthenticityOriginalDocumentPresentDocumentOnPrintedPaper', + message: , + }, + VisualAuthenticityOriginalDocumentPresentPhotoOfScreen: { + code: 'VisualAuthenticityOriginalDocumentPresentPhotoOfScreen', + message: , + }, + VisualAuthenticityOriginalDocumentPresentScan: { + code: 'VisualAuthenticityOriginalDocumentPresentScan', + message: ( + + ), + }, + VisualAuthenticityOriginalDocumentPresentScreenshot: { + code: 'VisualAuthenticityOriginalDocumentPresentScreenshot', + message: , + }, + VisualAuthenticityPictureFaceIntegrity: { + code: 'VisualAuthenticityPictureFaceIntegrity', + message: , + }, + VisualAuthenticitySecurityFeatures: { + code: 'VisualAuthenticitySecurityFeatures', + message: , + }, + VisualAuthenticityTemplate: { + code: 'VisualAuthenticityTemplate', + message: , + }, +}); diff --git a/packages/shared/src/utils/helpers/format-response.ts b/packages/shared/src/utils/helpers/format-response.ts index 3791d6d2810d..6138ea4ab5f8 100644 --- a/packages/shared/src/utils/helpers/format-response.ts +++ b/packages/shared/src/utils/helpers/format-response.ts @@ -4,7 +4,7 @@ import { getContractTypeFeatureFlag, getUnsupportedContracts, STATUS_CODES } fro import { getSymbolDisplayName, TActiveSymbols } from './active-symbols'; import { getMarketInformation } from './market-underlying'; import { TContractInfo } from '../contract'; -import { IDV_ERROR_STATUS, ONFIDO_ERROR_STATUS } from '../constants/idv-failure-codes'; +import { IDV_ERROR_STATUS, ONFIDO_ERROR_STATUS } from '../constants/poi-failure-codes'; import { LocalStore } from '../storage'; import { extractInfoFromShortcode, isHighLow } from '../shortcode'; @@ -83,7 +83,7 @@ export const formatIDVError = (errors: string[], status_code: string) => { return status.includes(IDV_ERROR_STATUS.NameMismatch.code as TIDVErrorStatus) && status.includes(IDV_ERROR_STATUS.DobMismatch.code as TIDVErrorStatus) && !status.includes(IDV_ERROR_STATUS.Failed.code as TIDVErrorStatus) - ? IDV_ERROR_STATUS.NameDOBMismatch.code + ? IDV_ERROR_STATUS.NameDobMismatch.code : status[0] ?? IDV_ERROR_STATUS.Failed.code; }; From e9bd0bf2051a9f2032a6bc90a39124d6c977c21c Mon Sep 17 00:00:00 2001 From: Likhith Kolayari Date: Mon, 16 Oct 2023 13:05:50 +0400 Subject: [PATCH 6/6] fix: incorporated review comments --- .../src/utils/constants/poi-failure-codes.tsx | 18 ++++++++++++++++-- .../src/utils/helpers/format-response.ts | 15 +++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/shared/src/utils/constants/poi-failure-codes.tsx b/packages/shared/src/utils/constants/poi-failure-codes.tsx index 385085570fea..c4ea427ce7e8 100644 --- a/packages/shared/src/utils/constants/poi-failure-codes.tsx +++ b/packages/shared/src/utils/constants/poi-failure-codes.tsx @@ -1,7 +1,21 @@ import React from 'react'; import { Localize } from '@deriv/translations'; -export const IDV_ERROR_STATUS = Object.freeze({ +type TIDVErrorStatus = Readonly<{ + [key: string]: { + code: keyof typeof IDV_ERROR_STATUS; + message: React.ReactNode; + }; +}>; + +type TOnfidoErrorStatus = Readonly<{ + [key: string]: { + code: keyof typeof ONFIDO_ERROR_STATUS; + message: React.ReactNode; + }; +}>; + +export const IDV_ERROR_STATUS: TIDVErrorStatus = Object.freeze({ DobMismatch: { code: 'DobMismatch', message: ( @@ -57,7 +71,7 @@ export const IDV_ERROR_STATUS = Object.freeze({ }, }); -export const ONFIDO_ERROR_STATUS = Object.freeze({ +export const ONFIDO_ERROR_STATUS: TOnfidoErrorStatus = Object.freeze({ AgeValidationMinimumAcceptedAge: { code: 'AgeValidationMinimumAcceptedAge', message: ( diff --git a/packages/shared/src/utils/helpers/format-response.ts b/packages/shared/src/utils/helpers/format-response.ts index 6138ea4ab5f8..0981325711b4 100644 --- a/packages/shared/src/utils/helpers/format-response.ts +++ b/packages/shared/src/utils/helpers/format-response.ts @@ -63,7 +63,7 @@ export type TIDVErrorStatus = keyof typeof IDV_ERROR_STATUS; export type TOnfidoErrorStatus = keyof typeof ONFIDO_ERROR_STATUS; //formatIDVError is parsing errors messages from BE (strings) and returns error codes for using it on FE -export const formatIDVError = (errors: string[], status_code: string) => { +export const formatIDVError = (errors: Array, status_code: string) => { /** * Check required incase of DIEL client */ @@ -74,21 +74,20 @@ export const formatIDVError = (errors: string[], status_code: string) => { } const status: Array = []; errors.forEach(error => { - const error_key: TIDVErrorStatus = IDV_ERROR_STATUS[error as keyof typeof IDV_ERROR_STATUS] - .code as TIDVErrorStatus; + const error_key: TIDVErrorStatus = IDV_ERROR_STATUS[error].code; if (error_key) { status.push(error_key); } }); - return status.includes(IDV_ERROR_STATUS.NameMismatch.code as TIDVErrorStatus) && - status.includes(IDV_ERROR_STATUS.DobMismatch.code as TIDVErrorStatus) && - !status.includes(IDV_ERROR_STATUS.Failed.code as TIDVErrorStatus) + return status.includes(IDV_ERROR_STATUS.NameMismatch.code) && + status.includes(IDV_ERROR_STATUS.DobMismatch.code) && + !status.includes(IDV_ERROR_STATUS.Failed.code) ? IDV_ERROR_STATUS.NameDobMismatch.code : status[0] ?? IDV_ERROR_STATUS.Failed.code; }; -export const formatOnfidoError = (error: string) => { - return ONFIDO_ERROR_STATUS[error as keyof typeof ONFIDO_ERROR_STATUS]?.message ?? ''; +export const formatOnfidoError = (error: TOnfidoErrorStatus) => { + return ONFIDO_ERROR_STATUS[error]?.message ?? ''; }; export const isVerificationServiceSupported = (