diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 84add6c9f423..77c3f0396cbc 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -112,9 +112,6 @@ export default { // Stores information about additional details form entry WALLET_ADDITIONAL_DETAILS: 'walletAdditionalDetails', - // Stores values put into the additional details step of the wallet KYC flow - WALLET_ADDITIONAL_DETAILS_DRAFT: 'walletAdditionalDetailsDraft', - // Object containing Wallet terms step state WALLET_TERMS: 'walletTerms', diff --git a/src/libs/actions/Wallet.js b/src/libs/actions/Wallet.js index 89f4753726c7..e9627d8bf329 100644 --- a/src/libs/actions/Wallet.js +++ b/src/libs/actions/Wallet.js @@ -262,13 +262,6 @@ function openEnablePaymentsPage() { API.read('OpenEnablePaymentsPage'); } -/** - * @param {Object} keyValuePair - */ -function updateAdditionalDetailsDraft(keyValuePair) { - Onyx.merge(ONYXKEYS.WALLET_ADDITIONAL_DETAILS_DRAFT, keyValuePair); -} - /** * @param {String} currentStep */ @@ -317,7 +310,6 @@ export { openInitialSettingsPage, openEnablePaymentsPage, setAdditionalDetailsErrors, - updateAdditionalDetailsDraft, setAdditionalDetailsErrorMessage, setAdditionalDetailsQuestions, updateCurrentStep, diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 4051a1becdc5..c303c6fac53c 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; @@ -17,20 +16,15 @@ import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import TextLink from '../../components/TextLink'; import TextInput from '../../components/TextInput'; -import FormScrollView from '../../components/FormScrollView'; -import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton'; import * as Wallet from '../../libs/actions/Wallet'; import * as ValidationUtils from '../../libs/ValidationUtils'; import * as LoginUtils from '../../libs/LoginUtils'; import AddressForm from '../ReimbursementAccount/AddressForm'; import DatePicker from '../../components/DatePicker'; -import FormHelper from '../../libs/FormHelper'; -import walletAdditionalDetailsDraftPropTypes from './walletAdditionalDetailsDraftPropTypes'; +import Form from '../../components/Form'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../components/withCurrentUserPersonalDetails'; import * as PersonalDetails from '../../libs/actions/PersonalDetails'; import OfflineIndicator from '../../components/OfflineIndicator'; -import * as ErrorUtils from '../../libs/ErrorUtils'; -import SafeAreaConsumer from '../../components/SafeAreaConsumer'; const propTypes = { ...withLocalizePropTypes, @@ -60,9 +54,6 @@ const propTypes = { /** Error code to determine additional behavior */ errorCode: PropTypes.string, }), - - /** Stores the personal details typed by the user */ - walletAdditionalDetailsDraft: walletAdditionalDetailsDraftPropTypes, }; const defaultProps = { @@ -74,25 +65,29 @@ const defaultProps = { idNumber: '', errorCode: '', }, - walletAdditionalDetailsDraft: { - legalFirstName: '', - legalLastName: '', - addressStreet: '', - addressCity: '', - addressState: '', - addressZip: '', - phoneNumber: '', - dob: '', - ssn: '', - }, ...withCurrentUserPersonalDetailsDefaultProps, }; +const INPUT_IDS = { + LEGAL_FIRST_NAME: 'legalFirstName', + LEGAL_LAST_NAME: 'legalLastName', + PHONE_NUMBER: 'phoneNumber', + DOB: 'dob', + SSN: 'ssn', + ADDRESS: { + street: 'addressStreet', + city: 'addressCity', + state: 'addressState', + zipCode: 'addressZip', + }, +}; + class AdditionalDetailsStep extends React.Component { constructor(props) { super(props); this.activateWallet = this.activateWallet.bind(this); + this.validate = this.validate.bind(this); this.errorTranslationKeys = { legalFirstName: 'bankAccount.error.firstName', @@ -113,146 +108,82 @@ class AdditionalDetailsStep extends React.Component { ssn: 'common.ssnLast4', ssnFull9: 'common.ssnFull9', }; - - this.formHelper = new FormHelper({ - errorPath: 'walletAdditionalDetails.errorFields', - setErrors: Wallet.setAdditionalDetailsErrors, - }); - } - - getFirstName() { - const {firstName} = PersonalDetails.extractFirstAndLastNameFromAvailableDetails(this.props.currentUserPersonalDetails); - return this.props.walletAdditionalDetailsDraft.legalFirstName || firstName; - } - - getLastName() { - const {lastName} = PersonalDetails.extractFirstAndLastNameFromAvailableDetails(this.props.currentUserPersonalDetails); - return this.props.walletAdditionalDetailsDraft.legalLastName || lastName; } /** + * @param {Object} values The values object is passed from Form.js and contains info for each form element that has an inputID * @returns {Object} */ - getErrors() { - return this.formHelper.getErrors(this.props); - } - - /** - * @param {String} fieldName - * @returns {String} - */ - getErrorText(fieldName) { - if (!this.getErrors()[fieldName]) { - return ''; - } - - return this.props.translate(this.errorTranslationKeys[fieldName]); - } - - /** - * @param {String} path - */ - clearError(path) { - this.formHelper.clearError(this.props, path); - } - - /** - * @returns {Boolean} - */ - validate() { - // Reset server error messages when resubmitting form - Wallet.setAdditionalDetailsErrorMessage(''); - + validate(values) { const errors = {}; - if (!this.getFirstName()) { - errors.legalFirstName = true; + if (_.isEmpty(values[INPUT_IDS.LEGAL_FIRST_NAME])) { + errors[INPUT_IDS.LEGAL_FIRST_NAME] = this.props.translate(this.errorTranslationKeys.legalFirstName); } - if (!this.getLastName()) { - errors.legalLastName = true; + if (_.isEmpty(values[INPUT_IDS.LEGAL_LAST_NAME])) { + errors[INPUT_IDS.LEGAL_LAST_NAME] = this.props.translate(this.errorTranslationKeys.legalLastName); } - if (!ValidationUtils.isValidPastDate(this.props.walletAdditionalDetailsDraft.dob)) { - errors.dob = true; + if (!ValidationUtils.isValidPastDate(values[INPUT_IDS.DOB])) { + errors[INPUT_IDS.DOB] = this.props.translate(this.errorTranslationKeys.dob); } - if (!ValidationUtils.meetsAgeRequirements(this.props.walletAdditionalDetailsDraft.dob)) { - errors.age = true; + if (!ValidationUtils.meetsAgeRequirements(values[INPUT_IDS.DOB])) { + errors[INPUT_IDS.DOB] = this.props.translate(this.errorTranslationKeys.age); } - if (!ValidationUtils.isValidAddress(this.props.walletAdditionalDetailsDraft.addressStreet)) { - errors.addressStreet = true; + if (!ValidationUtils.isValidAddress(values[INPUT_IDS.ADDRESS.street]) || _.isEmpty(values[INPUT_IDS.ADDRESS.street])) { + errors[INPUT_IDS.ADDRESS.street] = this.props.translate('bankAccount.error.addressStreet'); } - if (_.isEmpty(this.props.walletAdditionalDetailsDraft.addressCity)) { - errors.addressCity = true; + if (_.isEmpty(values[INPUT_IDS.ADDRESS.city])) { + errors[INPUT_IDS.ADDRESS.city] = this.props.translate('bankAccount.error.addressCity'); } - if (_.isEmpty(this.props.walletAdditionalDetailsDraft.addressState)) { - errors.addressState = true; + if (_.isEmpty(values[INPUT_IDS.ADDRESS.state])) { + errors[INPUT_IDS.ADDRESS.state] = this.props.translate('bankAccount.error.addressState'); } - if (!ValidationUtils.isValidZipCode(this.props.walletAdditionalDetailsDraft.addressZip)) { - errors.addressZip = true; + if (!ValidationUtils.isValidZipCode(values[INPUT_IDS.ADDRESS.zipCode])) { + errors[INPUT_IDS.ADDRESS.zipCode] = this.props.translate('bankAccount.error.zipCode'); } - if (!ValidationUtils.isValidUSPhone(this.props.walletAdditionalDetailsDraft.phoneNumber, true)) { - errors.phoneNumber = true; + if (!ValidationUtils.isValidUSPhone(values[INPUT_IDS.PHONE_NUMBER], true)) { + errors[INPUT_IDS.PHONE_NUMBER] = this.props.translate(this.errorTranslationKeys.phoneNumber); } + // this.props.walletAdditionalDetails stores errors returned by the server. If the server returns an SSN error + // then the user needs to provide the full 9 digit SSN. if (this.props.walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN) { - if (!ValidationUtils.isValidSSNFullNine(this.props.walletAdditionalDetailsDraft.ssn)) { - errors.ssnFull9 = true; + if (!ValidationUtils.isValidSSNFullNine(values[INPUT_IDS.SSN])) { + errors[INPUT_IDS.SSN] = this.props.translate(this.errorTranslationKeys.ssnFull9); } - } else if (!ValidationUtils.isValidSSNLastFour(this.props.walletAdditionalDetailsDraft.ssn)) { - errors.ssn = true; + } else if (!ValidationUtils.isValidSSNLastFour(values[INPUT_IDS.SSN])) { + errors[INPUT_IDS.SSN] = this.props.translate(this.errorTranslationKeys.ssn); } - Wallet.setAdditionalDetailsErrors(errors); - return _.size(errors) === 0; - } - - activateWallet() { - if (!this.validate()) { - return; - } - const personalDetails = { - ...this.props.walletAdditionalDetailsDraft, - phoneNumber: LoginUtils.getPhoneNumberWithoutUSCountryCodeAndSpecialChars(this.props.walletAdditionalDetailsDraft.phoneNumber), - legalFirstName: this.getFirstName(), - legalLastName: this.getLastName(), - }; - Wallet.updatePersonalDetails(personalDetails); + return errors; } /** - * Clear both errors associated with dob, and set the new value. - * - * @param {String} value + * @param {Object} values The values object is passed from Form.js and contains info for each form element that has an inputID */ - clearDateErrorsAndSetValue(value) { - this.formHelper.clearErrors(this.props, ['dob', 'age']); - Wallet.updateAdditionalDetailsDraft({dob: moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING)}); - } - - /** - * Clear ssn and ssnFull9 error and set the new value - * - * @param {String} value - */ - clearSSNErrorAndSetValue(value) { - this.formHelper.clearErrors(this.props, ['ssn', 'ssnFull9']); - Wallet.updateAdditionalDetailsDraft({ssn: value}); - } + activateWallet(values) { + const personalDetails = { + phoneNumber: LoginUtils.getPhoneNumberWithoutUSCountryCodeAndSpecialChars(values[INPUT_IDS.PHONE_NUMBER]), + legalFirstName: values[INPUT_IDS.LEGAL_FIRST_NAME], + legalLastName: values[INPUT_IDS.LEGAL_LAST_NAME], + addressStreet: values[INPUT_IDS.ADDRESS.street], + addressCity: values[INPUT_IDS.ADDRESS.city], + addressState: values[INPUT_IDS.ADDRESS.state], + addressZip: values[INPUT_IDS.ADDRESS.zipCode], + dob: moment(values[INPUT_IDS.DOB]).format(CONST.DATE.MOMENT_FORMAT_STRING), + ssn: values[INPUT_IDS.SSN], + }; - /** - * @param {String} fieldName - * @param {String} value - */ - clearErrorAndSetValue(fieldName, value) { - Wallet.updateAdditionalDetailsDraft({[fieldName]: value}); - this.clearError(fieldName); + // Attempt to set the personal details + Wallet.updatePersonalDetails(personalDetails); } render() { @@ -272,9 +203,6 @@ class AdditionalDetailsStep extends React.Component { ); } - - const errorMessage = ErrorUtils.getLatestErrorMessage(this.props.walletAdditionalDetails) || ''; - const isErrorVisible = _.size(this.getErrors()) > 0 || Boolean(errorMessage); const shouldAskForFullSSN = this.props.walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN; return ( @@ -293,97 +221,58 @@ class AdditionalDetailsStep extends React.Component { {this.props.translate('additionalDetailsStep.helpLink')} - this.form = el}> - - - this.clearErrorAndSetValue('legalFirstName', val)} - value={this.getFirstName()} - errorText={this.getErrorText('legalFirstName')} - /> - this.clearErrorAndSetValue('legalLastName', val)} - value={this.getLastName()} - errorText={this.getErrorText('legalLastName')} - /> - { - const renamedFields = { - street: 'addressStreet', - state: 'addressState', - city: 'addressCity', - zipCode: 'addressZip', - }; - _.each(values, (value, inputKey) => { - const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); - this.clearErrorAndSetValue(renamedInputKey, value); - }); - }} - /> - - this.clearErrorAndSetValue('phoneNumber', val)} - value={this.props.walletAdditionalDetailsDraft.phoneNumber || ''} - placeholder={this.props.translate('common.phoneNumberPlaceholder')} - errorText={this.getErrorText('phoneNumber')} - /> - this.clearDateErrorsAndSetValue(val)} - defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''} - placeholder={this.props.translate('common.dob')} - errorText={this.getErrorText('dob') || this.getErrorText('age')} - /> - this.clearSSNErrorAndSetValue(val)} - value={this.props.walletAdditionalDetailsDraft.ssn || ''} - errorText={this.getErrorText('ssnFull9') || this.getErrorText('ssn')} - maxLength={shouldAskForFullSSN ? 9 : 4} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - - - {({safeAreaPaddingBottomStyle}) => ( - - { - this.form.scrollTo({y: 0, animated: true}); - }} - message={errorMessage} - isLoading={this.props.walletAdditionalDetails.isLoading} - buttonText={this.props.translate('common.saveAndContinue')} - /> - - )} - +
+ + + + + + - + ); @@ -398,7 +287,6 @@ export default compose( withOnyx({ walletAdditionalDetails: { key: ONYXKEYS.WALLET_ADDITIONAL_DETAILS, - initWithStoredValues: false, }, }), )(AdditionalDetailsStep); diff --git a/src/pages/EnablePayments/EnablePaymentsPage.js b/src/pages/EnablePayments/EnablePaymentsPage.js index 91c4799a808b..a374adaab40b 100644 --- a/src/pages/EnablePayments/EnablePaymentsPage.js +++ b/src/pages/EnablePayments/EnablePaymentsPage.js @@ -78,7 +78,7 @@ class EnablePaymentsPage extends React.Component { return ( <> {(currentStep === CONST.WALLET.STEP.ADDITIONAL_DETAILS || currentStep === CONST.WALLET.STEP.ADDITIONAL_DETAILS_KBA) - && } + && } {currentStep === CONST.WALLET.STEP.ONFIDO && this.props.walletAdditionalDetailsDraft && } {currentStep === CONST.WALLET.STEP.TERMS && } @@ -104,9 +104,6 @@ export default compose( // stored values here. initWithStoredValues: false, }, - walletAdditionalDetailsDraft: { - key: ONYXKEYS.WALLET_ADDITIONAL_DETAILS_DRAFT, - }, }), withNetwork(), )(EnablePaymentsPage); diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index bccf706a16d9..a731952c9eed 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -46,7 +46,12 @@ const propTypes = { }), /** Any errors that can arise from form validation */ - errors: PropTypes.objectOf(PropTypes.bool), + errors: PropTypes.shape({ + street: PropTypes.bool, + city: PropTypes.bool, + state: PropTypes.bool, + zipCode: PropTypes.bool, + }), /** The map for inputID of the inputs */ inputKeys: PropTypes.shape({