diff --git a/packages/account/build/webpack.config.js b/packages/account/build/webpack.config.js index 8edb664ab742..4120d1c5e58c 100644 --- a/packages/account/build/webpack.config.js +++ b/packages/account/build/webpack.config.js @@ -49,7 +49,6 @@ module.exports = function (env) { 'proof-of-identity': 'Sections/Verification/ProofOfIdentity/proof-of-identity.jsx', 'proof-of-identity-container': 'Sections/Verification/ProofOfIdentity/proof-of-identity-container.jsx', 'proof-of-identity-config': 'Configs/proof-of-identity-config', - 'proof-of-identity-form-on-signup': 'Components/poi/poi-form-on-signup', 'proof-of-identity-container-for-mt5': 'Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5', 'poi-poa-docs-submitted': 'Components/poi-poa-docs-submitted/poi-poa-docs-submitted', diff --git a/packages/account/jest.config.js b/packages/account/jest.config.js index 1bb16f90d1ff..11898a71490e 100644 --- a/packages/account/jest.config.js +++ b/packages/account/jest.config.js @@ -9,6 +9,7 @@ module.exports = { '^Components/(.*)$': '/src/Components/$1', '^Constants/(.*)$': '/src/Constants/$1', '^Configs/(.*)$': '/src/Configs/$1', + '^Containers/(.*)$': '/src/Containers/$1', '^Duplicated/(.*)$': '/src/Duplicated/$1', '^Helpers/(.*)$': '/src/Helpers/$1', '^Services/(.*)$': '/src/Services/$1', diff --git a/packages/account/src/Assets/ic-poi-name-dob-example.svg b/packages/account/src/Assets/ic-poi-name-dob-example.svg new file mode 100644 index 000000000000..90aef9941c2d --- /dev/null +++ b/packages/account/src/Assets/ic-poi-name-dob-example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/account/src/Components/address-details/address-details.tsx b/packages/account/src/Components/address-details/address-details.tsx index 804437058b69..708781a99b07 100644 --- a/packages/account/src/Components/address-details/address-details.tsx +++ b/packages/account/src/Components/address-details/address-details.tsx @@ -24,6 +24,7 @@ import { TLocationList, } from '@deriv/shared'; import { splitValidationResultTypes } from '../real-account-signup/helpers/utils'; +import classNames from 'classnames'; type TAddressDetails = { states_list: TLocationList[]; @@ -199,7 +200,7 @@ const AddressDetails = ({ )} -
+
( + + {({ field: { value }, form: { setFieldValue, errors, touched, setFieldTouched } }) => ( + setFieldTouched(name)} + onChange={({ target }) => + setFieldValue(name, target?.value ? toMoment(target.value).format('YYYY-MM-DD') : '', true) + } + value={value} + portal_id={portal_id} + {...rest} + /> + )} + +); + +export const FormInputField = ({ name, optional = false, warn, ...props }) => ( + + {({ field, form: { errors, touched } }) => ( + + )} + +); diff --git a/packages/account/src/Components/forms/idv-form.tsx b/packages/account/src/Components/forms/idv-form.tsx new file mode 100644 index 000000000000..48a5a5fd0c1d --- /dev/null +++ b/packages/account/src/Components/forms/idv-form.tsx @@ -0,0 +1,308 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Field, FormikProps, FormikHandlers, FieldProps } from 'formik'; +import { ResidenceList } from '@deriv/api-types'; +import { localize } from '@deriv/translations'; +import { formatInput, IDV_NOT_APPLICABLE_OPTION } from '@deriv/shared'; +import { Autocomplete, DesktopWrapper, Input, MobileWrapper, SelectNative, Text } from '@deriv/components'; +import { getDocumentData, preventEmptyClipboardPaste, generatePlaceholderText, getExampleFormat } from 'Helpers/utils'; + +type TDocumentList = Array<{ + id: string; + text: string; + value?: string; + sample_image?: string; + example_format?: string; + additional?: any; +}>; + +type TFormProps = { + document_type: TDocumentList[0]; + document_number: string; + document_additional?: string; + error_message?: string; +}; + +type TIDVForm = { + selected_country: ResidenceList[0]; + hide_hint?: boolean; + class_name?: string; + can_skip_document_verification: boolean; +} & Partial & + FormikProps; + +const IDVForm = ({ + errors, + touched, + values, + handleBlur, + handleChange, + setFieldValue, + class_name, + selected_country, + hide_hint, + can_skip_document_verification = false, +}: TIDVForm) => { + const [document_list, setDocumentList] = React.useState([]); + const [document_image, setDocumentImage] = React.useState(null); + const [selected_doc, setSelectedDoc] = React.useState(''); + + const { documents_supported: document_data, has_visual_sample } = selected_country?.identity?.services?.idv ?? {}; + + const default_document = { + id: '', + text: '', + value: '', + example_format: '', + sample_image: '', + }; + + React.useEffect(() => { + if (document_data && selected_country && selected_country.value) { + const document_types = Object.keys(document_data); + if (document_types.length === 0) return; + const filtered_documents = ['gh', 'ng'].includes(selected_country.value) + ? document_types.filter(d => d !== 'voter_id') + : document_types; + + const new_document_list = filtered_documents.map(key => { + const { display_name, format } = document_data[key]; + const { new_display_name, example_format, sample_image } = getDocumentData( + selected_country.value ?? '', + key + ); + const needs_additional_document = !!document_data[key].additional; + + if (needs_additional_document) { + return { + id: key, + text: new_display_name || display_name, + additional: { + display_name: document_data[key].additional?.display_name, + format: document_data[key].additional?.format, + }, + value: format, + sample_image, + example_format, + }; + } + return { + id: key, + text: new_display_name || display_name, + value: format, + sample_image, + example_format, + }; + }); + if (can_skip_document_verification) { + setDocumentList([...new_document_list, IDV_NOT_APPLICABLE_OPTION]); + } else { + setDocumentList([...new_document_list]); + } + } + }, [document_data, selected_country, can_skip_document_verification]); + + const resetDocumentItemSelected = () => { + setFieldValue('document_type', default_document, true); + }; + + const getDocument = (text: string) => { + return document_list.find(d => d.text === text) ?? default_document; + }; + + const onKeyUp = (e: { target: HTMLInputElement }, document_name: string) => { + const { example_format } = + document_name === 'document_number' ? values.document_type : values.document_type.additional; + let current_input: string | null = null; + current_input = example_format.includes('-') + ? formatInput(example_format, current_input ?? e.target.value, '-') + : e.target.value; + setFieldValue(document_name, current_input, true); + }; + + const bindDocumentData = (item: TDocumentList[0]) => { + setFieldValue('document_type', item, true); + setSelectedDoc(item?.id); + if (item?.id === IDV_NOT_APPLICABLE_OPTION.id) { + setFieldValue('document_number', '', true); + setFieldValue('document_additional', '', true); + } + if (has_visual_sample) { + setDocumentImage(item.sample_image ?? ''); + } + }; + + return ( + +
+
+
+
+
+
+
+ + {({ field }: FieldProps) => ( + + +
+ { + handleBlur(e); + if (!getDocument(e.target.value)) { + resetDocumentItemSelected(); + } + }} + onChange={handleChange} + onItemSelection={(item: TDocumentList[0]) => { + if ( + item.text === 'No results found' || + !item.text + ) { + setSelectedDoc(''); + resetDocumentItemSelected(); + } else { + bindDocumentData(item); + } + }} + required + /> +
+
+ + { + handleChange(e); + const selected_document = getDocument(e.target.value); + bindDocumentData(selected_document); + }} + use_text={true} + required + /> + +
+ )} +
+
+
+ + {({ field }: FieldProps) => ( + + + onKeyUp(e, 'document_number') + } + required + label={generatePlaceholderText(selected_doc)} + /> + {values.document_type.additional?.display_name && ( + + onKeyUp(e, 'document_additional') + } + required + /> + )} + + )} + +
+
+ {document_image && ( +
+ + {localize('Sample:')} + +
+ document sample image +
+
+ )} +
+
+
+
+
+
+ ); +}; + +export default IDVForm; diff --git a/packages/account/src/Components/forms/personal-details-form.jsx b/packages/account/src/Components/forms/personal-details-form.jsx new file mode 100644 index 000000000000..a3f5aa7f0c83 --- /dev/null +++ b/packages/account/src/Components/forms/personal-details-form.jsx @@ -0,0 +1,588 @@ +import React from 'react'; +import { Field } from 'formik'; +import classNames from 'classnames'; +import { + Autocomplete, + Checkbox, + Dropdown, + DesktopWrapper, + MobileWrapper, + Popover, + RadioGroup, + SelectNative, + Text, +} from '@deriv/components'; +import { getLegalEntityName, isDesktop, isMobile, routes, validPhone } from '@deriv/shared'; +import { Localize, localize } from '@deriv/translations'; +import FormSubHeader from 'Components/form-sub-header'; +import PoiNameDobExample from 'Assets/ic-poi-name-dob-example.svg'; +import InlineNoteWithIcon from 'Components/inline-note-with-icon'; +import FormBodySection from 'Components/form-body-section'; +import { DateOfBirthField, FormInputField } from 'Components/forms/form-fields'; +import { Link } from 'react-router-dom'; +import { getEmploymentStatusList } from 'Sections/Assessment/FinancialAssessment/financial-information-list'; +import { isFieldImmutable } from 'Helpers/utils'; + +const PersonalDetailsForm = ({ + errors, + touched, + values, + setFieldValue, + handleChange, + handleBlur, + warning_items, + setFieldTouched, + ...props +}) => { + const { + is_virtual, + is_mf, + is_svg, + is_qualified_for_idv, + should_hide_helper_image, + is_appstore, + editable_fields = [], + has_real_account, + residence_list, + is_fully_authenticated, + account_opening_reason_list, + closeRealAccountSignup, + salutation_list, + is_rendered_for_onfido, + should_close_tooltip, + setShouldCloseTooltip, + } = props; + const autocomplete_value = 'none'; + const PoiNameDobExampleIcon = PoiNameDobExample; + + const [is_tax_residence_popover_open, setIsTaxResidencePopoverOpen] = React.useState(false); + const [is_tin_popover_open, setIsTinPopoverOpen] = React.useState(false); + + React.useEffect(() => { + if (should_close_tooltip) { + handleToolTipStatus(); + setShouldCloseTooltip(false); + } + }, [should_close_tooltip, handleToolTipStatus, setShouldCloseTooltip]); + + const getNameAndDobLabels = () => { + const is_asterisk_needed = is_svg || is_mf || is_rendered_for_onfido || is_qualified_for_idv; + const first_name_label = is_appstore || is_asterisk_needed ? localize('First name*') : localize('First name'); + const last_name_label = is_appstore + ? localize('Family name*') + : is_asterisk_needed + ? localize('Last name*') + : localize('Last name'); + const dob_label = is_appstore || is_asterisk_needed ? localize('Date of birth*') : localize('Date of birth'); + + return { + first_name_label, + last_name_label, + dob_label, + }; + }; + + const getFieldHint = field_name => { + return ( + + ); + }; + + const handleToolTipStatus = React.useCallback(() => { + if (is_tax_residence_popover_open) { + setIsTaxResidencePopoverOpen(false); + } + if (is_tin_popover_open) { + setIsTinPopoverOpen(false); + } + }, [is_tax_residence_popover_open, is_tin_popover_open]); + + const name_dob_clarification_message = ( + ]} + /> + ); + + return ( +
+ {(is_qualified_for_idv || is_rendered_for_onfido) && !should_hide_helper_image && ( + + )} + } + > +
+ {'salutation' in values && ( +
+ + {is_virtual ? ( + localize( + 'Please remember that it is your responsibility to keep your answers accurate and up to date. You can update your personal details at any time in your account settings.' + ) + ) : ( + , + ]} + /> + )} + +
+ )} + {!is_qualified_for_idv && !is_appstore && !is_rendered_for_onfido && ( + + )} + {'salutation' in values && ( + { + e.persist(); + setFieldValue('salutation', e.target.value); + }} + required + > + {salutation_list.map(item => ( + + ))} + + )} + {'first_name' in values && ( + + )} + {'last_name' in values && ( + + )} + {!is_appstore && !is_qualified_for_idv && !is_rendered_for_onfido && ( + + )} + {'date_of_birth' in values && ( + + )} + {'place_of_birth' in values && ( + + {({ field }) => ( + + + + setFieldValue('place_of_birth', value ? text : '', true) + } + required + data-testid='place_of_birth' + /> + + + { + handleChange(e); + setFieldValue('place_of_birth', e.target.value, true); + }} + {...field} + list_portal_id='modal_root' + required + should_hide_disabled_options={false} + data_testid='place_of_birth_mobile' + /> + + + )} + + )} + {'citizen' in values && ( + + {({ field }) => ( + + + + setFieldValue('citizen', value ? text : '', true) + } + list_portal_id='modal_root' + required + data-testid='citizenship' + /> + + + { + handleChange(e); + setFieldValue('citizen', e.target.value, true); + }} + {...field} + required + should_hide_disabled_options={false} + data_testid='citizenship_mobile' + /> + + + )} + + )} + {'phone' in values && ( + = 9 && + values?.phone?.length <= 35) + } + maxLength={50} + data-testid='phone' + /> + )} + {('tax_residence' in values || 'tax_identification_number' in values) && ( + + + {'tax_residence' in values && ( + + {({ field }) => ( +
+ + + setFieldValue('tax_residence', value ? text : '', true) + } + list_portal_id='modal_root' + data-testid='tax_residence' + disabled={isFieldImmutable('tax_residence', editable_fields)} + /> + + + { + handleChange(e); + setFieldValue('tax_residence', e.target.value, true); + }} + {...field} + required + data_testid='tax_residence_mobile' + disabled={isFieldImmutable('tax_residence', editable_fields)} + /> + +
{ + setIsTaxResidencePopoverOpen(true); + setIsTinPopoverOpen(false); + e.stopPropagation(); + }} + > + +
+
+ )} +
+ )} + {'tax_identification_number' in values && ( +
+ +
{ + setIsTaxResidencePopoverOpen(false); + setIsTinPopoverOpen(true); + e.stopPropagation(); + }} + > + here to learn more." + } + components={[ + , + ]} + /> + } + zIndex={9998} + disable_message_icon + /> +
+
+ )} + {warning_items?.tax_identification_number && ( +
+ )} + {'employment_status' in values && ( +
+ + + + + { + setFieldTouched('employment_status', true); + handleChange(e); + }} + disabled={isFieldImmutable('employment_status', editable_fields)} + /> + +
+ )} + {'tax_identification_confirm' in values && ( + + setFieldValue( + 'tax_identification_confirm', + !values.tax_identification_confirm, + true + ) + } + value={values.tax_identification_confirm} + label={localize( + 'I hereby confirm that the tax information I provided is true and complete. I will also inform {{legal_entity_name}} about any changes to this information.', + { + legal_entity_name: getLegalEntityName('maltainvest'), + } + )} + renderlabel={title => ( + + {title} + + )} + withTabIndex={0} + data-testid='tax_identification_confirm' + /> + )} + + )} + {'account_opening_reason' in values && ( + + + + {({ field }) => ( + + + + + + { + handleChange(e); + setFieldValue('account_opening_reason', e.target.value, true); + }} + {...field} + required + data_testid='account_opening_reason_mobile' + disabled={isFieldImmutable('account_opening_reason', editable_fields)} + /> + + + )} + + + )} +
+
+
+ ); +}; + +export default PersonalDetailsForm; diff --git a/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.scss b/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.scss index c26d6de102c7..ee710362f6aa 100644 --- a/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.scss +++ b/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.scss @@ -2,7 +2,7 @@ margin-top: 0.8rem; padding: 0.8rem; display: flex; - background: rgba(255, 173, 58, 0.16); + background: var(--status-alert-background); border-radius: 4px; gap: 0.8rem; line-height: 1.4rem; diff --git a/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.tsx b/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.tsx index 0d4664147d64..5e772cd3d46c 100644 --- a/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.tsx +++ b/packages/account/src/Components/inline-note-with-icon/inline-note-with-icon.tsx @@ -2,19 +2,22 @@ import React from 'react'; import { Icon, Text } from '@deriv/components'; type TInlineNoteWithIconExtend = { - icon: string; + icon?: string; + font_size?: string; message: React.ReactNode; - title: string; + title?: string; }; -const InlineNoteWithIcon = ({ icon, message, title }: TInlineNoteWithIconExtend) => { +const InlineNoteWithIcon = ({ icon, message, font_size = 'xxxs', title }: TInlineNoteWithIconExtend) => { return (
-
- -
- - {title}: + {icon && ( +
+ +
+ )} + + {title && {title}: } {message}
diff --git a/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js b/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js index 7e25fccd313e..210729fc956b 100644 --- a/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js +++ b/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js @@ -6,6 +6,8 @@ import { isDesktop, isMobile, PlatformContext } from '@deriv/shared'; import { splitValidationResultTypes } from '../../real-account-signup/helpers/utils'; import PersonalDetails from '../personal-details'; +jest.mock('Assets/ic-poi-name-dob-example.svg', () => jest.fn(() => 'PoiNameDobExampleImage')); + jest.mock('@deriv/components', () => ({ ...jest.requireActual('@deriv/components'), Popover: jest.fn(props => props.is_open && {props.message}), @@ -229,6 +231,7 @@ describe('', () => { getCurrentStep: jest.fn(() => 1), onSave: jest.fn(), onCancel: jest.fn(), + account_settings: {}, }; beforeAll(() => (ReactDOM.createPortal = jest.fn(component => component))); @@ -253,6 +256,7 @@ describe('', () => { expect(screen.getByText(fake_alert_messaget)).toBeInTheDocument(); }); + it('should not show fake_alert_message when is_appstore is false ', () => { renderwithRouter( @@ -298,6 +302,7 @@ describe('', () => { }) ).toBeInTheDocument(); }); + it('should show Name label when salutation is not passed', () => { const newprops = { ...props, value: {} }; renderwithRouter(); diff --git a/packages/account/src/Components/personal-details/personal-details.jsx b/packages/account/src/Components/personal-details/personal-details.jsx index 3a13c4b9e5dc..dfd5045bee50 100644 --- a/packages/account/src/Components/personal-details/personal-details.jsx +++ b/packages/account/src/Components/personal-details/personal-details.jsx @@ -1,74 +1,33 @@ -import { Formik, Field } from 'formik'; import React from 'react'; +import classNames from 'classnames'; +import { Formik, Form } from 'formik'; import { Modal, - Autocomplete, AutoHeightWrapper, - Checkbox, - Dropdown, - DesktopWrapper, - MobileWrapper, - DateOfBirthPicker, Div100vhContainer, FormSubmitButton, - Input, - Popover, - RadioGroup, - SelectNative, ThemedScrollbars, Text, } from '@deriv/components'; -import { Link } from 'react-router-dom'; +import { + isDesktop, + isMobile, + PlatformContext, + IDV_NOT_APPLICABLE_OPTION, + removeEmptyPropertiesFromObject, +} from '@deriv/shared'; import { localize, Localize } from '@deriv/translations'; -import { getLegalEntityName, isDesktop, isMobile, routes, toMoment, PlatformContext, validPhone } from '@deriv/shared'; -import { getEmploymentStatusList } from 'Sections/Assessment/FinancialAssessment/financial-information-list'; +import { + shouldShowIdentityInformation, + isDocumentTypeValid, + isAdditionalDocumentValid, + isDocumentNumberValid, + shouldHideHelperImage, +} from 'Helpers/utils'; import { splitValidationResultTypes } from '../real-account-signup/helpers/utils'; +import IDVForm from '../forms/idv-form'; +import PersonalDetailsForm from '../forms/personal-details-form'; import FormSubHeader from '../form-sub-header'; -import classNames from 'classnames'; - -const DateOfBirthField = props => ( - - {({ field: { value }, form: { setFieldValue, errors, touched, setTouched } }) => ( - - setTouched({ - ...touched, - date_of_birth: true, - }) - } - onChange={({ target }) => - setFieldValue( - 'date_of_birth', - target?.value ? toMoment(target.value).format('YYYY-MM-DD') : '', - true - ) - } - value={value} - portal_id={props.portal_id} - {...props} - /> - )} - -); - -const FormInputField = ({ name, optional = false, warn, ...props }) => ( - - {({ field, form: { errors, touched } }) => ( - - )} - -); const PersonalDetails = ({ getCurrentStep, @@ -92,9 +51,9 @@ const PersonalDetails = ({ has_real_account, ...props }) => { + const { account_status, account_settings, residence, real_account_signup_target } = props; const { is_appstore } = React.useContext(PlatformContext); - const [is_tax_residence_popover_open, setIsTaxResidencePopoverOpen] = React.useState(false); - const [is_tin_popover_open, setIsTinPopoverOpen] = React.useState(false); + const [should_close_tooltip, setShouldCloseTooltip] = React.useState(false); const [warning_items, setWarningItems] = React.useState({}); const is_submit_disabled_ref = React.useRef(true); @@ -117,50 +76,55 @@ const PersonalDetails = ({ onCancel(current_step, goToPreviousStep); }; - const handleValidate = values => { - const { errors, warnings } = splitValidationResultTypes(validate(values)); - setWarningItems(warnings); - checkSubmitStatus(errors); - return errors; - }; + const is_qualified_for_idv = shouldShowIdentityInformation({ + account_status, + account_settings, + residence, + residence_list, + real_account_signup_target, + }); - const closeTooltipOnScroll = () => { - // Close any open tooltip - if (!is_tax_residence_popover_open || !is_tin_popover_open) { - setIsTaxResidencePopoverOpen(false); - setIsTinPopoverOpen(false); - } - }; + const validateIDV = values => { + const errors = {}; + const { document_type, document_number, document_additional } = values; + if (document_type.id === IDV_NOT_APPLICABLE_OPTION.id) return errors; - const handleClickOutside = () => { - if (is_tax_residence_popover_open) { - setIsTaxResidencePopoverOpen(false); - } - if (is_tin_popover_open) { - setIsTinPopoverOpen(false); + errors.document_type = isDocumentTypeValid(document_type); + + const needs_additional_document = !!document_type.additional; + + if (needs_additional_document) { + errors.document_additional = isAdditionalDocumentValid(document_type, document_additional); } - }; - const getLastNameLabel = () => { - if (is_appstore) return localize('Family name*'); - return is_svg || is_mf ? localize('Last name*') : localize('Last name'); + errors.document_number = isDocumentNumberValid(document_number, document_type); + return removeEmptyPropertiesFromObject(errors); }; - const getFieldHint = field_name => { - return ( - - ); + const handleValidate = values => { + let idv_error = {}; + if (is_qualified_for_idv) { + idv_error = validateIDV(values); + } + const { errors, warnings } = splitValidationResultTypes(validate(values)); + const error_data = { ...idv_error, ...errors }; + setWarningItems(warnings); + checkSubmitStatus(error_data); + return error_data; }; + const closeToolTip = () => setShouldCloseTooltip(true); + /* In most modern browsers, setting autocomplete to "off" will not prevent a password manager from asking the user if they would like to save username and password information, or from automatically filling in those values in a site's login form. check this link https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion#the_autocomplete_attribute_and_login_fields */ // for dropdowns use 'none' - const autocomplete_value = 'none'; + + const citizen = account_settings?.citizen || residence; + const selected_country = residence_list.find(residence_data => residence_data.value === citizen) || {}; + + const editable_fields = Object.keys(props.value).filter(field => !disabled_items.includes(field)) || []; return ( ( {({ setRef, height }) => ( -
- - - - + + {!is_qualified_for_idv && ( + + + + )} - {is_appstore && ( + {!is_qualified_for_idv && is_appstore && (
{localize( @@ -206,564 +172,63 @@ const PersonalDetails = ({ )}
- {'salutation' in props.value && ( -
- - {is_virtual ? ( - localize( - 'Please remember that it is your responsibility to keep your answers accurate and up to date. You can update your personal details at any time in your account settings.' - ) - ) : ( - , - ]} - /> - )} - -
- )} - {!is_appstore && ( - - )} - {'salutation' in props.value && ( // TODO: [deriv-eu] Remove salutation once api is optional - { - e.persist(); - setFieldValue('salutation', e.target.value); - }} - required - > - {salutation_list.map(item => ( - - ))} - - )} - {'first_name' in props.value && ( - - )} - {'last_name' in props.value && ( - - )} - {!is_appstore && } - {'date_of_birth' in props.value && ( - - )} - {'place_of_birth' in props.value && ( - - {({ field }) => ( - - - - setFieldValue( - 'place_of_birth', - value ? text : '', - true - ) - } - required - data-testid='place_of_birth' - /> - - - { - handleChange(e); - setFieldValue( - 'place_of_birth', - e.target.value, - true - ); - }} - {...field} - list_portal_id='modal_root' - required - should_hide_disabled_options={false} - data_testid='place_of_birth_mobile' - /> - - - )} - - )} - {'citizen' in props.value && ( - - {({ field }) => ( - - - - setFieldValue('citizen', value ? text : '', true) - } - list_portal_id='modal_root' - required - data-testid='citizenship' - /> - - - { - handleChange(e); - setFieldValue('citizen', e.target.value, true); - }} - {...field} - required - should_hide_disabled_options={false} - data_testid='citizenship_mobile' - /> - - - )} - - )} - {'phone' in props.value && ( - = 9 && - props.value?.phone?.length <= 35) - } - maxLength={50} - data-testid='phone' - /> - )} - {('tax_residence' in props.value || - 'tax_identification_number' in props.value) && ( + {is_qualified_for_idv && ( - - {'tax_residence' in props.value && ( - - {({ field }) => ( -
- - - setFieldValue( - 'tax_residence', - value ? text : '', - true - ) - } - list_portal_id='modal_root' - data-testid='tax_residence' - disabled={disabled_items.includes( - 'tax_residence' - )} - /> - - - { - handleChange(e); - setFieldValue( - 'tax_residence', - e.target.value, - true - ); - }} - {...field} - required - data_testid='tax_residence_mobile' - disabled={disabled_items.includes( - 'tax_residence' - )} - /> - -
{ - setIsTaxResidencePopoverOpen(true); - setIsTinPopoverOpen(false); - e.stopPropagation(); - }} - > - -
-
- )} -
- )} - {'tax_identification_number' in props.value && ( -
- )} - {warning_items?.tax_identification_number && ( -
- )} - {'employment_status' in props.value && ( -
- - - - - { - setFieldTouched('employment_status', true); - handleChange(e); - }} - disabled={disabled_items.includes('employment_status')} - /> - -
- )} - {'tax_identification_confirm' in props.value && ( - - setFieldValue( - 'tax_identification_confirm', - !values.tax_identification_confirm, - true - ) - } - value={values.tax_identification_confirm} - label={localize( - 'I hereby confirm that the tax information I provided is true and complete. I will also inform {{legal_entity_name}} about any changes to this information.', - { - legal_entity_name: getLegalEntityName('maltainvest'), - } - )} - renderlabel={title => ( - - {title} - - )} - withTabIndex={0} - data-testid='tax_identification_confirm' - /> - )} + + + )} - {'account_opening_reason' in props.value && ( // TODO: [deriv-eu] Remove account opening reason once api is optional - - - - {({ field }) => ( - - - - - - { - handleChange(e); - setFieldValue( - 'account_opening_reason', - e.target.value, - true - ); - }} - {...field} - required - data_testid='account_opening_reason_mobile' - disabled={disabled_items.includes( - 'account_opening_reason' - )} - /> - - + +
+ - - )} + /> +
+
@@ -777,7 +242,7 @@ const PersonalDetails = ({ onCancel={() => handleCancel(values)} /> - + )} )} diff --git a/packages/account/src/Components/poi/idv-document-submit/__tests__/idv-document-submit.spec.tsx b/packages/account/src/Components/poi/idv-document-submit/__tests__/idv-document-submit.spec.tsx index 06da0530a8f3..cd91cddbcb05 100644 --- a/packages/account/src/Components/poi/idv-document-submit/__tests__/idv-document-submit.spec.tsx +++ b/packages/account/src/Components/poi/idv-document-submit/__tests__/idv-document-submit.spec.tsx @@ -2,14 +2,13 @@ import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { isDesktop, isMobile } from '@deriv/shared'; import IdvDocumentSubmit from '../idv-document-submit'; -// import { isSequentialNumber, isRecurringNumberRegex } from '../utils'; -import { FormikValues } from 'formik'; jest.mock('react-router'); jest.mock('Assets/ic-document-submit-icon.svg', () => jest.fn(() => 'DocumentSubmitLogo')); -jest.mock('../utils.js', () => ({ - getDocumentData(country_code: keyof FormikValues, key: number) { - const data: FormikValues = { +jest.mock('Helpers/utils.ts', () => ({ + ...jest.requireActual('Helpers/utils.ts'), + getDocumentData: jest.fn((country_code, key) => { + const data = { tc: { document_1: { new_display_name: '', @@ -24,7 +23,7 @@ jest.mock('../utils.js', () => ({ }, }; return data[country_code][key]; - }, + }), getRegex: jest.fn(() => /5436454364243/i), })); @@ -35,7 +34,18 @@ jest.mock('@deriv/shared', () => ({ formatInput: jest.fn(() => '5436454364243'), WS: { send: jest.fn(() => Promise.resolve({ error: '' })), + setSettings: jest.fn(() => Promise.resolve({ error: '' })), + authorized: { + storage: { + getSettings: jest.fn(() => Promise.resolve({ error: '' })), + }, + }, }, + filterObjProperties: jest.fn(() => ({ + first_name: 'test', + last_name: 'test', + date_of_birth: '1970-01-01', + })), })); describe('', () => { @@ -57,19 +67,20 @@ describe('', () => { }, }, is_from_external: false, + account_settings: {}, + getChangeableFields: jest.fn(() => []), }; it('should render IdvDocumentSubmit component', () => { render(); - expect(screen.getByText(/verify your identity/i)).toBeInTheDocument(); - expect(screen.getByText(/Please select the document type and enter the ID number/i)).toBeInTheDocument(); - expect(screen.getByText('DocumentSubmitLogo')).toBeInTheDocument(); + expect(screen.getByText(/Identity verification/i)).toBeInTheDocument(); + expect(screen.getByText(/details/i)).toBeInTheDocument(); expect(screen.queryByText('New ID type name')).not.toBeInTheDocument(); expect(screen.queryByText('Please select a document type.')).not.toBeInTheDocument(); const inputs = screen.getAllByRole('textbox'); - expect(inputs.length).toBe(2); + expect(inputs.length).toBe(5); expect(inputs[0].name).toBe('document_type'); expect(inputs[1].name).toBe('document_number'); }); @@ -104,9 +115,6 @@ describe('', () => { (isDesktop as jest.Mock).mockReturnValue(false); (isMobile as jest.Mock).mockReturnValue(true); - const selected_doc_msg = - 'Please ensure all your personal details are the same as in your chosen document. If you wish to update your personal details, go to account settings.'; - render(); const verifyBtn = screen.getByRole('button', { name: /verify/i }); @@ -117,11 +125,9 @@ describe('', () => { const document_number_input = screen.getByPlaceholderText('Enter your document number'); expect(document_number_input.name).toBe('document_number'); expect(document_number_input).toBeDisabled(); - expect(screen.queryByText(selected_doc_msg)).not.toBeInTheDocument(); fireEvent.change(document_type_input, { target: { value: 'Test document 2 name' } }); expect(document_number_input).toBeEnabled(); - expect(screen.getByText(selected_doc_msg)).toBeInTheDocument(); expect(screen.queryByText(/please enter the correct format/i)).not.toBeInTheDocument(); fireEvent.blur(document_number_input); diff --git a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx index 3733232977c7..347cb848e396 100644 --- a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx +++ b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx @@ -1,200 +1,144 @@ import React from 'react'; +import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { Autocomplete, Button, DesktopWrapper, Input, MobileWrapper, Text, SelectNative } from '@deriv/components'; -import { Formik, Field, FormikProps, FormikValues, FormikHelpers } from 'formik'; -import { localize, Localize } from '@deriv/translations'; -import { formatInput, WS } from '@deriv/shared'; -import { generatePlaceholderText } from 'Helpers/utils'; -import { documentAdditionalError, getDocumentData, getRegex, preventEmptyClipboardPaste } from './utils'; +import { Button } from '@deriv/components'; +import { Formik } from 'formik'; +import { localize } from '@deriv/translations'; +import { + WS, + IDV_NOT_APPLICABLE_OPTION, + toMoment, + filterObjProperties, + isDesktop, + removeEmptyPropertiesFromObject, +} from '@deriv/shared'; +import { documentAdditionalError, getRegex, validate, makeSettingsRequest, validateName } from 'Helpers/utils'; import FormFooter from 'Components/form-footer'; import BackButtonIcon from 'Assets/ic-poi-back-btn.svg'; -import DocumentSubmitLogo from 'Assets/ic-document-submit-icon.svg'; - -type TIdvDocumentSubmit = { - handleBack: () => void; - handleViewComplete: () => void; - selected_country: { - value: string; - identity: { - services: { - idv: { - has_visual_sample: boolean; - documents_supported: Record< - string, - { display_name: string; format: string; additional?: { display_name: string; format: string } } - >; - }; - }; - }; - }; - is_from_external: boolean; -}; - -type TFormValues = { - error_message?: string; - example_format?: string; - document_type?: - | string - | { - example_format?: string; - id: string; - additional?: { display_name: string; format: string; example_format: string }; - }; - document_number?: string; - document_additional?: string; -}; +import IDVForm from 'Components/forms/idv-form'; +import PersonalDetailsForm from 'Components/forms/personal-details-form'; +import FormSubHeader from 'Components/form-sub-header'; const IdvDocumentSubmit = ({ handleBack, handleViewComplete, selected_country, is_from_external, -}: Partial> & TIdvDocumentSubmit) => { - const [document_list, setDocumentList] = React.useState([]); - const [document_image, setDocumentImage] = React.useState(null); - const [is_input_disable, setInputDisable] = React.useState(true); - const [selected_doc, setSelectedDoc] = React.useState(null); - const [input_value, setInputValue] = React.useState(''); - - const document_data = selected_country.identity.services.idv.documents_supported; - - const { - value: country_code, - identity: { - services: { - idv: { has_visual_sample }, - }, + account_settings, + getChangeableFields, +}) => { + const visible_settings = ['first_name', 'last_name', 'date_of_birth']; + const form_initial_values = filterObjProperties(account_settings, visible_settings) || {}; + + if (form_initial_values.date_of_birth) { + form_initial_values.date_of_birth = toMoment(form_initial_values.date_of_birth).format('YYYY-MM-DD'); + } + + const changeable_fields = [...getChangeableFields()]; + + const initial_values = { + document_type: { + id: '', + text: '', + value: '', + example_format: '', + sample_image: '', }, - } = selected_country; - - React.useEffect(() => { - // NOTE: This is a temporary filter. Remove after backend handles this from their side - const document_types = Object.keys(document_data); - const filtered_documents = ['gh', 'ng'].includes(country_code) - ? document_types.filter(d => d !== 'voter_id') - : document_types; - - setDocumentList( - filtered_documents.map(key => { - const { display_name, format } = document_data[key]; - const { new_display_name, example_format, sample_image } = getDocumentData(country_code, key); - const needs_additional_document = !!document_data[key].additional; - - if (needs_additional_document) { - return { - id: key, - text: new_display_name || display_name, - additional: { - display_name: document_data[key].additional?.display_name, - format: document_data[key].additional?.format, - }, - value: format, - sample_image, - example_format, - }; - } - return { - id: key, - text: new_display_name || display_name, - value: format, - sample_image, - example_format, - }; - }) - ); - }, [country_code, document_data]); - - type TsetFieldValue = FormikHelpers['setFieldValue']; - - const onKeyUp = (e: FormikValues, document_name: string, values: TFormValues, setFieldValue: TsetFieldValue) => { - const example_format = - document_name === 'document_number' - ? typeof values.document_type === 'object' && values.document_type?.example_format - : typeof values.document_type === 'object' && values.document_type.additional?.example_format; - let current_input = ''; - if (typeof example_format === 'string' && example_format.includes('-')) { - current_input = formatInput(example_format, current_input || e.target.value, '-'); - } else { - current_input = e.target.value; - } - setFieldValue(document_name, current_input, true); - validateFields(values); + document_number: '', + ...form_initial_values, }; - const resetDocumentItemSelected = (setFieldValue: TsetFieldValue) => { - setFieldValue( - 'document_type', - { - id: '', - text: '', - value: '', - example_format: '', - sample_image: '', - }, - true - ); - setDocumentImage(''); + const getExampleFormat = example_format => { + return example_format ? localize('Example: ') + example_format : ''; }; - const initial_form_values: TFormValues = { - document_type: '', - document_number: '', + const shouldHideHelperImage = document_id => document_id === IDV_NOT_APPLICABLE_OPTION.id; + + const isDocumentTypeValid = document_type => { + if (!document_type?.text) { + return localize('Please select a document type.'); + } + return undefined; }; - const getDocument = (text: string) => { - return document_list.find((d: FormikValues) => d.text === text); + const isAdditionalDocumentValid = (document_type, document_additional) => { + const error_message = documentAdditionalError(document_additional, document_type.additional?.format); + if (error_message) { + return localize(error_message) + getExampleFormat(document_type.additional?.example_format); + } + return undefined; }; - const getExampleFormat = (example_format: boolean) => { - return example_format ? localize('Example: ') + example_format : ''; + const isDocumentNumberValid = (document_number, document_type) => { + const is_document_number_invalid = document_number === document_type.example_format; + if (!document_number) { + return localize('Please enter your document number. ') + getExampleFormat(document_type.example_format); + } else if (is_document_number_invalid) { + return localize('Please enter a valid ID number.'); + } + const format_regex = getRegex(document_type.value); + if (!format_regex.test(document_number)) { + return localize('Please enter the correct format. ') + getExampleFormat(document_type.example_format); + } + return undefined; }; - const validateFields = (values: FormikValues) => { - const errors: TFormValues = {}; + const validateFields = values => { + const errors = {}; const { document_type, document_number, document_additional } = values; const needs_additional_document = !!document_type.additional; - const is_document_number_invalid = document_number === document_type.example_format; - if (!document_type || !document_type.text || !document_type.value) { - errors.document_type = localize('Please select a document type.'); - } else { - setInputDisable(false); + errors.document_type = isDocumentTypeValid(document_type); + if (!shouldHideHelperImage(document_type?.id)) { + if (needs_additional_document) { + errors.document_additional = isAdditionalDocumentValid(document_type, document_additional); + } + errors.document_number = isDocumentNumberValid(document_number, document_type); } + const required_fields = ['first_name', 'last_name', 'date_of_birth']; + const validateValues = validate(errors, values); + validateValues(val => val, required_fields, localize('This field is required')); - if (needs_additional_document) { - const error_message = documentAdditionalError(document_additional, document_type.additional?.format); - if (error_message) - errors.document_additional = - localize(error_message) + getExampleFormat(document_type.additional?.example_format); + if (values.first_name) { + errors.first_name = validateName(values.first_name); } - - if (!document_number) { - errors.document_number = - localize('Please enter your document number. ') + getExampleFormat(document_type.example_format); - } else if (is_document_number_invalid) { - errors.document_number = localize('Please enter a valid ID number.'); - } else { - const format_regex = getRegex(document_type.value); - if (!format_regex.test(document_number)) { - errors.document_number = - localize('Please enter the correct format. ') + getExampleFormat(document_type.example_format); - } + if (values.last_name) { + errors.last_name = validateName(values.last_name); } - return errors; + return removeEmptyPropertiesFromObject(errors); }; - const submitHandler = (values: TFormValues, { setSubmitting, setErrors }: FormikHelpers) => { + const submitHandler = async (values, { setSubmitting, setErrors }) => { setSubmitting(true); + + const request = makeSettingsRequest(values, changeable_fields); + + const data = await WS.setSettings(request); + + if (data.error) { + setErrors({ error_message: data.error.message }); + setSubmitting(false); + return; + } + const get_settings = WS.authorized.storage.getSettings(); + if (get_settings.error) { + setErrors({ error_message: data.error.message }); + setSubmitting(false); + return; + } const submit_data = { identity_verification_document_add: 1, document_number: values.document_number, document_additional: values.document_additional || '', - document_type: typeof values.document_type === 'object' && values.document_type?.id, - issuing_country: country_code, + document_type: values.document_type.id, + issuing_country: selected_country.value, }; - WS.send(submit_data).then((response: FormikValues) => { + if (submit_data.document_type === IDV_NOT_APPLICABLE_OPTION.id) { + return; + } + WS.send(submit_data).then(response => { setSubmitting(false); if (response.error) { setErrors({ error_message: response.error.message }); @@ -205,7 +149,7 @@ const IdvDocumentSubmit = ({ }; return ( - + {({ dirty, errors, @@ -215,211 +159,59 @@ const IdvDocumentSubmit = ({ isSubmitting, isValid, setFieldValue, + setFieldTouched, touched, values, - }: Pick< - FormikProps, - | 'dirty' - | 'errors' - | 'handleBlur' - | 'handleChange' - | 'handleSubmit' - | 'isSubmitting' - | 'isValid' - | 'setFieldValue' - | 'touched' - | 'values' - >) => ( -
- - - {localize('Verify your identity')} - - - {localize('Please select the document type and enter the ID number.')} - -
-
-
- - {({ field }: FormikValues) => ( - - -
- { - handleBlur(e); - if (!getDocument(e.target.value)) { - resetDocumentItemSelected(setFieldValue); - } - }} - onChange={(e: FormikValues) => { - setInputValue(e.target.value); - handleChange(e); - }} - onItemSelection={(item: { - id: string; - text: string; - value: string; - sample_image: string; - example_format: string; - }) => { - if (item.text === 'No results found' || !item.text) { - setSelectedDoc(''); - resetDocumentItemSelected(setFieldValue); - } else { - if (typeof setFieldValue === 'function') { - setFieldValue('document_type', item, true); - } - setSelectedDoc(item.id); - if (has_visual_sample) { - setDocumentImage(item.sample_image || ''); - } - } - }} - required - /> -
-
- - { - if (typeof handleChange === 'function') { - handleChange(e); - } - const selected_document: undefined | FormikValues = getDocument( - e.target.value - ); - if (selected_document) { - setSelectedDoc(selected_document.id); - if (typeof setFieldValue === 'function') { - setFieldValue('document_type', selected_document, true); - } - if (has_visual_sample) { - setDocumentImage(selected_document.sample_image); - } - } - }} - use_text={true} - required - /> - -
- )} -
-
-
- - {({ field }: FormikValues) => ( - - ) => - onKeyUp(e, 'document_number', values, setFieldValue) - } - required - /> - {values.document_type.additional?.display_name && ( - ) => - onKeyUp(e, 'document_additional', values, setFieldValue) - } - /> - )} - - )} - -
-
- {document_image && ( -
- - {localize('Sample:')} - -
- document sample image -
-
- )} -
- {selected_doc && ( - ( +
+
+ + + + +
- - - )} + +
+
- + {isDesktop() && ( + + )} + {status?.error_msg && ( +
+ + + + } + is_danger + /> +
+ )} + + + )} + + ); +}; + +export default PoiConfirmWithExampleFormContainer; diff --git a/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx b/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx index 9fa2a04ca000..5fdc8ba2fb6b 100644 --- a/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx +++ b/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx @@ -1,31 +1,25 @@ import React from 'react'; -import { Formik, Field, FormikValues, FormikHelpers, FormikErrors } from 'formik'; -import { localize, Localize } from '@deriv/translations'; +import { Formik, FormikValues, FormikHelpers, FormikErrors, Form } from 'formik'; +import { localize } from '@deriv/translations'; +import classNames from 'classnames'; +import { Button } from '@deriv/components'; +import { filterObjProperties, toMoment, removeEmptyPropertiesFromObject } from '@deriv/shared'; import { - Autocomplete, - AutoHeightWrapper, - DesktopWrapper, - FormSubmitButton, - Input, - MobileWrapper, - Modal, - SelectNative, - Text, - ThemedScrollbars, -} from '@deriv/components'; -import { isDesktop, formatInput, isMobile } from '@deriv/shared'; -import { - documentAdditionalError, - getDocumentData, - getRegex, - preventEmptyClipboardPaste, -} from '../../idv-document-submit/utils'; -import DocumentSubmitLogo from 'Assets/ic-document-submit-icon.svg'; -import { generatePlaceholderText } from 'Helpers/utils'; + validate, + validateName, + isDocumentTypeValid, + isAdditionalDocumentValid, + isDocumentNumberValid, + shouldHideHelperImage, +} from 'Helpers/utils'; +import FormSubHeader from 'Components/form-sub-header'; +import IDVForm from 'Components/forms/idv-form'; +import PersonalDetailsForm from 'Components/forms/personal-details-form'; +import FormFooter from 'Components/form-footer'; +import { GetSettings } from '@deriv/api-types'; type TIdvDocSubmitOnSignup = { citizen_data: FormikValues; - has_previous: boolean; onPrevious: (values: FormikValues) => void; onNext: ( values: FormikValues, @@ -33,158 +27,66 @@ type TIdvDocSubmitOnSignup = { ) => void; value: FormikValues; has_idv_error?: boolean; + account_settings: GetSettings; + getChangeableFields: () => string[]; }; export const IdvDocSubmitOnSignup = ({ citizen_data, - has_previous, - onPrevious, onNext, - value, - has_idv_error, + account_settings, + getChangeableFields, }: TIdvDocSubmitOnSignup) => { - const [document_list, setDocumentList] = React.useState([]); - const [document_image, setDocumentImage] = React.useState(null); - const [is_input_disable, setInputDisable] = React.useState(true); - const [selected_doc, setSelectedDoc] = React.useState(null); - const [input_value, setInputValue] = React.useState(''); - - const document_data = citizen_data.identity.services.idv.documents_supported; - const { - value: country_code, - identity: { - services: { - idv: { has_visual_sample }, - }, - }, - } = citizen_data; - - React.useEffect(() => { - const document_types = Object.keys(document_data); - const filtered_documents = ['gh', 'ng'].includes(country_code) - ? document_types.filter(d => d !== 'voter_id') - : document_types; - - setDocumentList( - filtered_documents.map(key => { - const { display_name, format } = document_data[key]; - const { new_display_name, example_format, sample_image } = getDocumentData(country_code, key); - const needs_additional_document = !!document_data[key].additional; - - if (needs_additional_document) { - return { - id: key, - text: new_display_name || display_name, - additional: { - display_name: document_data[key].additional?.display_name, - format: document_data[key].additional?.format, - }, - value: format, - sample_image, - example_format, - }; - } - return { - id: key, - text: new_display_name || display_name, - value: format, - sample_image, - example_format, - }; - }) - ); - }, [country_code, document_data]); - - const initial_form_values = { - document_type: value - ? value.document_type - : { - id: '', - text: '', - value: '', - example_format: '', - sample_image: '', - }, - - document_number: value ? value.document_number : '', - }; - const validateFields = (values: FormikValues) => { const errors: FormikErrors = {}; const { document_type, document_number, document_additional } = values; const needs_additional_document = !!document_type.additional; - const is_document_number_invalid = document_number === document_type.example_format; - if (!document_type || !document_type.text || !document_type.value) { - errors.document_type = localize('Please select a document type.'); - } else { - setInputDisable(false); + errors.document_type = isDocumentTypeValid(document_type); + if (!shouldHideHelperImage(document_type?.id)) { + if (needs_additional_document) { + errors.document_additional = isAdditionalDocumentValid(document_type, document_additional); + } + errors.document_number = isDocumentNumberValid(document_number, document_type); } + const required_fields = ['first_name', 'last_name', 'date_of_birth']; + const validateValues = validate(errors, values); + validateValues(val => val, required_fields, localize('This field is required')); - if (needs_additional_document) { - const error_message = documentAdditionalError(document_additional, document_type.additional?.format); - if (error_message) - errors.document_additional = - localize(error_message) + getExampleFormat(document_type.additional?.example_format); + if (values.first_name) { + errors.first_name = validateName(values.first_name); } - - if (!document_number) { - errors.document_number = - localize('Please enter your document number. ') + getExampleFormat(document_type.example_format); - } else if (is_document_number_invalid) { - errors.document_number = localize('Please enter a valid ID number.'); - } else { - const format_regex = getRegex(document_type.value); - if (!format_regex.test(document_number)) { - errors.document_number = - localize('Please enter the correct format. ') + getExampleFormat(document_type.example_format); - } + if (values.last_name) { + errors.last_name = validateName(values.last_name); } - return errors; + return removeEmptyPropertiesFromObject(errors); }; - const resetDocumentItemSelected = (setFieldValue: FormikHelpers['setFieldValue']) => { - setFieldValue( - 'document_type', - { - id: '', - text: '', - value: '', - example_format: '', - sample_image: '', - }, - true - ); - setDocumentImage(''); - }; + const visible_settings = ['first_name', 'last_name', 'date_of_birth']; + const form_initial_values = filterObjProperties(account_settings, visible_settings) || {}; - const onKeyUp = ( - e: React.KeyboardEvent, - document_name: string, - values: FormikValues, - setFieldValue: FormikHelpers['setFieldValue'] - ) => { - const { example_format } = - document_name === 'document_number' ? values.document_type : values.document_type.additional; - const current_input: string = example_format.includes('-') - ? formatInput(example_format, current_input || e.target.value, '-') - : e.target.value; - setFieldValue(document_name, current_input, true); - validateFields(values); - }; + if (form_initial_values.date_of_birth) { + form_initial_values.date_of_birth = toMoment(form_initial_values.date_of_birth).format('YYYY-MM-DD'); + } - const getDocument = (text: string) => { - return document_list.find((d: FormikValues) => d.text === text); - }; + const changeable_fields = [...getChangeableFields()]; - const getExampleFormat = (example_format: string) => { - return example_format ? localize('Example: ') + example_format : ''; + const initial_values = { + document_type: { + id: '', + text: '', + value: '', + example_format: '', + sample_image: '', + }, + document_number: '', + ...form_initial_values, }; return ( { onNext(values, actions); @@ -197,292 +99,63 @@ export const IdvDocSubmitOnSignup = ({ errors, handleBlur, handleChange, - handleSubmit, isSubmitting, isValid, setFieldValue, + setFieldTouched, touched, + dirty, values, }) => ( - - {({ setRef }) => ( -
- -
-
-
- - - {has_idv_error - ? localize('Verify your identity') - : localize('Identity information')} - - - {localize('Please select the document type and enter the ID number.')} - - {has_idv_error && !selected_doc && ( - <> - - {localize( - 'We were unable to verify your ID with the details you provided.' - )} - - - {localize( - 'Please check and resubmit or choose a different document type.' - )} - - - )} -
-
-
- - {({ field }: FormikValues) => ( - - -
- - ) => { - handleBlur(e); - if (!getDocument(e.target.value)) { - resetDocumentItemSelected( - setFieldValue - ); - } - }} - onChange={( - e: React.ChangeEvent - ) => { - setInputValue(e.target.value); - handleChange(e); - }} - onItemSelection={( - item: FormikValues - ) => { - if ( - item.text === - 'No results found' || - !item.text - ) { - setSelectedDoc(null); - resetDocumentItemSelected( - setFieldValue - ); - } else { - setFieldValue( - 'document_type', - item, - true - ); - setSelectedDoc(item.id); - if (has_visual_sample) { - setDocumentImage( - item.sample_image || '' - ); - } - } - }} - required - /> -
-
- - { - handleChange(e); - const selected_document: - | undefined - | FormikValues = getDocument( - e.target.value - ); - if (selected_document) { - setSelectedDoc( - selected_document.id - ); - setFieldValue( - 'document_type', - selected_document, - true - ); - if (has_visual_sample) { - setDocumentImage( - // eslint-disable-next-line max-len - selected_document.sample_image - ); - } - } - }} - use_text={true} - required - /> - -
- )} -
-
-
- - {({ field }: FormikValues) => ( - - - ) => - onKeyUp( - e, - 'document_number', - values, - setFieldValue - ) - } - required - /> - {values.document_type.additional?.display_name && ( - - ) => - onKeyUp( - e, - 'document_additional', - values, - setFieldValue - ) - } - required - /> - )} - - )} - -
-
- {document_image && ( -
- - {localize('Sample:')} - -
- document sample image -
-
- )} - {selected_doc && ( - - - - )} -
-
-
-
-
- - - onPrevious(values)} - /> - -
- )} -
+
+
+ + + +
+ +
+
+ +