From 32e2a148fa6873b6b9a5d97eeef343870817c34d Mon Sep 17 00:00:00 2001 From: josemak25 Date: Mon, 27 Feb 2023 17:56:43 +0100 Subject: [PATCH 01/11] refactor form component to carry new multiple error changes --- src/components/Form.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Form.js b/src/components/Form.js index 5a45d599661b..1b9264afecd0 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -174,11 +174,13 @@ class Form extends React.Component { FormActions.setErrorFields(this.props.formID, null); const validationErrors = this.props.validate(values); - if (!_.isObject(validationErrors)) { + if (!_.every(validationErrors, _.isObject)) { throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}'); } - const errors = _.pick(validationErrors, (inputValue, inputID) => ( + const uniqueErrors = ErrorUtils.getUniqueErrorMessages(validationErrors) + + const errors = _.pick(uniqueErrors, (inputValue, inputID) => ( Boolean(this.touchedInputs[inputID]) )); From 353ee7c69a7e53e2a4681ddc84d98abe085b140f Mon Sep 17 00:00:00 2001 From: josemak25 Date: Mon, 27 Feb 2023 17:57:23 +0100 Subject: [PATCH 02/11] add error util method to get unique multiple error messages --- src/libs/ErrorUtils.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libs/ErrorUtils.js b/src/libs/ErrorUtils.js index e1ca7233ba8a..f5330cd566ff 100644 --- a/src/libs/ErrorUtils.js +++ b/src/libs/ErrorUtils.js @@ -54,8 +54,28 @@ function getLatestErrorMessage(onyxData) { .value(); } +/** + * @param {Array} errorMessages + * @returns {Object} + */ +function getUniqueErrorMessages(errorMessages) { + const errors = _.reduce(errorMessages, (acc, error) => { + const [key] = _.keys(error); + if (acc[key]) { + acc[key] = `${acc[key]}. ${error[key]}`; + } else { + acc[key] = error[key]; + } + + return acc; + }, {}); + + return errors; +} + export { // eslint-disable-next-line import/prefer-default-export getAuthenticateErrorMessage, getLatestErrorMessage, + getUniqueErrorMessages, }; From bcf334e2891bf3ddcadbf90db1d4cf492115cdb8 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Mon, 27 Feb 2023 17:57:47 +0100 Subject: [PATCH 03/11] refactor display name page error handler --- src/pages/settings/Profile/DisplayNamePage.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Profile/DisplayNamePage.js b/src/pages/settings/Profile/DisplayNamePage.js index 9e4e948237f4..4f95106f6eb5 100644 --- a/src/pages/settings/Profile/DisplayNamePage.js +++ b/src/pages/settings/Profile/DisplayNamePage.js @@ -51,21 +51,23 @@ class DisplayNamePage extends Component { * @param {Object} values * @param {String} values.firstName * @param {String} values.lastName - * @returns {Object} - An object containing the errors for each inputID + * @returns {Object} - An array containing the object errors for each inputID */ validate(values) { - const errors = {}; + const errors = []; // First we validate the first name field if (!ValidationUtils.isValidDisplayName(values.firstName)) { - errors.firstName = this.props.translate('personalDetails.error.hasInvalidCharacter'); - } else if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_FIRST_NAMES)) { - errors.firstName = this.props.translate('personalDetails.error.containsReservedWord'); + errors.push({firstName: this.props.translate('personalDetails.error.hasInvalidCharacter')}); + } + + if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_FIRST_NAMES)) { + errors.push({firstName: this.props.translate('personalDetails.error.containsReservedWord')}); } // Then we validate the last name field if (!ValidationUtils.isValidDisplayName(values.lastName)) { - errors.lastName = this.props.translate('personalDetails.error.hasInvalidCharacter'); + errors.push({lastName: this.props.translate('personalDetails.error.hasInvalidCharacter')}); } return errors; From c436e22a21231ab95e86c99ebab315fa0bba745d Mon Sep 17 00:00:00 2001 From: josemak25 Date: Mon, 27 Feb 2023 23:03:16 +0100 Subject: [PATCH 04/11] remove white space on display name page --- src/pages/settings/Profile/DisplayNamePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Profile/DisplayNamePage.js b/src/pages/settings/Profile/DisplayNamePage.js index 4f95106f6eb5..695accebd8ec 100644 --- a/src/pages/settings/Profile/DisplayNamePage.js +++ b/src/pages/settings/Profile/DisplayNamePage.js @@ -59,8 +59,8 @@ class DisplayNamePage extends Component { // First we validate the first name field if (!ValidationUtils.isValidDisplayName(values.firstName)) { errors.push({firstName: this.props.translate('personalDetails.error.hasInvalidCharacter')}); - } - + } + if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_FIRST_NAMES)) { errors.push({firstName: this.props.translate('personalDetails.error.containsReservedWord')}); } From b03826e7fa6dfcada0d298d934a2b8aa5baa3b36 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:01:40 +0100 Subject: [PATCH 05/11] refactor additional details step component errors --- src/components/Form.js | 6 ++--- .../EnablePayments/AdditionalDetailsStep.js | 24 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/Form.js b/src/components/Form.js index 1b9264afecd0..b82809fe9878 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -178,11 +178,9 @@ class Form extends React.Component { throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}'); } - const uniqueErrors = ErrorUtils.getUniqueErrorMessages(validationErrors) + const uniqueErrors = ErrorUtils.getUniqueErrorMessages(validationErrors); - const errors = _.pick(uniqueErrors, (inputValue, inputID) => ( - Boolean(this.touchedInputs[inputID]) - )); + const errors = _.pick(uniqueErrors, (inputValue, inputID) => (Boolean(this.touchedInputs[inputID]))); if (!_.isEqual(errors, this.state.errors)) { this.setState({errors}); diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 1091b849c630..e320c860c7e4 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -114,52 +114,52 @@ class AdditionalDetailsStep extends React.Component { * @returns {Object} */ validate(values) { - const errors = {}; + const errors = []; if (_.isEmpty(values[INPUT_IDS.LEGAL_FIRST_NAME])) { - errors[INPUT_IDS.LEGAL_FIRST_NAME] = this.props.translate(this.errorTranslationKeys.legalFirstName); + errors.push({[INPUT_IDS.LEGAL_FIRST_NAME]: this.props.translate(this.errorTranslationKeys.legalFirstName)}); } if (_.isEmpty(values[INPUT_IDS.LEGAL_LAST_NAME])) { - errors[INPUT_IDS.LEGAL_LAST_NAME] = this.props.translate(this.errorTranslationKeys.legalLastName); + errors.push({[INPUT_IDS.LEGAL_LAST_NAME]: this.props.translate(this.errorTranslationKeys.legalLastName)}); } if (!ValidationUtils.isValidPastDate(values[INPUT_IDS.DOB])) { - errors[INPUT_IDS.DOB] = this.props.translate(this.errorTranslationKeys.dob); + errors.push({[INPUT_IDS.DOB]: this.props.translate(this.errorTranslationKeys.dob)}); } if (!ValidationUtils.meetsAgeRequirements(values[INPUT_IDS.DOB])) { - errors[INPUT_IDS.DOB] = this.props.translate(this.errorTranslationKeys.age); + errors.push({[INPUT_IDS.DOB]: this.props.translate(this.errorTranslationKeys.age)}); } 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'); + errors.push({[INPUT_IDS.ADDRESS.street]: this.props.translate('bankAccount.error.addressStreet')}); } if (_.isEmpty(values[INPUT_IDS.ADDRESS.city])) { - errors[INPUT_IDS.ADDRESS.city] = this.props.translate('bankAccount.error.addressCity'); + errors.push({[INPUT_IDS.ADDRESS.city]: this.props.translate('bankAccount.error.addressCity')}); } if (_.isEmpty(values[INPUT_IDS.ADDRESS.state])) { - errors[INPUT_IDS.ADDRESS.state] = this.props.translate('bankAccount.error.addressState'); + errors.push({[INPUT_IDS.ADDRESS.state]: this.props.translate('bankAccount.error.addressState')}); } if (!ValidationUtils.isValidZipCode(values[INPUT_IDS.ADDRESS.zipCode])) { - errors[INPUT_IDS.ADDRESS.zipCode] = this.props.translate('bankAccount.error.zipCode'); + errors.push({[INPUT_IDS.ADDRESS.zipCode]: this.props.translate('bankAccount.error.zipCode')}); } if (!ValidationUtils.isValidUSPhone(values[INPUT_IDS.PHONE_NUMBER], true)) { - errors[INPUT_IDS.PHONE_NUMBER] = this.props.translate(this.errorTranslationKeys.phoneNumber); + errors.push({[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(values[INPUT_IDS.SSN])) { - errors[INPUT_IDS.SSN] = this.props.translate(this.errorTranslationKeys.ssnFull9); + errors.push({[INPUT_IDS.SSN]: this.props.translate(this.errorTranslationKeys.ssnFull9)}); } } else if (!ValidationUtils.isValidSSNLastFour(values[INPUT_IDS.SSN])) { - errors[INPUT_IDS.SSN] = this.props.translate(this.errorTranslationKeys.ssn); + errors.push({[INPUT_IDS.SSN]: this.props.translate(this.errorTranslationKeys.ssn)}); } return errors; From e00a34d90080cfd326d69411cecb1523086c18a4 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:02:10 +0100 Subject: [PATCH 06/11] refactor ACHC contract step component errors --- .../ReimbursementAccount/ACHContractStep.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index 26e2dd0ee410..1360a072a650 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -48,13 +48,14 @@ class ACHContractStep extends React.Component { * @returns {Object} */ validate(values) { - const errors = {}; + const errors = []; const errorKeys = { street: 'address', city: 'addressCity', state: 'addressState', }; + const requiredFields = ['firstName', 'lastName', 'dob', 'ssnLast4', 'street', 'city', 'zipCode', 'state']; if (values.hasOtherBeneficialOwners) { _.each(this.state.beneficialOwners, (ownerKey) => { @@ -62,34 +63,34 @@ class ACHContractStep extends React.Component { _.each(requiredFields, (inputKey) => { if (!ValidationUtils.isRequiredFulfilled(values[`beneficialOwner_${ownerKey}_${inputKey}`])) { const errorKey = errorKeys[inputKey] || inputKey; - errors[`beneficialOwner_${ownerKey}_${inputKey}`] = this.props.translate(`bankAccount.error.${errorKey}`); + errors.push({[`beneficialOwner_${ownerKey}_${inputKey}`]: this.props.translate(`bankAccount.error.${errorKey}`)}); } }); if (values[`beneficialOwner_${ownerKey}_dob`] && !ValidationUtils.meetsAgeRequirements(values[`beneficialOwner_${ownerKey}_dob`])) { - errors[`beneficialOwner_${ownerKey}_dob`] = this.props.translate('bankAccount.error.age'); + errors.push({[`beneficialOwner_${ownerKey}_dob`]: this.props.translate('bankAccount.error.age')}); } if (values[`beneficialOwner_${ownerKey}_ssnLast4`] && !ValidationUtils.isValidSSNLastFour(values[`beneficialOwner_${ownerKey}_ssnLast4`])) { - errors[`beneficialOwner_${ownerKey}_ssnLast4`] = this.props.translate('bankAccount.error.ssnLast4'); + errors.push({[`beneficialOwner_${ownerKey}_ssnLast4`]: this.props.translate('bankAccount.error.ssnLast4')}); } if (values[`beneficialOwner_${ownerKey}_street`] && !ValidationUtils.isValidAddress(values[`beneficialOwner_${ownerKey}_street`])) { - errors[`beneficialOwner_${ownerKey}_street`] = this.props.translate('bankAccount.error.addressStreet'); + errors.push({[`beneficialOwner_${ownerKey}_street`]: this.props.translate('bankAccount.error.addressStreet')}); } if (values[`beneficialOwner_${ownerKey}_zipCode`] && !ValidationUtils.isValidZipCode(values[`beneficialOwner_${ownerKey}_zipCode`])) { - errors[`beneficialOwner_${ownerKey}_zipCode`] = this.props.translate('bankAccount.error.zipCode'); + errors.push({[`beneficialOwner_${ownerKey}_zipCode`]: this.props.translate('bankAccount.error.zipCode')}); } }); } if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { - errors.acceptTermsAndConditions = this.props.translate('common.error.acceptTerms'); + errors.push({acceptTermsAndConditions: this.props.translate('common.error.acceptTerms')}); } if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { - errors.certifyTrueInformation = this.props.translate('beneficialOwnersStep.error.certify'); + errors.push({certifyTrueInformation: this.props.translate('beneficialOwnersStep.error.certify')}); } return errors; From 55adee0dade23f04cec727156206ad55f833a58f Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:03:35 +0100 Subject: [PATCH 07/11] refactor reimbursement account component errors --- .../BankAccountManualStep.js | 8 ++--- .../BankAccountPlaidStep.js | 2 +- src/pages/ReimbursementAccount/CompanyStep.js | 32 ++++++++++--------- .../ReimbursementAccount/RequestorStep.js | 24 +++++++------- .../ReimbursementAccount/ValidationStep.js | 4 +-- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/pages/ReimbursementAccount/BankAccountManualStep.js b/src/pages/ReimbursementAccount/BankAccountManualStep.js index a4e7a0eba73f..3eae28efd03b 100644 --- a/src/pages/ReimbursementAccount/BankAccountManualStep.js +++ b/src/pages/ReimbursementAccount/BankAccountManualStep.js @@ -35,20 +35,20 @@ class BankAccountManualStep extends React.Component { * @returns {Object} */ validate(values) { - const errorFields = {}; + const errorFields = []; const routingNumber = values.routingNumber && values.routingNumber.trim(); if ( !values.accountNumber || (!CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) && !CONST.BANK_ACCOUNT.REGEX.MASKED_US_ACCOUNT_NUMBER.test(values.accountNumber.trim())) ) { - errorFields.accountNumber = this.props.translate('bankAccount.error.accountNumber'); + errorFields.push({accountNumber: this.props.translate('bankAccount.error.accountNumber')}); } if (!routingNumber || !CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber)) { - errorFields.routingNumber = this.props.translate('bankAccount.error.routingNumber'); + errorFields.push({routingNumber: this.props.translate('bankAccount.error.routingNumber')}); } if (!values.acceptTerms) { - errorFields.acceptTerms = this.props.translate('common.error.acceptTerms'); + errorFields.push({acceptTerms: this.props.translate('common.error.acceptTerms')}); } return errorFields; diff --git a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js index 45ef47dd4686..a818e0f7fcc3 100644 --- a/src/pages/ReimbursementAccount/BankAccountPlaidStep.js +++ b/src/pages/ReimbursementAccount/BankAccountPlaidStep.js @@ -80,7 +80,7 @@ class BankAccountPlaidStep extends React.Component { />
({})} + validate={() => []} onSubmit={this.submit} scrollContextEnabled submitButtonText={this.props.translate('common.saveAndContinue')} diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 3f9e285aa400..a177d6d73026 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -63,56 +63,58 @@ class CompanyStep extends React.Component { * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} */ validate(values) { - const errors = {}; + const errors = []; if (!values.companyName) { - errors.companyName = this.props.translate('bankAccount.error.companyName'); + errors.push({companyName: this.props.translate('bankAccount.error.companyName')}); } if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = this.props.translate('bankAccount.error.addressStreet'); + errors.push({addressStreet: this.props.translate('bankAccount.error.addressStreet')}); } if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = this.props.translate('bankAccount.error.zipCode'); + errors.push({addressZipCode: this.props.translate('bankAccount.error.zipCode')}); } if (!values.addressCity) { - errors.addressCity = this.props.translate('bankAccount.error.addressCity'); + errors.push({addressCity: this.props.translate('bankAccount.error.addressCity')}); } if (!values.addressState) { - errors.addressState = this.props.translate('bankAccount.error.addressState'); + errors.push({addressState: this.props.translate('bankAccount.error.addressState')}); } if (!values.companyPhone || !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { - errors.companyPhone = this.props.translate('bankAccount.error.phoneNumber'); + errors.push({companyPhone: this.props.translate('bankAccount.error.phoneNumber')}); } if (!values.website || !ValidationUtils.isValidWebsite(values.website)) { - errors.website = this.props.translate('bankAccount.error.website'); + errors.push({website: this.props.translate('bankAccount.error.website')}); } if (!values.companyTaxID || !ValidationUtils.isValidTaxID(values.companyTaxID)) { - errors.companyTaxID = this.props.translate('bankAccount.error.taxID'); + errors.push({companyTaxID: this.props.translate('bankAccount.error.taxID')}); } if (!values.incorporationType) { - errors.incorporationType = this.props.translate('bankAccount.error.companyType'); + errors.push({incorporationType: this.props.translate('bankAccount.error.companyType')}); } if (!values.incorporationDate || !ValidationUtils.isValidDate(values.incorporationDate)) { - errors.incorporationDate = this.props.translate('common.error.dateInvalid'); - } else if (!values.incorporationDate || !ValidationUtils.isValidPastDate(values.incorporationDate)) { - errors.incorporationDate = this.props.translate('bankAccount.error.incorporationDateFuture'); + errors.push({incorporationDate: this.props.translate('common.error.dateInvalid')}); + } + + if (!values.incorporationDate || !ValidationUtils.isValidPastDate(values.incorporationDate)) { + errors.push({incorporationDate: this.props.translate('bankAccount.error.incorporationDateFuture')}); } if (!values.incorporationState) { - errors.incorporationState = this.props.translate('bankAccount.error.incorporationState'); + errors.push({incorporationState: this.props.translate('bankAccount.error.incorporationState')}); } if (!values.hasNoConnectionToCannabis) { - errors.hasNoConnectionToCannabis = this.props.translate('bankAccount.error.restrictedBusiness'); + errors.push({hasNoConnectionToCannabis: this.props.translate('bankAccount.error.restrictedBusiness')}); } return errors; diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 92108110ef49..7b728735f590 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -39,50 +39,50 @@ class RequestorStep extends React.Component { * @returns {Object} */ validate(values) { - const errors = {}; + const errors = []; if (!ValidationUtils.isRequiredFulfilled(values.firstName)) { - errors.firstName = this.props.translate('bankAccount.error.firstName'); + errors.push({firstName: this.props.translate('bankAccount.error.firstName')}); } if (!ValidationUtils.isRequiredFulfilled(values.lastName)) { - errors.lastName = this.props.translate('bankAccount.error.lastName'); + errors.push({lastName: this.props.translate('bankAccount.error.lastName')}); } if (!ValidationUtils.isRequiredFulfilled(values.dob)) { - errors.dob = this.props.translate('bankAccount.error.dob'); + errors.push({dob: this.props.translate('bankAccount.error.dob')}); } if (values.dob && !ValidationUtils.meetsAgeRequirements(values.dob)) { - errors.dob = this.props.translate('bankAccount.error.age'); + errors.push({dob: this.props.translate('bankAccount.error.age')}); } if (!ValidationUtils.isRequiredFulfilled(values.ssnLast4) || !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { - errors.ssnLast4 = this.props.translate('bankAccount.error.ssnLast4'); + errors.push({ssnLast4: this.props.translate('bankAccount.error.ssnLast4')}); } if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressStreet)) { - errors.requestorAddressStreet = this.props.translate('bankAccount.error.address'); + errors.push({requestorAddressStreet: this.props.translate('bankAccount.error.address')}); } if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { - errors.requestorAddressStreet = this.props.translate('bankAccount.error.addressStreet'); + errors.push({requestorAddressStreet: this.props.translate('bankAccount.error.addressStreet')}); } if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressCity)) { - errors.requestorAddressCity = this.props.translate('bankAccount.error.addressCity'); + errors.push({requestorAddressCity: this.props.translate('bankAccount.error.addressCity')}); } if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressState)) { - errors.requestorAddressState = this.props.translate('bankAccount.error.addressState'); + errors.push({requestorAddressState: this.props.translate('bankAccount.error.addressState')}); } if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressZipCode) || !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { - errors.requestorAddressZipCode = this.props.translate('bankAccount.error.zipCode'); + errors.push({requestorAddressZipCode: this.props.translate('bankAccount.error.zipCode')}); } if (!ValidationUtils.isRequiredFulfilled(values.isControllingOfficer)) { - errors.isControllingOfficer = this.props.translate('requestorStep.isControllingOfficerError'); + errors.push({isControllingOfficer: this.props.translate('requestorStep.isControllingOfficerError')}); } return errors; diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index 077ee7544029..5c1d660f0b88 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -66,14 +66,14 @@ class ValidationStep extends React.Component { * @returns {Object} */ validate(values) { - const errors = {}; + const errors = []; _.each(values, (value, key) => { const filteredValue = this.filterInput(value); if (ValidationUtils.isRequiredFulfilled(filteredValue)) { return; } - errors[key] = this.props.translate('common.error.invalidAmount'); + errors.push({[key]: this.props.translate('common.error.invalidAmount')}); }); return errors; From b63689858dad0c276098dfdf72dce7ddc5b0cb11 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:05:19 +0100 Subject: [PATCH 08/11] refactor payments component errors --- .../settings/Payments/AddDebitCardPage.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 886601c1475a..919b2b153870 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -70,42 +70,42 @@ class DebitCardPage extends Component { * @returns {Boolean} */ validate(values) { - const errors = {}; + const errors = []; if (!values.nameOnCard || !ValidationUtils.isValidCardName(values.nameOnCard)) { - errors.nameOnCard = this.props.translate('addDebitCardPage.error.invalidName'); + errors.push({nameOnCard: this.props.translate('addDebitCardPage.error.invalidName')}); } if (!values.cardNumber || !ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { - errors.cardNumber = this.props.translate('addDebitCardPage.error.debitCardNumber'); + errors.push({cardNumber: this.props.translate('addDebitCardPage.error.debitCardNumber')}); } if (!values.expirationDate || !ValidationUtils.isValidExpirationDate(values.expirationDate)) { - errors.expirationDate = this.props.translate('addDebitCardPage.error.expirationDate'); + errors.push({expirationDate: this.props.translate('addDebitCardPage.error.expirationDate')}); } if (!values.securityCode || !ValidationUtils.isValidSecurityCode(values.securityCode)) { - errors.securityCode = this.props.translate('addDebitCardPage.error.securityCode'); + errors.push({securityCode: this.props.translate('addDebitCardPage.error.securityCode')}); } if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = this.props.translate('addDebitCardPage.error.addressStreet'); + errors.push({addressStreet: this.props.translate('addDebitCardPage.error.addressStreet')}); } if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = this.props.translate('addDebitCardPage.error.addressZipCode'); + errors.push({addressZipCode: this.props.translate('addDebitCardPage.error.addressZipCode')}); } if (!values.addressState || !values.addressState) { - errors.addressState = this.props.translate('addDebitCardPage.error.addressState'); + errors.push({addressState: this.props.translate('addDebitCardPage.error.addressState')}); } if (!Permissions.canUsePasswordlessLogins(this.props.betas) && (!values.password || _.isEmpty(values.password.trim()))) { - errors.password = this.props.translate('addDebitCardPage.error.password'); + errors.push({password: this.props.translate('addDebitCardPage.error.password')}); } if (!values.acceptTerms) { - errors.acceptTerms = this.props.translate('common.error.acceptTerms'); + errors.push({acceptTerms: this.props.translate('common.error.acceptTerms')}); } return errors; From ce45a488d5892999fa9bb24d87b0389fc0b2ea00 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:05:47 +0100 Subject: [PATCH 09/11] refactor personal details component errors --- .../Profile/PersonalDetails/AddressPage.js | 6 +++--- .../Profile/PersonalDetails/DateOfBirthPage.js | 8 +++++--- .../Profile/PersonalDetails/LegalNamePage.js | 18 +++++++++++------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 848373fe2cc9..5d1f48824977 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -96,7 +96,7 @@ class AddressPage extends Component { * @returns {Object} - An object containing the errors for each inputID */ validate(values) { - const errors = {}; + const errors = []; const requiredFields = [ 'addressLine1', @@ -108,7 +108,7 @@ class AddressPage extends Component { // Check "State" dropdown is a valid state if selected Country is USA. if (this.state.isUsaForm && !COMMON_CONST.STATES[values.state]) { - errors.state = this.props.translate('common.error.fieldRequired'); + errors.push({state: this.props.translate('common.error.fieldRequired')}); } // Add "Field required" errors if any required field is empty @@ -116,7 +116,7 @@ class AddressPage extends Component { if (!_.isEmpty(values[fieldKey])) { return; } - errors[fieldKey] = this.props.translate('common.error.fieldRequired'); + errors.push({[fieldKey]: this.props.translate('common.error.fieldRequired')}); }); return errors; diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index 260152188ff5..c967e71e9419 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -57,16 +57,18 @@ class DateOfBirthPage extends Component { * @returns {Object} - An object containing the errors for each inputID */ validate(values) { - const errors = {}; + const errors = []; const minimumAge = 5; const maximumAge = 150; if (!values.dob || !ValidationUtils.isValidDate(values.dob)) { - errors.dob = this.props.translate('common.error.fieldRequired'); + errors.push({dob: this.props.translate('common.error.fieldRequired')}); } + const dateError = ValidationUtils.getAgeRequirementError(values.dob, minimumAge, maximumAge); + if (dateError) { - errors.dob = dateError; + errors.push({dob: dateError}); } return errors; diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js index dee460123f1e..08a8c5c997fe 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js @@ -64,18 +64,22 @@ class LegalNamePage extends Component { * @returns {Object} - An object containing the errors for each inputID */ validate(values) { - const errors = {}; + const errors = []; if (!ValidationUtils.isValidDisplayName(values.legalFirstName)) { - errors.legalFirstName = this.props.translate('personalDetails.error.hasInvalidCharacter'); - } else if (_.isEmpty(values.legalFirstName)) { - errors.legalFirstName = this.props.translate('common.error.fieldRequired'); + errors.push({legalFirstName: this.props.translate('personalDetails.error.hasInvalidCharacter')}); + } + + if (_.isEmpty(values.legalFirstName)) { + errors.push({legalFirstName: this.props.translate('common.error.fieldRequired')}); } if (!ValidationUtils.isValidDisplayName(values.legalLastName)) { - errors.legalLastName = this.props.translate('personalDetails.error.hasInvalidCharacter'); - } else if (_.isEmpty(values.legalLastName)) { - errors.legalLastName = this.props.translate('common.error.fieldRequired'); + errors.push({legalLastName: this.props.translate('personalDetails.error.hasInvalidCharacter')}); + } + + if (_.isEmpty(values.legalLastName)) { + errors.push({legalLastName: this.props.translate('common.error.fieldRequired')}); } return errors; From 7f9f8dc462034c162acaffe8acb75ca81568bce7 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:06:03 +0100 Subject: [PATCH 10/11] refactor security component errors --- src/pages/settings/Security/CloseAccountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 676f645242ba..6a4a19cee2cd 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -69,10 +69,10 @@ class CloseAccountPage extends Component { validate(values) { const userEmailOrPhone = Str.removeSMSDomain(this.props.session.email); - const errors = {}; + const errors = []; if (_.isEmpty(values.phoneOrEmail) || userEmailOrPhone.toLowerCase() !== values.phoneOrEmail.toLowerCase()) { - errors.phoneOrEmail = this.props.translate('closeAccountPage.enterYourDefaultContactMethod'); + errors.push({phoneOrEmail: this.props.translate('closeAccountPage.enterYourDefaultContactMethod')}); } return errors; } From 727c2a6934b421821ae24c316ba5946aeca9ae35 Mon Sep 17 00:00:00 2001 From: josemak25 Date: Tue, 28 Feb 2023 00:06:26 +0100 Subject: [PATCH 11/11] refactor workspace component errors --- src/pages/AddPersonalBankAccountPage.js | 2 +- src/pages/ReportSettingsPage.js | 10 ++++---- src/pages/RequestCallPage.js | 10 ++++---- src/pages/workspace/WorkspaceNewRoomPage.js | 24 ++++++++++++-------- src/pages/workspace/WorkspaceSettingsPage.js | 6 ++--- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/pages/AddPersonalBankAccountPage.js b/src/pages/AddPersonalBankAccountPage.js index b140d4657801..3397fbc9f24e 100644 --- a/src/pages/AddPersonalBankAccountPage.js +++ b/src/pages/AddPersonalBankAccountPage.js @@ -70,7 +70,7 @@ class AddPersonalBankAccountPage extends React.Component { * @returns {Object} */ validate() { - return {}; + return []; } submit() { diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js index 86fa8b6cec16..99616989519f 100644 --- a/src/pages/ReportSettingsPage.js +++ b/src/pages/ReportSettingsPage.js @@ -83,7 +83,7 @@ class ReportSettingsPage extends Component { * @returns {Boolean} */ validate(values) { - const errors = {}; + const errors = []; // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method if (values.newRoomName === this.props.report.reportName) { @@ -93,16 +93,16 @@ class ReportSettingsPage extends Component { // The following validations are ordered by precedence. // First priority: We error if the user doesn't enter a room name or left blank if (!values.newRoomName || values.newRoomName === CONST.POLICY.ROOM_PREFIX) { - errors.newRoomName = this.props.translate('newRoomPage.pleaseEnterRoomName'); + errors.push({newRoomName: this.props.translate('newRoomPage.pleaseEnterRoomName')}); } else if (ValidationUtils.isReservedRoomName(values.newRoomName)) { // Second priority: Certain names are reserved for default rooms and should not be used for policy rooms. - errors.newRoomName = this.props.translate('newRoomPage.roomNameReservedError'); + errors.push({newRoomName: this.props.translate('newRoomPage.roomNameReservedError')}); } else if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { // Third priority: Show error if the room name already exists - errors.newRoomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); + errors.push({newRoomName: this.props.translate('newRoomPage.roomAlreadyExistsError')}); } else if (!ValidationUtils.isValidRoomName(values.newRoomName)) { // Fourth priority: We error if the room name has invalid characters - errors.newRoomName = this.props.translate('newRoomPage.roomNameInvalidError'); + errors.push({newRoomName: this.props.translate('newRoomPage.roomNameInvalidError')}); } return errors; diff --git a/src/pages/RequestCallPage.js b/src/pages/RequestCallPage.js index 91b6d1e5e0e8..570d1fd729ea 100644 --- a/src/pages/RequestCallPage.js +++ b/src/pages/RequestCallPage.js @@ -209,23 +209,23 @@ class RequestCallPage extends Component { * @returns {Boolean} */ validate(values) { - const errors = {}; + const errors = []; if (_.isEmpty(values.firstName.trim())) { - errors.firstName = this.props.translate('requestCallPage.error.firstName'); + errors.push({firstName: this.props.translate('requestCallPage.error.firstName')}); } if (_.isEmpty(values.lastName.trim())) { - errors.lastName = this.props.translate('requestCallPage.error.lastName'); + errors.push({lastName: this.props.translate('requestCallPage.error.lastName')}); } const phoneNumber = LoginUtils.getPhoneNumberWithoutSpecialChars(values.phoneNumber); if (_.isEmpty(values.phoneNumber.trim()) || !Str.isValidPhone(phoneNumber)) { - errors.phoneNumber = this.props.translate('common.error.phoneNumber'); + errors.push({phoneNumber: this.props.translate('common.error.phoneNumber')}); } if (!_.isEmpty(values.phoneNumberExtension) && !ValidationUtils.isPositiveInteger(values.phoneNumberExtension)) { - errors.phoneNumberExtension = this.props.translate('requestCallPage.error.phoneNumberExtension'); + errors.push({phoneNumberExtension: this.props.translate('requestCallPage.error.phoneNumberExtension')}); } return errors; diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 0cc96e7ce33a..a25efcd8850a 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -80,25 +80,31 @@ class WorkspaceNewRoomPage extends React.Component { * @returns {Boolean} */ validate(values) { - const errors = {}; + const errors = []; // The following validations are ordered by precedence. // First priority: We error if the user doesn't enter a room name or left blank if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { - errors.roomName = this.props.translate('newRoomPage.pleaseEnterRoomName'); - } else if (ValidationUtils.isReservedRoomName(values.roomName)) { + errors.push({roomName: this.props.translate('newRoomPage.pleaseEnterRoomName')}); + } + + if (ValidationUtils.isReservedRoomName(values.roomName)) { // Second priority: Certain names are reserved for default rooms and should not be used for policy rooms. - errors.roomName = this.props.translate('newRoomPage.roomNameReservedError'); - } else if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, values.policyID)) { + errors.push({roomName: this.props.translate('newRoomPage.roomNameReservedError')}); + } + + if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, values.policyID)) { // Third priority: We error if the room name already exists. - errors.roomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); - } else if (!ValidationUtils.isValidRoomName(values.roomName)) { + errors.push({roomName: this.props.translate('newRoomPage.roomAlreadyExistsError')}); + } + + if (!ValidationUtils.isValidRoomName(values.roomName)) { // Fourth priority: We error if the room name has invalid characters - errors.roomName = this.props.translate('newRoomPage.roomNameInvalidError'); + errors.push({roomName: this.props.translate('newRoomPage.roomNameInvalidError')}); } if (!values.policyID) { - errors.policyID = this.props.translate('newRoomPage.pleaseSelectWorkspace'); + errors.push({policyID: this.props.translate('newRoomPage.pleaseSelectWorkspace')}); } return errors; diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index de014170abf9..9c130ee2af94 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -61,16 +61,16 @@ class WorkspaceSettingsPage extends React.Component { } validate(values) { - const errors = {}; + const errors = []; const name = values.name.trim(); // Searches for anything that looks like an html tag "< >"" if (name.search(/<(.|\n)*?>/g) !== -1) { - errors.name = this.props.translate('workspace.editor.nameHasHtml'); + errors.push({name: this.props.translate('workspace.editor.nameHasHtml')}); } if (!name || !name.length) { - errors.name = this.props.translate('workspace.editor.nameIsRequiredError'); + errors.push({name: this.props.translate('workspace.editor.nameIsRequiredError')}); } return errors;