Skip to content

Commit

Permalink
Merge pull request #24379 from chiragxarora/fix/23954
Browse files Browse the repository at this point in the history
setting required field errors in app
  • Loading branch information
bondydaa authored Aug 18, 2023
2 parents 08dd014 + aaf4813 commit 0c35910
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 161 deletions.
19 changes: 19 additions & 0 deletions src/libs/ValidationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ function isRequiredFulfilled(value) {
return Boolean(value);
}

/**
* Used to add requiredField error to the fields passed.
*
* @param {Object} values
* @param {Array} requiredFields
* @returns {Object}
*/
function getFieldRequiredErrors(values, requiredFields) {
const errors = {};
_.each(requiredFields, (fieldKey) => {
if (isRequiredFulfilled(values[fieldKey])) {
return;
}
errors[fieldKey] = 'common.error.fieldRequired';
});
return errors;
}

/**
* Validates that this is a valid expiration date. Supports the following formats:
* 1. MM/YY
Expand Down Expand Up @@ -451,6 +469,7 @@ export {
isValidIndustryCode,
isValidZipCode,
isRequiredFulfilled,
getFieldRequiredErrors,
isValidUSPhone,
isValidWebsite,
validateIdentity,
Expand Down
109 changes: 38 additions & 71 deletions src/pages/EnablePayments/AdditionalDetailsStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import TextLink from '../../components/TextLink';
import TextInput from '../../components/TextInput';
import * as Wallet from '../../libs/actions/Wallet';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as ErrorUtils from '../../libs/ErrorUtils';
import AddressForm from '../ReimbursementAccount/AddressForm';
import DatePicker from '../../components/DatePicker';
import Form from '../../components/Form';
Expand Down Expand Up @@ -70,29 +69,6 @@ const defaultProps = {
...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',
},
};
const errorTranslationKeys = {
legalFirstName: 'bankAccount.error.firstName',
legalLastName: 'bankAccount.error.lastName',
phoneNumber: 'bankAccount.error.phoneNumber',
dob: 'bankAccount.error.dob',
age: 'bankAccount.error.age',
ssn: 'bankAccount.error.ssnLast4',
ssnFull9: 'additionalDetailsStep.ssnFull9Error',
};

const fieldNameTranslationKeys = {
legalFirstName: 'additionalDetailsStep.legalFirstNameLabel',
legalLastName: 'additionalDetailsStep.legalLastNameLabel',
Expand All @@ -113,50 +89,37 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
* @returns {Object}
*/
const validate = (values) => {
const errors = {};

if (_.isEmpty(values[INPUT_IDS.LEGAL_FIRST_NAME])) {
errors[INPUT_IDS.LEGAL_FIRST_NAME] = errorTranslationKeys.legalFirstName;
}

if (_.isEmpty(values[INPUT_IDS.LEGAL_LAST_NAME])) {
errors[INPUT_IDS.LEGAL_LAST_NAME] = errorTranslationKeys.legalLastName;
}

if (!ValidationUtils.isValidPastDate(values[INPUT_IDS.DOB]) || !ValidationUtils.meetsMaximumAgeRequirement(values[INPUT_IDS.DOB])) {
ErrorUtils.addErrorMessage(errors, INPUT_IDS.DOB, errorTranslationKeys.dob);
} else if (!ValidationUtils.meetsMinimumAgeRequirement(values[INPUT_IDS.DOB])) {
ErrorUtils.addErrorMessage(errors, INPUT_IDS.DOB, errorTranslationKeys.age);
}
const requiredFields = ['legalFirstName', 'legalLastName', 'addressStreet', 'addressCity', 'addressZipCode', 'phoneNumber', 'dob', 'ssn', 'addressState'];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);

if (!ValidationUtils.isValidAddress(values[INPUT_IDS.ADDRESS.street]) || _.isEmpty(values[INPUT_IDS.ADDRESS.street])) {
errors[INPUT_IDS.ADDRESS.street] = 'bankAccount.error.addressStreet';
}

if (_.isEmpty(values[INPUT_IDS.ADDRESS.city])) {
errors[INPUT_IDS.ADDRESS.city] = 'bankAccount.error.addressCity';
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 (_.isEmpty(values[INPUT_IDS.ADDRESS.state])) {
errors[INPUT_IDS.ADDRESS.state] = 'bankAccount.error.addressState';
if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) {
errors.addressStreet = 'bankAccount.error.addressStreet';
}

if (!ValidationUtils.isValidZipCode(values[INPUT_IDS.ADDRESS.zipCode])) {
errors[INPUT_IDS.ADDRESS.zipCode] = 'bankAccount.error.zipCode';
if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) {
errors.addressZipCode = 'bankAccount.error.zipCode';
}

if (!ValidationUtils.isValidUSPhone(values[INPUT_IDS.PHONE_NUMBER], true)) {
errors[INPUT_IDS.PHONE_NUMBER] = errorTranslationKeys.phoneNumber;
if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) {
errors.phoneNumber = 'bankAccount.error.phoneNumber';
}

// 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 (walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN) {
if (!ValidationUtils.isValidSSNFullNine(values[INPUT_IDS.SSN])) {
errors[INPUT_IDS.SSN] = errorTranslationKeys.ssnFull9;
if (values.ssn && !ValidationUtils.isValidSSNFullNine(values.ssn)) {
errors.ssn = 'additionalDetailsStep.ssnFull9Error';
}
} else if (!ValidationUtils.isValidSSNLastFour(values[INPUT_IDS.SSN])) {
errors[INPUT_IDS.SSN] = errorTranslationKeys.ssn;
} else if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) {
errors.ssn = 'bankAccount.error.ssnLast4';
}

return errors;
Expand All @@ -167,17 +130,16 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
*/
const activateWallet = (values) => {
const personalDetails = {
phoneNumber: parsePhoneNumber(values[INPUT_IDS.PHONE_NUMBER], {regionCode: CONST.COUNTRY.US}).number.significant,
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: values[INPUT_IDS.DOB],
ssn: values[INPUT_IDS.SSN],
phoneNumber: parsePhoneNumber(values.phoneNumber, {regionCode: CONST.COUNTRY.US}).number.significant,
legalFirstName: values.legalFirstName,
legalLastName: values.legalLastName,
addressStreet: values.addressStreet,
addressCity: values.addressCity,
addressState: values.addressState,
addressZip: values.addressZipCode,
dob: values.dob,
ssn: values.ssn,
};

// Attempt to set the personal details
Wallet.updatePersonalDetails(personalDetails);
};
Expand Down Expand Up @@ -222,7 +184,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
style={[styles.mh5, styles.flexGrow1]}
>
<TextInput
inputID={INPUT_IDS.LEGAL_FIRST_NAME}
inputID="legalFirstName"
containerStyles={[styles.mt4]}
label={translate(fieldNameTranslationKeys.legalFirstName)}
accessibilityLabel={translate(fieldNameTranslationKeys.legalFirstName)}
Expand All @@ -231,7 +193,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
shouldSaveDraft
/>
<TextInput
inputID={INPUT_IDS.LEGAL_LAST_NAME}
inputID="legalLastName"
containerStyles={[styles.mt4]}
label={translate(fieldNameTranslationKeys.legalLastName)}
accessibilityLabel={translate(fieldNameTranslationKeys.legalLastName)}
Expand All @@ -240,13 +202,18 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
shouldSaveDraft
/>
<AddressForm
inputKeys={INPUT_IDS.ADDRESS}
inputKeys={{
street: 'addressStreet',
city: 'addressCity',
state: 'addressState',
zipCode: 'addressZipCode',
}}
translate={translate}
streetTranslationKey={fieldNameTranslationKeys.addressStreet}
shouldSaveDraft
/>
<TextInput
inputID={INPUT_IDS.PHONE_NUMBER}
inputID="phoneNumber"
containerStyles={[styles.mt4]}
keyboardType={CONST.KEYBOARD_TYPE.PHONE_PAD}
label={translate(fieldNameTranslationKeys.phoneNumber)}
Expand All @@ -257,7 +224,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
shouldSaveDraft
/>
<DatePicker
inputID={INPUT_IDS.DOB}
inputID="dob"
containerStyles={[styles.mt4]}
label={translate(fieldNameTranslationKeys.dob)}
placeholder={translate('common.dob')}
Expand All @@ -266,7 +233,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP
shouldSaveDraft
/>
<TextInput
inputID={INPUT_IDS.SSN}
inputID="ssn"
containerStyles={[styles.mt4]}
label={translate(fieldNameTranslationKeys[shouldAskForFullSSN ? 'ssnFull9' : 'ssn'])}
accessibilityLabel={translate(fieldNameTranslationKeys[shouldAskForFullSSN ? 'ssnFull9' : 'ssn'])}
Expand Down
22 changes: 12 additions & 10 deletions src/pages/ReimbursementAccount/BankAccountManualStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,27 @@ function BankAccountManualStep(props) {
*/
const validate = useCallback(
(values) => {
const errorFields = {};
const requiredFields = ['routingNumber', 'accountNumber'];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);
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()))
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 = 'bankAccount.error.accountNumber';
} else if (values.accountNumber === routingNumber) {
errorFields.accountNumber = translate('bankAccount.error.routingAndAccountNumberCannotBeSame');
errors.accountNumber = 'bankAccount.error.accountNumber';
} else if (values.accountNumber && values.accountNumber === routingNumber) {
errors.accountNumber = translate('bankAccount.error.routingAndAccountNumberCannotBeSame');
}
if (!routingNumber || !CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber)) {
errorFields.routingNumber = 'bankAccount.error.routingNumber';
if (routingNumber && (!CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber))) {
errors.routingNumber = 'bankAccount.error.routingNumber';
}
if (!values.acceptTerms) {
errorFields.acceptTerms = 'common.error.acceptTerms';
errors.acceptTerms = 'common.error.acceptTerms';
}

return errorFields;
return errors;
},
[translate],
);
Expand Down
49 changes: 21 additions & 28 deletions src/pages/ReimbursementAccount/CompanyStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,54 +80,47 @@ 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 requiredFields = [
'companyName',
'addressStreet',
'addressZipCode',
'addressCity',
'addressState',
'companyPhone',
'website',
'companyTaxID',
'incorporationType',
'incorporationDate',
'incorporationState',
];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);

if (!values.companyName) {
errors.companyName = 'bankAccount.error.companyName';
}

if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) {
if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) {
errors.addressStreet = 'bankAccount.error.addressStreet';
}

if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) {
if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) {
errors.addressZipCode = 'bankAccount.error.zipCode';
}

if (!values.addressCity) {
errors.addressCity = 'bankAccount.error.addressCity';
}

if (!values.addressState) {
errors.addressState = 'bankAccount.error.addressState';
}

if (!values.companyPhone || !ValidationUtils.isValidUSPhone(values.companyPhone, true)) {
if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) {
errors.companyPhone = 'bankAccount.error.phoneNumber';
}

if (!values.website || !ValidationUtils.isValidWebsite(values.website)) {
if (values.website && !ValidationUtils.isValidWebsite(values.website)) {
errors.website = 'bankAccount.error.website';
}

if (!values.companyTaxID || !ValidationUtils.isValidTaxID(values.companyTaxID)) {
if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) {
errors.companyTaxID = 'bankAccount.error.taxID';
}

if (!values.incorporationType) {
errors.incorporationType = 'bankAccount.error.companyType';
}

if (!values.incorporationDate || !ValidationUtils.isValidDate(values.incorporationDate)) {
if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) {
errors.incorporationDate = 'common.error.dateInvalid';
} else if (!values.incorporationDate || !ValidationUtils.isValidPastDate(values.incorporationDate)) {
} else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) {
errors.incorporationDate = 'bankAccount.error.incorporationDateFuture';
}

if (!values.incorporationState) {
errors.incorporationState = 'bankAccount.error.incorporationState';
}

if (!values.hasNoConnectionToCannabis) {
errors.hasNoConnectionToCannabis = 'bankAccount.error.restrictedBusiness';
}
Expand Down
37 changes: 7 additions & 30 deletions src/pages/ReimbursementAccount/RequestorStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,49 +38,26 @@ class RequestorStep extends React.Component {
* @returns {Object}
*/
validate(values) {
const errors = {};

if (!ValidationUtils.isRequiredFulfilled(values.firstName)) {
errors.firstName = 'bankAccount.error.firstName';
}

if (!ValidationUtils.isRequiredFulfilled(values.lastName)) {
errors.lastName = 'bankAccount.error.lastName';
}

if (!ValidationUtils.isRequiredFulfilled(values.dob)) {
errors.dob = 'bankAccount.error.dob';
}
const requiredFields = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode'];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);

if (values.dob) {
if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) {
errors.dob = 'bankAccount.error.age';
} else if (!ValidationUtils.meetsMaximumAgeRequirement(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.ssnLast4) || !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) {
if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) {
errors.ssnLast4 = 'bankAccount.error.ssnLast4';
}

if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressStreet)) {
errors.requestorAddressStreet = 'bankAccount.error.address';
}

if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) {
errors.requestorAddressStreet = 'bankAccount.error.addressStreet';
}

if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressCity)) {
errors.requestorAddressCity = 'bankAccount.error.addressCity';
}

if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressState)) {
errors.requestorAddressState = 'bankAccount.error.addressState';
}

if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressZipCode) || !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) {
if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) {
errors.requestorAddressZipCode = 'bankAccount.error.zipCode';
}

Expand Down
Loading

0 comments on commit 0c35910

Please sign in to comment.