diff --git a/src/CONST.ts b/src/CONST.ts index eed1b98ae551..11634e97f1a2 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -442,6 +442,12 @@ const CONST = { INTERNAL_DEV_EXPENSIFY_URL: 'https://www.expensify.com.dev', STAGING_EXPENSIFY_URL: 'https://staging.expensify.com', EXPENSIFY_URL: 'https://www.expensify.com', + BANK_ACCOUNT_PERSONAL_DOCUMENTATION_INFO_URL: + 'https://community.expensify.com/discussion/6983/faq-why-do-i-need-to-provide-personal-documentation-when-setting-up-updating-my-bank-account', + PERSONAL_DATA_PROTECTION_INFO_URL: 'https://community.expensify.com/discussion/5677/deep-dive-security-how-expensify-protects-your-information', + ONFIDO_FACIAL_SCAN_POLICY_URL: 'https://onfido.com/facial-scan-policy-and-release/', + ONFIDO_PRIVACY_POLICY_URL: 'https://onfido.com/privacy/', + ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/', // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'http://localhost:', diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index cb1d306eebb6..53ca279c2cb2 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -1,9 +1,8 @@ -import React from 'react'; +import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; +import _ from 'lodash'; import styles from '../../styles/styles'; -import withLocalize from '../../components/withLocalize'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import CONST from '../../CONST'; import TextLink from '../../components/TextLink'; @@ -16,182 +15,188 @@ import ONYXKEYS from '../../ONYXKEYS'; import RequestorOnfidoStep from './RequestorOnfidoStep'; import Form from '../../components/Form'; import ScreenWrapper from '../../components/ScreenWrapper'; -import StepPropTypes from './StepPropTypes'; +import useLocalize from '../../hooks/useLocalize'; +import {reimbursementAccountPropTypes} from './reimbursementAccountPropTypes'; +import ReimbursementAccountDraftPropTypes from './ReimbursementAccountDraftPropTypes'; const propTypes = { - ...StepPropTypes, + onBackButtonPress: PropTypes.func.isRequired, + getDefaultStateForField: PropTypes.func.isRequired, + reimbursementAccount: reimbursementAccountPropTypes.isRequired, + reimbursementAccountDraft: ReimbursementAccountDraftPropTypes.isRequired, /** If we should show Onfido flow */ shouldShowOnfido: PropTypes.bool.isRequired, }; -class RequestorStep extends React.Component { - constructor(props) { - super(props); - - this.validate = this.validate.bind(this); - this.submit = this.submit.bind(this); - } - - /** - * @param {Object} values - * @returns {Object} - */ - validate(values) { - const requiredFields = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; - const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - - if (values.dob) { - if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.dob'; - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.age'; - } - } - - if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { - errors.ssnLast4 = 'bankAccount.error.ssnLast4'; - } +const REQUIRED_FIELDS = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; +const INPUT_KEYS = { + firstName: 'firstName', + lastName: 'lastName', + dob: 'dob', + ssnLast4: 'ssnLast4', + street: 'requestorAddressStreet', + city: 'requestorAddressCity', + state: 'requestorAddressState', + zipCode: 'requestorAddressZipCode', +}; +const STEP_COUNTER = {step: 3, total: 5}; - if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { - errors.requestorAddressStreet = 'bankAccount.error.addressStreet'; - } +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); - if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { - errors.requestorAddressZipCode = 'bankAccount.error.zipCode'; + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = 'bankAccount.error.dob'; + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = 'bankAccount.error.age'; } + } - if (!ValidationUtils.isRequiredFulfilled(values.isControllingOfficer)) { - errors.isControllingOfficer = 'requestorStep.isControllingOfficerError'; - } + if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { + errors.ssnLast4 = 'bankAccount.error.ssnLast4'; + } - return errors; + if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { + errors.requestorAddressStreet = 'bankAccount.error.addressStreet'; } - submit(values) { - const payload = { - bankAccountID: lodashGet(this.props.reimbursementAccount, 'achData.bankAccountID') || 0, - ...values, - }; + if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { + errors.requestorAddressZipCode = 'bankAccount.error.zipCode'; + } - BankAccounts.updatePersonalInformationForBankAccount(payload); + if (!ValidationUtils.isRequiredFulfilled(values.isControllingOfficer)) { + errors.isControllingOfficer = 'requestorStep.isControllingOfficerError'; } - render() { - if (this.props.shouldShowOnfido) { - return ( - - ); - } + return errors; +}; +function RequestorStep({reimbursementAccount, shouldShowOnfido, reimbursementAccountDraft, onBackButtonPress, getDefaultStateForField}) { + const {translate} = useLocalize(); + + const defaultValues = useMemo( + () => ({ + firstName: getDefaultStateForField(INPUT_KEYS.firstName), + lastName: getDefaultStateForField(INPUT_KEYS.lastName), + street: getDefaultStateForField(INPUT_KEYS.street), + city: getDefaultStateForField(INPUT_KEYS.city), + state: getDefaultStateForField(INPUT_KEYS.state), + zipCode: getDefaultStateForField(INPUT_KEYS.zipCode), + dob: getDefaultStateForField(INPUT_KEYS.dob), + ssnLast4: getDefaultStateForField(INPUT_KEYS.ssnLast4), + }), + [getDefaultStateForField], + ); + + const submit = useCallback( + (values) => { + const payload = { + bankAccountID: _.get(reimbursementAccount, 'achData.bankAccountID', 0), + ...values, + }; + + BankAccounts.updatePersonalInformationForBankAccount(payload); + }, + [reimbursementAccount], + ); + + const renderLabelComponent = () => ( + + {translate('requestorStep.isControllingOfficer')} + + ); + + if (shouldShowOnfido) { return ( - - -
- {this.props.translate('requestorStep.subtitle')} - - - {`${this.props.translate('requestorStep.learnMore')}`} - - {' | '} - - {`${this.props.translate('requestorStep.isMyDataSafe')}`} - - - - ( - - {this.props.translate('requestorStep.isControllingOfficer')} - - )} - style={[styles.mt4]} - shouldSaveDraft - /> - - {this.props.translate('requestorStep.onFidoConditions')} - - {this.props.translate('onfidoStep.facialScan')} - - {', '} - - {this.props.translate('common.privacy')} - - {` ${this.props.translate('common.and')} `} - - {this.props.translate('common.termsOfService')} - - - -
+ ); } + + return ( + + +
+ {translate('requestorStep.subtitle')} + + + {translate('requestorStep.learnMore')} + + {' | '} + + {translate('requestorStep.isMyDataSafe')} + + + + + + {translate('requestorStep.onFidoConditions')} + + {translate('onfidoStep.facialScan')} + + {', '} + + {translate('common.privacy')} + + {` ${translate('common.and')} `} + + {translate('common.termsOfService')} + + + +
+ ); } RequestorStep.propTypes = propTypes; +RequestorStep.displayName = 'RequestorStep'; -export default withLocalize(RequestorStep); +export default React.forwardRef(RequestorStep);