From 4dd5ccfff06f1b7a3ef5581a9189d2d40c0d7c88 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 21 Jun 2021 11:47:22 -1000 Subject: [PATCH 1/7] move validation utils over --- src/CONST.js | 5 ++ src/languages/en.js | 1 + src/languages/es.js | 1 + src/libs/ValidationUtils.js | 86 +++++++++++++++++++ .../ReimbursementAccount/IdentityForm.js | 1 + .../ReimbursementAccount/RequestorStep.js | 35 +++++++- 6 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 src/libs/ValidationUtils.js diff --git a/src/CONST.js b/src/CONST.js index 8e047d087b5a..c991e26ea59e 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -284,6 +284,11 @@ const CONST = { US_PHONE: /^\+1\d{10}$/, PHONE_E164_PLUS: /^\+?[1-9]\d{1,14}$/, NON_ALPHA_NUMERIC: /[^A-Za-z0-9+]/g, + PO_BOX: /\\b[P|p]?(OST|ost)?\\.?\\s*[O|o|0]?(ffice|FFICE)?\\.?\\s*[B|b][O|o|0]?[X|x]?\\.?\\s+[#]?(\\d+)\\b/, + ANY_VALUE: /^.+$/, + ZIP_CODE: /[0-9]{5}(?:[- ][0-9]{4})?/, + INDUSTRY_CODE: /^[0-9]{6}$/, + SSN_LAST_FOUR: /[0-9]{4}/, }, GROWL: { diff --git a/src/languages/en.js b/src/languages/en.js index 5d6d6ddbb897..fddb95bb5cf3 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -49,6 +49,7 @@ export default { invite: 'Invite', iAcceptThe: 'I accept the ', passwordCannotBeBlank: 'Password cannot be blank', + dateFormat: 'YYYY-MM-DD', }, attachmentPicker: { cameraPermissionRequired: 'Camera Permission Required', diff --git a/src/languages/es.js b/src/languages/es.js index 8a0fd6565c1e..283628a37d7f 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -45,6 +45,7 @@ export default { invite: 'Invitar', iAcceptThe: 'Acepto los ', passwordCannotBeBlank: 'La contraseña no puede estar vacía', + dateFormat: 'AAAA-MM-DD', }, attachmentPicker: { cameraPermissionRequired: 'Se necesita permiso para usar la cámara', diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js new file mode 100644 index 000000000000..604ace0e08cb --- /dev/null +++ b/src/libs/ValidationUtils.js @@ -0,0 +1,86 @@ +import moment from 'moment'; +import CONST from '../CONST'; +import Growl from './Growl'; + +/** + * Validating that this is a valid address (PO boxes are not allowed) + * + * @param {String} value + * @returns {Boolean} + */ +function isValidAddress(value) { + if (!CONST.REGEX.ANY_VALUE.test(value)) { + return false; + } + + return !CONST.REGEX.PO_BOX.test(value); +} + +/** + * Validate date fields + * + * @param {String} date + * @returns {Boolean} true if valid + */ +function isValidDate(date) { + return moment(date).isValid(); +} + +/** + * @param {String} code + * @returns {Boolean} + */ +function isValidIndustryCode(code) { + return CONST.REGEX.INDUSTRY_CODE.test(code); +} + +/** + * @param {String} zipCode + * @returns {Boolean} + */ +function isValidZipCode(zipCode) { + return CONST.REGEX.ZIP_CODE.test(zipCode); +} + +/** + * @param {String} ssnLast4 + * @returns {Boolean} + */ +function isValidSSNLastFour(ssnLast4) { + return CONST.REGEX.SSN_LAST_FOUR.test(ssnLast4); +} + +/** + * @param {Object} identity + * @returns {Boolean} + */ +function isValidIdentity(identity) { + if (!isValidAddress(identity.street)) { + Growl.error('Please enter a valid address'); + return false; + } + + if (!isValidZipCode(identity.zipCode)) { + Growl.error('Please enter a valid zip code'); + return false; + } + + if (!isValidDate(identity.dob)) { + Growl.error('Please enter a valid date of birth'); + return false; + } + + if (!isValidSSNLastFour(identity.ssnLast4)) { + Growl.error('Please enter a valid SSN (last 4)'); + return false; + } + + return true; +} + +export { + isValidAddress, + isValidDate, + isValidIndustryCode, + isValidIdentity, +}; diff --git a/src/pages/ReimbursementAccount/IdentityForm.js b/src/pages/ReimbursementAccount/IdentityForm.js index db2ba800de25..d0506e5a06b6 100644 --- a/src/pages/ReimbursementAccount/IdentityForm.js +++ b/src/pages/ReimbursementAccount/IdentityForm.js @@ -85,6 +85,7 @@ const IdentityForm = ({ onFieldChange('dob', val)} /> diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 4147c7b3999d..f256cade8177 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -12,11 +12,15 @@ import {goToWithdrawalAccountSetupStep} from '../../libs/actions/BankAccounts'; import Button from '../../components/Button'; import FixedFooter from '../../components/FixedFooter'; import IdentityForm from './IdentityForm'; +import {isValidIdentity} from '../../libs/ValidationUtils'; +import Growl from '../../libs/Growl'; class RequestorStep extends React.Component { constructor(props) { super(props); + this.submit = this.submit.bind(this); + this.state = { firstName: '', lastName: '', @@ -26,9 +30,31 @@ class RequestorStep extends React.Component { zipCode: '', dob: '', ssnLast4: '', + isControllingOfficer: false, }; } + validate() { + if (!this.state.isControllingOfficer) { + Growl.error('Please verify that you are authorized to use this bank account for business spend'); + return false; + } + + if (!isValidIdentity({...this.state})) { + return false; + } + + return true; + } + + submit() { + if (!this.validate()) { + return; + } + + console.log('magic'); + } + render() { return ( <> @@ -54,8 +80,10 @@ class RequestorStep extends React.Component { }} /> {}} + isChecked={this.state.isControllingOfficer} + onPress={() => { + this.setState(prevState => ({isControllingOfficer: !prevState.isControllingOfficer})); + }} LabelComponent={() => ( @@ -111,8 +139,7 @@ class RequestorStep extends React.Component {