diff --git a/src/CONST.ts b/src/CONST.ts index 95fc36930f52..3ca22790b686 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -180,6 +180,22 @@ const CONST = { ZIP_CODE: 'requestorAddressZipCode', }, }, + BUSINESS_INFO_STEP: { + INPUT_KEY: { + COMPANY_NAME: 'companyName', + COMPANY_TAX_ID: 'companyTaxID', + COMPANY_WEBSITE: 'website', + COMPANY_PHONE: 'companyPhone', + STREET: 'addressStreet', + CITY: 'addressCity', + STATE: 'addressState', + ZIP_CODE: 'addressZipCode', + INCORPORATION_TYPE: 'incorporationType', + INCORPORATION_DATE: 'incorporationDate', + INCORPORATION_STATE: 'incorporationState', + HAS_NO_CONNECTION_TO_CANNABIS: 'hasNoConnectionToCannabis', + }, + }, PLAID: { ALLOWED_THROTTLED_COUNT: 2, ERROR: { @@ -481,6 +497,7 @@ const CONST = { 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/', + LIST_OF_RESTRICTED_BUSINESSES: 'https://community.expensify.com/discussion/6191/list-of-restricted-businesses', // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', diff --git a/src/components/Form/FormWrapper.js b/src/components/Form/FormWrapper.js index eaa0bab91e1e..2c335d0c9c2a 100644 --- a/src/components/Form/FormWrapper.js +++ b/src/components/Form/FormWrapper.js @@ -169,7 +169,6 @@ function FormWrapper(props) { isSubmitActionDangerous, isSubmitButtonVisible, onSubmit, - submitButtonStyles, style, submitButtonStyles, submitButtonText, diff --git a/src/components/StatePicker/index.js b/src/components/StatePicker/index.js index c36c7e6b97a0..0d5e50f7d629 100644 --- a/src/components/StatePicker/index.js +++ b/src/components/StatePicker/index.js @@ -7,6 +7,7 @@ import FormHelpMessage from '@components/FormHelpMessage'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import refPropTypes from '@components/refPropTypes'; import useLocalize from '@hooks/useLocalize'; +import stylePropTypes from '@styles/stylePropTypes'; import styles from '@styles/styles'; import StateSelectorModal from './StateSelectorModal'; @@ -25,6 +26,9 @@ const propTypes = { /** Label to display on field */ label: PropTypes.string, + + /** Any additional styles to apply */ + wrapperStyle: stylePropTypes, }; const defaultProps = { @@ -33,9 +37,10 @@ const defaultProps = { errorText: '', onInputChange: () => {}, label: undefined, + wrapperStyle: {}, }; -function StatePicker({value, errorText, onInputChange, forwardedRef, label}) { +function StatePicker({value, errorText, onInputChange, forwardedRef, label, wrapperStyle}) { const {translate} = useLocalize(); const [isPickerVisible, setIsPickerVisible] = useState(false); const [searchValue, setSearchValue] = useState(''); @@ -67,6 +72,7 @@ function StatePicker({value, errorText, onInputChange, forwardedRef, label}) { description={label || translate('common.state')} descriptionTextStyle={descStyle} onPress={showPickerModal} + wrapperStyle={wrapperStyle} /> diff --git a/src/hooks/useSubStep.js b/src/hooks/useSubStep.js index bfd88f5d5c57..e1954080687a 100644 --- a/src/hooks/useSubStep.js +++ b/src/hooks/useSubStep.js @@ -2,13 +2,13 @@ import PropTypes from 'prop-types'; import {useCallback, useRef, useState} from 'react'; const propTypes = { - /** an array of substep components */ + /** An array of substep components */ bodyContent: PropTypes.arrayOf(PropTypes.element).isRequired, - /** an index of the component from bodyContent array to start from */ + /** An index of the component from bodyContent array to start from */ onFinished: PropTypes.func.isRequired, - /** a callback to be fired when pressing Confirm on the last substep screen */ + /** A callback to be fired when pressing Confirm on the last substep screen */ startFrom: PropTypes.number, }; diff --git a/src/languages/en.ts b/src/languages/en.ts index 68057c24e1fa..ac3d9097d5a5 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1365,9 +1365,40 @@ export default { last4SSN: 'Last 4 Social Security Number', enterYourAddress: 'Enter your address.', address: 'Address', - letsDoubleCheck: "Let's double check everything looks right", + letsDoubleCheck: "Let's double check that everything looks right", byAddingThisBankAccount: 'By adding this bank account, you confirm that you have read, understand and accept', }, + businessInfoStep: { + businessInfo: 'Business info', + enterTheNameOfYourBusiness: 'Enter the name of your business.', + businessName: 'Legal business name', + enterYourCompanysTaxIdNumber: 'Enter your company’s Tax ID number.', + taxIDNumber: 'Tax ID number', + taxIDNumberPlaceholder: '9 digits', + enterYourCompanysWebsite: 'Enter your company’s website.', + companyWebsite: 'Company website', + enterYourCompanysPhoneNumber: 'Enter your company’s phone number.', + enterYourCompanysAddress: 'Enter your company’s address.', + selectYourCompanysType: 'Select your company’s type.', + companyType: 'Company type', + incorporationType: { + LLC: 'LLC', + CORPORATION: 'Corp', + PARTNERSHIP: 'Partnership', + COOPERATIVE: 'Cooperative', + SOLE_PROPRIETORSHIP: 'Sole proprietorship', + OTHER: 'Other', + }, + selectYourCompanysIncorporationDate: 'Select your company’s incorporation date.', + incorporationDate: 'Incorporation date', + incorporationDatePlaceholder: 'Start date (yyyy-mm-dd)', + incorporationState: 'Incorporation state', + pleaseSelectTheStateYourCompanyWasIncorporatedIn: 'Please select the state your company was incorporated in.', + letsDoubleCheck: "Let's double check that everything looks right", + companyAddress: 'Company address', + listOfRestrictedBusinesses: 'list of restricted businesses', + confirmCompanyIsNot: 'I confirm that this company is not on the', + }, validationStep: { headerTitle: 'Validate Bank Account', buttonText: 'Finish setup', diff --git a/src/languages/es.ts b/src/languages/es.ts index 33daceae2dcb..7feac8c5c200 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1387,6 +1387,37 @@ export default { letsDoubleCheck: 'Revisemos que todo esté bien', byAddingThisBankAccount: 'Agregando esta cuenta bancaria, confirmas que as leído, entendido y aceptado', }, + businessInfoStep: { + businessInfo: 'Información de Negocio', + enterTheNameOfYourBusiness: 'Introduzca el nombre de su negocio.', + businessName: 'Nombre del Negocio', + enterYourCompanysTaxIdNumber: 'Introduzca el número de identificación fiscal.', + taxIDNumber: 'Número de identificación fiscal', + taxIDNumberPlaceholder: '9 dígitos', + enterYourCompanysWebsite: 'Introduzca el sitio web de su compañia.', + companyWebsite: 'Sitio web de la compañia', + enterYourCompanysPhoneNumber: 'Introduzca el número de teléfono de su compañia.', + enterYourCompanysAddress: 'Introduzca el la dirección de su compañia.', + selectYourCompanysType: 'Seleccione el tipo de compañia.', + companyType: 'Tipo de compañia', + incorporationType: { + LLC: 'SRL', + CORPORATION: 'Corporación', + PARTNERSHIP: 'Asociación', + COOPERATIVE: 'Cooperativa', + SOLE_PROPRIETORSHIP: 'Empresa unipersonal', + OTHER: 'Otros', + }, + selectYourCompanysIncorporationDate: 'Seleccione la fecha de constitución de su empresa.', + incorporationDate: 'Fecha de constitución', + incorporationDatePlaceholder: 'Fecha de inicio (yyyy-mm-dd)', + incorporationState: 'Estado de constitución', + pleaseSelectTheStateYourCompanyWasIncorporatedIn: 'Seleccione el estado en el que se constituyó su empresa.', + letsDoubleCheck: 'Verifiquemos que todo esté correcto', + companyAddress: 'Dirección de la empresa', + listOfRestrictedBusinesses: 'lista de negocios restringidos', + confirmCompanyIsNot: 'Confirmo que esta empresa no está en la', + }, validationStep: { headerTitle: 'Validar cuenta bancaria', buttonText: 'Finalizar configuración', diff --git a/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.js b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.js new file mode 100644 index 000000000000..af458b903b6d --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.js @@ -0,0 +1,152 @@ +import {parsePhoneNumber} from 'awesome-phonenumber'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useSubStep from '@hooks/useSubStep'; +import Navigation from '@libs/Navigation/Navigation'; +import reimbursementAccountDraftPropTypes from '@pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes'; +import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import getInitialSubstepForBusinessInfo from '@pages/ReimbursementAccount/utils/getInitialSubstepForBusinessInfo'; +import getSubstepValues from '@pages/ReimbursementAccount/utils/getSubstepValues'; +import styles from '@styles/styles'; +import * as BankAccounts from '@userActions/BankAccounts'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import AddressBusiness from './substeps/AddressBusiness'; +import ConfirmationBusiness from './substeps/ConfirmationBusiness'; +import IncorporationDateBusiness from './substeps/IncorporationDateBusiness'; +import IncorporationStateBusiness from './substeps/IncorporationStateBusiness'; +import NameBusiness from './substeps/NameBusiness'; +import PhoneNumberBusiness from './substeps/PhoneNumberBusiness'; +import TaxIdBusiness from './substeps/TaxIdBusiness'; +import TypeBusiness from './substeps/TypeBusiness'; +import WebsiteBusiness from './substeps/WebsiteBusiness'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + /** The draft values of the bank account being setup */ + reimbursementAccountDraft: reimbursementAccountDraftPropTypes, + + /* The workspace policyID */ + policyID: PropTypes.string, +}; + +const defaultProps = { + reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, + reimbursementAccountDraft: {}, + policyID: '', +}; + +const STEPS_HEADER_HEIGHT = 40; +// TODO Will most likely come from different place +const STEP_NAMES = ['1', '2', '3', '4', '5']; + +const bodyContent = [ + NameBusiness, + TaxIdBusiness, + WebsiteBusiness, + PhoneNumberBusiness, + AddressBusiness, + TypeBusiness, + IncorporationDateBusiness, + IncorporationStateBusiness, + ConfirmationBusiness, +]; + +const businessInfoStepKeys = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY; + +function BusinessInfo({reimbursementAccount, reimbursementAccountDraft, policyID}) { + const {translate} = useLocalize(); + + /** + * @param {Array} fieldNames + * + * @returns {*} + */ + const getBankAccountFields = useCallback( + (fieldNames) => ({ + ..._.pick(lodashGet(reimbursementAccount, 'achData'), ...fieldNames), + ..._.pick(reimbursementAccountDraft, ...fieldNames), + }), + [reimbursementAccount, reimbursementAccountDraft], + ); + + const values = useMemo(() => getSubstepValues(businessInfoStepKeys, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); + + const submit = useCallback(() => { + const payload = { + bankAccountID: getDefaultStateForField({reimbursementAccount, fieldName: 'bankAccountID', defaultValue: 0}), + ...values, + ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), + companyTaxID: values.companyTaxID.replace(CONST.REGEX.NON_NUMERIC, ''), + companyPhone: parsePhoneNumber(values.companyPhone, {regionCode: CONST.COUNTRY.US}).number.significant, + }; + + BankAccounts.updateCompanyInformationForBankAccount(payload, policyID); + }, [reimbursementAccount, values, getBankAccountFields, policyID]); + + const startFrom = useMemo(() => getInitialSubstepForBusinessInfo(values), [values]); + + const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent, startFrom, onFinished: submit}); + + const handleBackButtonPress = () => { + if (screenIndex === 0) { + Navigation.goBack(ROUTES.HOME); + } else { + prevScreen(); + } + }; + + return ( + + + + {}} + // TODO Will be replaced with proper values + startStep={2} + stepNames={STEP_NAMES} + /> + + + + ); +} + +BusinessInfo.propTypes = propTypes; +BusinessInfo.defaultProps = defaultProps; +BusinessInfo.displayName = 'BusinessInfo'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + reimbursementAccountDraft: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, + }, +})(BusinessInfo); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.js new file mode 100644 index 000000000000..80a43e75292b --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.js @@ -0,0 +1,91 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import AddressForm from '@pages/ReimbursementAccount/AddressForm'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyBusinessInfoKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY; + +const INPUT_KEYS = { + street: companyBusinessInfoKey.STREET, + city: companyBusinessInfoKey.CITY, + state: companyBusinessInfoKey.STATE, + zipCode: companyBusinessInfoKey.ZIP_CODE, +}; + +const REQUIRED_FIELDS = [companyBusinessInfoKey.STREET, companyBusinessInfoKey.CITY, companyBusinessInfoKey.STATE, companyBusinessInfoKey.ZIP_CODE]; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); + + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = 'bankAccount.error.addressStreet'; + } + + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = 'bankAccount.error.zipCode'; + } + + return errors; +}; + +function AddressBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultValues = { + street: getDefaultStateForField({reimbursementAccount, fieldName: companyBusinessInfoKey.STREET, defaultValue: ''}), + city: getDefaultStateForField({reimbursementAccount, fieldName: companyBusinessInfoKey.CITY, defaultValue: ''}), + state: getDefaultStateForField({reimbursementAccount, fieldName: companyBusinessInfoKey.STATE, defaultValue: ''}), + zipCode: getDefaultStateForField({reimbursementAccount, fieldName: companyBusinessInfoKey.ZIP_CODE, defaultValue: ''}), + }; + + return ( +
+ {translate('businessInfoStep.enterYourCompanysAddress')} + {translate('common.noPO')} + + + ); +} + +AddressBusiness.propTypes = propTypes; +AddressBusiness.defaultProps = defaultProps; +AddressBusiness.displayName = 'AddressBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(AddressBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.js new file mode 100644 index 000000000000..f6c82a53e884 --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.js @@ -0,0 +1,185 @@ +import React, {useMemo} from 'react'; +import {ScrollView, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import DotIndicatorMessage from '@components/DotIndicatorMessage'; +import Form from '@components/Form'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import reimbursementAccountDraftPropTypes from '@pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes'; +import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import getSubstepValues from '@pages/ReimbursementAccount/utils/getSubstepValues'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + /** The draft values of the bank account being setup */ + reimbursementAccountDraft: reimbursementAccountDraftPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, + reimbursementAccountDraft: {}, +}; + +const businessInfoStepKeys = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [businessInfoStepKeys.HAS_NO_CONNECTION_TO_CANNABIS]); + + if (!values.hasNoConnectionToCannabis) { + errors.hasNoConnectionToCannabis = 'bankAccount.error.restrictedBusiness'; + } + + return errors; +}; + +function ConfirmationBusiness({reimbursementAccount, reimbursementAccountDraft, onNext, onMove}) { + const {translate} = useLocalize(); + + const values = useMemo(() => getSubstepValues(businessInfoStepKeys, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); + + const error = ErrorUtils.getLatestErrorMessage(reimbursementAccount); + + const defaultCheckboxState = getDefaultStateForField({reimbursementAccount, fieldName: businessInfoStepKeys.HAS_NO_CONNECTION_TO_CANNABIS, defaultValue: false}); + + return ( + + + {translate('businessInfoStep.letsDoubleCheck')} + { + onMove(0); + }} + /> + { + onMove(1); + }} + /> + { + onMove(4); + }} + /> + { + onMove(3); + }} + /> + { + onMove(2); + }} + /> + { + onMove(5); + }} + /> + { + onMove(6); + }} + /> + { + onMove(7); + }} + /> +
+ ( + + {`${translate('businessInfoStep.confirmCompanyIsNot')} `} + + {`${translate('businessInfoStep.listOfRestrictedBusinesses')}.`} + + + )} + style={[styles.mt4]} + shouldSaveDraft + onInputChange={() => {}} + /> + + + {error.length > 0 && ( + + )} + +
+
+ ); +} + +ConfirmationBusiness.propTypes = propTypes; +ConfirmationBusiness.defaultProps = defaultProps; +ConfirmationBusiness.displayName = 'ConfirmationBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + reimbursementAccountDraft: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, + }, +})(ConfirmationBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.js new file mode 100644 index 000000000000..009bf2fd609d --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.js @@ -0,0 +1,88 @@ +import lodashGet from 'lodash/get'; +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import NewDatePicker from '@components/NewDatePicker'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import reimbursementAccountDraftPropTypes from '@pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes'; +import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + /** The draft values of the bank account being setup */ + reimbursementAccountDraft: reimbursementAccountDraftPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, + reimbursementAccountDraft: {}, +}; + +const companyIncorporationDateKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.INCORPORATION_DATE; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [companyIncorporationDateKey]); + + if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { + errors.incorporationDate = 'common.error.dateInvalid'; + } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { + errors.incorporationDate = 'bankAccount.error.incorporationDateFuture'; + } + + return errors; +}; + +function IncorporationDateBusiness({reimbursementAccount, reimbursementAccountDraft, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultCompanyIncorporationDate = + getDefaultStateForField({reimbursementAccount, fieldName: companyIncorporationDateKey, defaultValue: ''}) || lodashGet(reimbursementAccountDraft, companyIncorporationDateKey, ''); + + return ( + + {translate('businessInfoStep.selectYourCompanysIncorporationDate')} + + + ); +} + +IncorporationDateBusiness.propTypes = propTypes; +IncorporationDateBusiness.defaultProps = defaultProps; +IncorporationDateBusiness.displayName = 'IncorporationDateBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + reimbursementAccountDraft: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, + }, +})(IncorporationDateBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationStateBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationStateBusiness.js new file mode 100644 index 000000000000..7a8ccf3bfecb --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationStateBusiness.js @@ -0,0 +1,64 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import StatePicker from '@components/StatePicker'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyIncorporationStateKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.INCORPORATION_STATE; + +const validate = (values) => ValidationUtils.getFieldRequiredErrors(values, [companyIncorporationStateKey]); + +function IncorporationStateBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultCompanyIncorporationState = getDefaultStateForField({reimbursementAccount, fieldName: companyIncorporationStateKey, defaultValue: ''}); + + return ( +
+ {translate('businessInfoStep.pleaseSelectTheStateYourCompanyWasIncorporatedIn')} + + + ); +} + +IncorporationStateBusiness.propTypes = propTypes; +IncorporationStateBusiness.defaultProps = defaultProps; +IncorporationStateBusiness.displayName = 'IncorporationStateBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(IncorporationStateBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.js new file mode 100644 index 000000000000..f41f25b3070d --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.js @@ -0,0 +1,72 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyNameKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.COMPANY_NAME; + +const validate = (values) => ValidationUtils.getFieldRequiredErrors(values, [companyNameKey]); + +function NameBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultCompanyName = getDefaultStateForField({reimbursementAccount, fieldName: companyNameKey, defaultValue: ''}); + + const bankAccountID = getDefaultStateForField({reimbursementAccount, fieldName: 'bankAccountID', defaultValue: 0}); + + const shouldDisableCompanyName = Boolean(bankAccountID && defaultCompanyName); + + return ( +
+ {translate('businessInfoStep.enterTheNameOfYourBusiness')} + + + ); +} + +NameBusiness.propTypes = propTypes; +NameBusiness.defaultProps = defaultProps; +NameBusiness.displayName = 'NameBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(NameBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.js new file mode 100644 index 000000000000..e594119bb7ca --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.js @@ -0,0 +1,76 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyPhoneNumberKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.COMPANY_PHONE; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [companyPhoneNumberKey]); + + if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { + errors.companyPhone = 'bankAccount.error.phoneNumber'; + } + + return errors; +}; + +function PhoneNumberBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultCompanyPhoneNumber = getDefaultStateForField({reimbursementAccount, fieldName: companyPhoneNumberKey, defaultValue: ''}); + + return ( +
+ {translate('businessInfoStep.enterYourCompanysPhoneNumber')} + + + ); +} + +PhoneNumberBusiness.propTypes = propTypes; +PhoneNumberBusiness.defaultProps = defaultProps; +PhoneNumberBusiness.displayName = 'PhoneNumberBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(PhoneNumberBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.js new file mode 100644 index 000000000000..f03936550e7e --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.js @@ -0,0 +1,82 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyTaxIdKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.COMPANY_TAX_ID; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [companyTaxIdKey]); + + if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { + errors.companyTaxID = 'bankAccount.error.taxID'; + } + + return errors; +}; + +function TaxIdBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultCompanyTaxId = getDefaultStateForField({reimbursementAccount, fieldName: companyTaxIdKey, defaultValue: ''}); + + const bankAccountID = getDefaultStateForField({reimbursementAccount, fieldName: 'bankAccountID', defaultValue: 0}); + + const shouldDisableCompanyTaxID = Boolean(bankAccountID && defaultCompanyTaxId); + + return ( +
+ {translate('businessInfoStep.enterYourCompanysTaxIdNumber')} + + + ); +} + +TaxIdBusiness.propTypes = propTypes; +TaxIdBusiness.defaultProps = defaultProps; +TaxIdBusiness.displayName = 'TaxIdBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(TaxIdBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.js new file mode 100644 index 000000000000..8eadf263b947 --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.js @@ -0,0 +1,66 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import Form from '@components/Form'; +import Picker from '@components/Picker'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, +}; + +const companyIncorporationTypeKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.INCORPORATION_TYPE; + +const validate = (values) => ValidationUtils.getFieldRequiredErrors(values, [companyIncorporationTypeKey]); + +function TypeBusiness({reimbursementAccount, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultIncorporationType = getDefaultStateForField({reimbursementAccount, fieldName: companyIncorporationTypeKey, defaultValue: ''}); + + return ( +
+ {translate('businessInfoStep.selectYourCompanysType')} + ({value: key, label: translate(`businessInfoStep.incorporationType.${key}`)}))} + placeholder={{value: '', label: '-'}} + defaultValue={defaultIncorporationType} + shouldSaveDraft + /> + + ); +} + +TypeBusiness.propTypes = propTypes; +TypeBusiness.defaultProps = defaultProps; +TypeBusiness.displayName = 'TypeBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, +})(TypeBusiness); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.js b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.js new file mode 100644 index 000000000000..3e95690f49df --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.js @@ -0,0 +1,103 @@ +import Str from 'expensify-common/lib/str'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React, {useMemo} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import Form from '@components/Form'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import {reimbursementAccountDefaultProps, reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; +import styles from '@styles/styles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const propTypes = { + /** Reimbursement account from ONYX */ + reimbursementAccount: reimbursementAccountPropTypes, + + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user email */ + email: PropTypes.string, + }), + + /** Object with various information about the user */ + user: PropTypes.shape({ + /** Whether or not the user is on a public domain email account or not */ + isFromPublicDomain: PropTypes.bool, + }), + + ...subStepPropTypes, +}; + +const defaultProps = { + reimbursementAccount: reimbursementAccountDefaultProps, + session: { + email: null, + }, + user: {}, +}; + +const companyWebsiteKey = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.COMPANY_WEBSITE; + +const validate = (values) => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [companyWebsiteKey]); + + if (values.website && !ValidationUtils.isValidWebsite(values.website)) { + errors.website = 'bankAccount.error.website'; + } + + return errors; +}; + +function WebsiteBusiness({reimbursementAccount, user, session, onNext, isEditing}) { + const {translate} = useLocalize(); + + const defaultWebsiteExample = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); + + const defaultCompanyWebsite = getDefaultStateForField({reimbursementAccount, fieldName: companyWebsiteKey, defaultValue: defaultWebsiteExample}); + + return ( +
+ {translate('businessInfoStep.enterYourCompanysWebsite')} + {translate('common.websiteExample')} + + + ); +} + +WebsiteBusiness.propTypes = propTypes; +WebsiteBusiness.defaultProps = defaultProps; +WebsiteBusiness.displayName = 'WebsiteBusiness'; + +export default withOnyx({ + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + session: { + key: ONYXKEYS.SESSION, + }, + user: { + key: ONYXKEYS.USER, + }, +})(WebsiteBusiness); diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 41f73d1ebf8e..b1e53efc6087 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -1,276 +1,22 @@ -import {parsePhoneNumber} from 'awesome-phonenumber'; -import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useMemo} from 'react'; -import {View} from 'react-native'; +import React from 'react'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import CheckboxWithLabel from '@components/CheckboxWithLabel'; -import DatePicker from '@components/DatePicker'; -import Form from '@components/Form'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Picker from '@components/Picker'; -import ScreenWrapper from '@components/ScreenWrapper'; -import StatePicker from '@components/StatePicker'; -import Text from '@components/Text'; -import TextInput from '@components/TextInput'; -import TextLink from '@components/TextLink'; import withLocalize from '@components/withLocalize'; import compose from '@libs/compose'; -import * as ValidationUtils from '@libs/ValidationUtils'; -import styles from '@styles/styles'; -import * as BankAccounts from '@userActions/BankAccounts'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import AddressForm from './AddressForm'; -import StepPropTypes from './StepPropTypes'; +import BusinessInfo from './BusinessInfo/BusinessInfo'; const propTypes = { - ...StepPropTypes, - - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user email */ - email: PropTypes.string, - }), - - /** Object with various information about the user */ - user: PropTypes.shape({ - /** Whether or not the user is on a public domain email account or not */ - isFromPublicDomain: PropTypes.bool, - }), - /* The workspace policyID */ policyID: PropTypes.string, }; const defaultProps = { - session: { - email: null, - }, - user: {}, policyID: '', }; -function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaultStateForField, onBackButtonPress, translate, session, user, policyID}) { - /** - * @param {Array} fieldNames - * - * @returns {*} - */ - const getBankAccountFields = (fieldNames) => ({ - ..._.pick(lodashGet(reimbursementAccount, 'achData'), ...fieldNames), - ..._.pick(reimbursementAccountDraft, ...fieldNames), - }); - - const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); - - /** - * @param {Object} values - form input values passed by the Form component - * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} - */ - const validate = (values) => { - const requiredFields = [ - 'companyName', - 'addressStreet', - 'addressZipCode', - 'addressCity', - 'addressState', - 'companyPhone', - 'website', - 'companyTaxID', - 'incorporationType', - 'incorporationDate', - 'incorporationState', - ]; - const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - - if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = 'bankAccount.error.addressStreet'; - } - - if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = 'bankAccount.error.zipCode'; - } - - if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { - errors.companyPhone = 'bankAccount.error.phoneNumber'; - } - - if (values.website && !ValidationUtils.isValidWebsite(values.website)) { - errors.website = 'bankAccount.error.website'; - } - - if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { - errors.companyTaxID = 'bankAccount.error.taxID'; - } - - if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { - errors.incorporationDate = 'common.error.dateInvalid'; - } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { - errors.incorporationDate = 'bankAccount.error.incorporationDateFuture'; - } - - if (!values.hasNoConnectionToCannabis) { - errors.hasNoConnectionToCannabis = 'bankAccount.error.restrictedBusiness'; - } - - return errors; - }; - - const submit = (values) => { - const bankAccount = { - bankAccountID: lodashGet(reimbursementAccount, 'achData.bankAccountID') || 0, - - // Fields from BankAccount step - ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), - - // Fields from Company step - ...values, - companyTaxID: values.companyTaxID.replace(CONST.REGEX.NON_NUMERIC, ''), - companyPhone: parsePhoneNumber(values.companyPhone, {regionCode: CONST.COUNTRY.US}).number.significant, - }; - - BankAccounts.updateCompanyInformationForBankAccount(bankAccount, policyID); - }; - - const bankAccountID = lodashGet(reimbursementAccount, 'achData.bankAccountID', 0); - const shouldDisableCompanyName = Boolean(bankAccountID && getDefaultStateForField('companyName')); - const shouldDisableCompanyTaxID = Boolean(bankAccountID && getDefaultStateForField('companyTaxID')); - - return ( - - -
- {translate('companyStep.subtitle')} - - - - - - - ({value: key, label: translate(`companyStep.incorporationTypes.${key}`)}))} - placeholder={{value: '', label: '-'}} - defaultValue={getDefaultStateForField('incorporationType')} - shouldSaveDraft - /> - - - - - - - - ( - - {`${translate('companyStep.confirmCompanyIsNot')} `} - - {`${translate('companyStep.listOfRestrictedBusinesses')}.`} - - - )} - style={[styles.mt4]} - shouldSaveDraft - /> - -
- ); +function CompanyStep({policyID}) { + return ; } CompanyStep.propTypes = propTypes; diff --git a/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.js b/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.js index 218e7438d06e..60e4c1d75d2b 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.js +++ b/src/pages/ReimbursementAccount/PersonalInfo/PersonalInfo.js @@ -1,4 +1,3 @@ -import _ from 'lodash'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -11,6 +10,7 @@ import Navigation from '@libs/Navigation/Navigation'; import reimbursementAccountDraftPropTypes from '@pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes'; import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; import getInitialSubstepForPersonalInfo from '@pages/ReimbursementAccount/utils/getInitialSubstepForPersonalInfo'; import getPersonalInfoValues from '@pages/ReimbursementAccount/utils/getPersonalInfoValues'; import styles from '@styles/styles'; @@ -49,7 +49,7 @@ function PersonalInfo({reimbursementAccount, reimbursementAccountDraft}) { const submit = useCallback(() => { const payload = { - bankAccountID: _.get(reimbursementAccount, 'achData.bankAccountID', 0), + bankAccountID: getDefaultStateForField({reimbursementAccount, fieldName: 'bankAccountID', defaultValue: 0}), ...values, }; diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.js b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.js index b50ee2f22e3a..407ac2365ca3 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.js +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -11,6 +10,7 @@ import HelpLinks from '@pages/ReimbursementAccount/PersonalInfo/HelpLinks'; import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; import styles from '@styles/styles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -26,19 +26,16 @@ const defaultProps = { reimbursementAccount: ReimbursementAccountProps.reimbursementAccountDefaultProps, }; +const personalInfoStepKey = CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY; + const INPUT_KEYS = { - street: CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STREET, - city: CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.CITY, - state: CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STATE, - zipCode: CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.ZIP_CODE, + street: personalInfoStepKey.STREET, + city: personalInfoStepKey.CITY, + state: personalInfoStepKey.STATE, + zipCode: personalInfoStepKey.ZIP_CODE, }; -const REQUIRED_FIELDS = [ - CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STREET, - CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.CITY, - CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STATE, - CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.ZIP_CODE, -]; +const REQUIRED_FIELDS = [personalInfoStepKey.STREET, personalInfoStepKey.CITY, personalInfoStepKey.STATE, personalInfoStepKey.ZIP_CODE]; const validate = (values) => { const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); @@ -58,10 +55,10 @@ function Address({reimbursementAccount, onNext, isEditing}) { const {translate} = useLocalize(); const defaultValues = { - street: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STREET], ''), - city: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.CITY], ''), - state: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.STATE], ''), - zipCode: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.ZIP_CODE], ''), + street: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.STREET, defaultValue: ''}), + city: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.CITY, defaultValue: ''}), + state: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.STATE, defaultValue: ''}), + zipCode: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.ZIP_CODE, defaultValue: ''}), }; return ( diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.js b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.js index 4a4e35cda463..b9f4a110de4e 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.js +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.js @@ -12,6 +12,7 @@ import reimbursementAccountDraftPropTypes from '@pages/ReimbursementAccount/Reim import {reimbursementAccountPropTypes} from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import subStepPropTypes from '@pages/ReimbursementAccount/subStepPropTypes'; +import getDefaultStateForField from '@pages/ReimbursementAccount/utils/getDefaultStateForField'; import styles from '@styles/styles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -31,10 +32,10 @@ const defaultProps = { reimbursementAccountDraft: {}, }; -const REQUIRED_FIELDS = [CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.DOB]; +const personalInfoDobKey = CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.DOB; const validate = (values) => { - const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); + const errors = ValidationUtils.getFieldRequiredErrors(values, [personalInfoDobKey]); if (values.dob) { if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { @@ -50,9 +51,7 @@ const validate = (values) => { function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, isEditing}) { const {translate} = useLocalize(); - const dobDefaultValue = - lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.DOB], '') || - lodashGet(reimbursementAccountDraft, CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.DOB, ''); + const dobDefaultValue = getDefaultStateForField({reimbursementAccount, fieldName: personalInfoDobKey, defaultValue: ''}) || lodashGet(reimbursementAccountDraft, personalInfoDobKey, ''); const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); const maxDate = subYears(new Date(), CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); @@ -69,7 +68,7 @@ function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, i {translate('personalInfoStep.enterYourDateOfBirth')} ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); @@ -34,8 +36,8 @@ function FullName({reimbursementAccount, onNext, isEditing}) { const {translate} = useLocalize(); const defaultValues = { - firstName: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.FIRST_NAME], ''), - lastName: lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.LAST_NAME], ''), + firstName: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.FIRST_NAME, defaultValue: ''}), + lastName: getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.LAST_NAME, defaultValue: ''}), }; return ( @@ -51,7 +53,7 @@ function FullName({reimbursementAccount, onNext, isEditing}) { {translate('personalInfoStep.enterYourLegalFirstAndLast')} { - const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); + const errors = ValidationUtils.getFieldRequiredErrors(values, [personalInfoStepKey.SSN_LAST_4]); if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { errors.ssnLast4 = 'bankAccount.error.ssnLast4'; @@ -40,7 +39,7 @@ const validate = (values) => { function SocialSecurityNumber({reimbursementAccount, onNext, isEditing}) { const {translate} = useLocalize(); - const defaultSsnLast4 = lodashGet(reimbursementAccount, ['achData', CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.INPUT_KEY.SSN_LAST_4], ''); + const defaultSsnLast4 = getDefaultStateForField({reimbursementAccount, fieldName: personalInfoStepKey.SSN_LAST_4, defaultValue: ''}); return (
{translate('personalInfoStep.dontWorry')} { if (shouldShowOnfido) { return ( ; -} +}); RequestorStep.propTypes = propTypes; RequestorStep.displayName = 'RequestorStep'; diff --git a/src/pages/ReimbursementAccount/utils/getDefaultStateForField.js b/src/pages/ReimbursementAccount/utils/getDefaultStateForField.js new file mode 100644 index 000000000000..f857885fa32b --- /dev/null +++ b/src/pages/ReimbursementAccount/utils/getDefaultStateForField.js @@ -0,0 +1,15 @@ +import lodashGet from 'lodash/get'; + +/** + * @param {Object} params + * @param {Object} params.reimbursementAccount + * @param {String} params.fieldName + * @param {* | undefined} params.defaultValue + * + * @returns {String} + */ +function getDefaultStateForField({reimbursementAccount, fieldName, defaultValue = ''}) { + return lodashGet(reimbursementAccount, ['achData', fieldName], defaultValue); +} + +export default getDefaultStateForField; diff --git a/src/pages/ReimbursementAccount/utils/getInitialSubstepForBusinessInfo.js b/src/pages/ReimbursementAccount/utils/getInitialSubstepForBusinessInfo.js new file mode 100644 index 000000000000..3f3ba75753db --- /dev/null +++ b/src/pages/ReimbursementAccount/utils/getInitialSubstepForBusinessInfo.js @@ -0,0 +1,48 @@ +import CONST from '@src/CONST'; + +const businessInfoStepKeys = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY; + +/** + * Returns the initial substep for the Business Info step based on already existing data + * + * @param {Object} data object that stores business info data + * + * @returns {number} + */ +function getInitialSubstepForBusinessInfo(data) { + if (data[businessInfoStepKeys.COMPANY_NAME] === '') { + return 0; + } + + if (data[businessInfoStepKeys.COMPANY_TAX_ID] === '') { + return 1; + } + + if (data[businessInfoStepKeys.COMPANY_WEBSITE] === '') { + return 2; + } + + if (data[businessInfoStepKeys.COMPANY_PHONE] === '') { + return 3; + } + + if (data[businessInfoStepKeys.STREET] === '' || data[businessInfoStepKeys.CITY] === '' || data[businessInfoStepKeys.STATE] === '' || data[businessInfoStepKeys.ZIP_CODE] === '') { + return 4; + } + + if (data[businessInfoStepKeys.INCORPORATION_TYPE] === '') { + return 5; + } + + if (data[businessInfoStepKeys.INCORPORATION_DATE] === '') { + return 6; + } + + if (data[businessInfoStepKeys.INCORPORATION_STATE] === '') { + return 7; + } + + return 8; +} + +export default getInitialSubstepForBusinessInfo; diff --git a/src/pages/ReimbursementAccount/utils/getSubstepValues.js b/src/pages/ReimbursementAccount/utils/getSubstepValues.js new file mode 100644 index 000000000000..8181fe70156f --- /dev/null +++ b/src/pages/ReimbursementAccount/utils/getSubstepValues.js @@ -0,0 +1,21 @@ +import lodashGet from 'lodash/get'; +import lodashReduce from 'lodash/reduce'; +import getDefaultStateForField from './getDefaultStateForField'; + +/** + * Returns values for substep confirmation page + * + * @param {Object} inputKeys object that stores substep info keys + * @param {Object} reimbursementAccountDraft object that stores substep info draft data + * @param {Object} reimbursementAccount object that stores substep info data + * @returns {Object} + */ +function getSubstepValues(inputKeys, reimbursementAccountDraft, reimbursementAccount) { + return lodashReduce( + Object.entries(inputKeys), + (acc, [, value]) => ({...acc, [value]: lodashGet(reimbursementAccountDraft, value, '') || getDefaultStateForField({reimbursementAccount, fieldName: value, defaultValue: ''})}), + {}, + ); +} + +export default getSubstepValues;