diff --git a/src/libs/actions/ReimbursementAccount/index.ts b/src/libs/actions/ReimbursementAccount/index.ts index 62e5f8454cbd..c7c2d8e237d5 100644 --- a/src/libs/actions/ReimbursementAccount/index.ts +++ b/src/libs/actions/ReimbursementAccount/index.ts @@ -26,6 +26,10 @@ function updateReimbursementAccountDraft(bankAccountData: Partial): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); @@ -86,6 +88,7 @@ function Manual({onNext}: ManualProps) { inputMode={CONST.INPUT_MODE.NUMERIC} shouldSaveDraft shouldUseDefaultValue={hasBankAccountData} + disabled={shouldBeReadOnlyInput} /> ); diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 972363bed7fc..11820cd2c471 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -8,6 +8,7 @@ import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {useSession} from '@components/OnyxProvider'; import ReimbursementAccountLoadingIndicator from '@components/ReimbursementAccountLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -24,6 +25,7 @@ import shouldReopenOnfido from '@libs/shouldReopenOnfido'; import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; import * as BankAccounts from '@userActions/BankAccounts'; +import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -96,23 +98,73 @@ function getRouteForCurrentStep(currentStep: TBankAccountStep): ValueOf(fieldNames: T[]): Pick { return { @@ -120,40 +172,13 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps }; } - /** - * Returns selected bank account fields based on field names provided. - */ - function getFieldsForStep(step: TBankAccountStep): InputID[] { - switch (step) { - case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: - return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; - case CONST.BANK_ACCOUNT.STEP.COMPANY: - return [ - 'companyName', - 'addressStreet', - 'addressZipCode', - 'addressCity', - 'addressState', - 'companyPhone', - 'website', - 'companyTaxID', - 'incorporationType', - 'incorporationDate', - 'incorporationState', - ]; - case CONST.BANK_ACCOUNT.STEP.REQUESTOR: - return ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; - default: - return []; - } - } - /** * Returns true if a VBBA exists in any state other than OPEN or LOCKED */ function hasInProgressVBBA(): boolean { - return !!achData?.bankAccountID && achData?.state !== BankAccount.STATE.OPEN && achData?.state !== BankAccount.STATE.LOCKED; + return !!achData?.bankAccountID && !!achData?.state && achData?.state !== BankAccount.STATE.OPEN && achData?.state !== BankAccount.STATE.LOCKED; } + /* * Calculates the state used to show the "Continue with setup" view. If a bank account setup is already in progress and * no specific further step was passed in the url we'll show the workspace bank account reset modal if the user wishes to start over @@ -166,75 +191,36 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps return achData?.state === BankAccount.STATE.PENDING || [CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT, ''].includes(getStepToOpenFromRouteParams(route)); } - /** - When this page is first opened, `reimbursementAccount` prop might not yet be fully loaded from Onyx. - Calculating `shouldShowContinueSetupButton` immediately on initial render doesn't make sense as - it relies on incomplete data. Thus, we should wait to calculate it until we have received - the full `reimbursementAccount` data from the server. This logic is handled within the useEffect hook, - which acts similarly to `componentDidUpdate` when the `reimbursementAccount` dependency changes. - */ - const [hasACHDataBeenLoaded, setHasACHDataBeenLoaded] = useState(reimbursementAccount !== CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA); - - const [shouldShowContinueSetupButton, setShouldShowContinueSetupButton] = useState(hasACHDataBeenLoaded ? getShouldShowContinueSetupButtonInitialValue() : false); - const [isReimbursementAccountLoading, setIsReimbursementAccountLoading] = useState(() => { - // By default return true (loading), if there are already loaded data we can skip the loading state - if (hasACHDataBeenLoaded && typeof reimbursementAccount?.isLoading === 'boolean' && !reimbursementAccount?.isLoading) { - return false; - } - return true; - }); - - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const currentStep = achData?.currentStep || CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT; - const policyName = policy?.name ?? ''; - const policyIDParam = route.params?.policyID ?? '-1'; - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const {isOffline} = useNetwork(); - const requestorStepRef = useRef(null); - const prevIsReimbursementAccountLoading = usePrevious(reimbursementAccount?.isLoading); - const prevReimbursementAccount = usePrevious(reimbursementAccount); - const prevIsOffline = usePrevious(isOffline); - /** * Retrieve verified business bank account currently being set up. - * @param ignoreLocalCurrentStep Pass true if you want the last "updated" view (from db), not the last "viewed" view (from onyx). - * @param ignoreLocalSubStep Pass true if you want the last "updated" view (from db), not the last "viewed" view (from onyx). */ - function fetchData(ignoreLocalCurrentStep?: boolean, ignoreLocalSubStep?: boolean) { - // Show loader right away, as optimisticData might be set only later in case multiple calls are in the queue - BankAccounts.setReimbursementAccountLoading(true); - + function fetchData() { // We can specify a step to navigate to by using route params when the component mounts. // We want to use the same stepToOpen variable when the network state changes because we can be redirected to a different step when the account refreshes. const stepToOpen = getStepToOpenFromRouteParams(route); - const subStep = achData?.subStep ?? ''; - const localCurrentStep = achData?.currentStep ?? ''; - BankAccounts.openReimbursementAccountPage(stepToOpen, ignoreLocalSubStep ? '' : subStep, ignoreLocalCurrentStep ? '' : localCurrentStep, policyIDParam); + const subStep = isPreviousPolicy ? achData?.subStep ?? '' : ''; + const localCurrentStep = isPreviousPolicy ? achData?.currentStep ?? '' : ''; + BankAccounts.openReimbursementAccountPage(stepToOpen, subStep, localCurrentStep, policyIDParam); } useEffect(() => { - if (!isReimbursementAccountLoading) { + if (isPreviousPolicy) { return; } + BankAccounts.setReimbursementAccountLoading(true); + ReimbursementAccount.clearReimbursementAccountDraft(); + // If the step to open is empty, we want to clear the sub step, so the connect option view is shown to the user const isStepToOpenEmpty = getStepToOpenFromRouteParams(route) === ''; if (isStepToOpenEmpty) { BankAccounts.setBankAccountSubStep(null); BankAccounts.setPlaidEvent(null); } - fetchData(false, isStepToOpenEmpty); + fetchData(); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); // The empty dependency array ensures this runs only once after the component mounts. - useEffect(() => { - if (typeof reimbursementAccount?.isLoading !== 'boolean' || reimbursementAccount.isLoading === prevIsReimbursementAccountLoading) { - return; - } - setIsReimbursementAccountLoading(reimbursementAccount.isLoading); - }, [prevIsReimbursementAccountLoading, reimbursementAccount?.isLoading]); - useEffect( () => { // Check for network change from offline to online @@ -243,8 +229,7 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps } if (!hasACHDataBeenLoaded) { - if (reimbursementAccount !== CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA && isReimbursementAccountLoading === false) { - setShouldShowContinueSetupButton(getShouldShowContinueSetupButtonInitialValue()); + if (reimbursementAccount !== CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA && reimbursementAccount?.isLoading === false) { setHasACHDataBeenLoaded(true); } return; @@ -296,7 +281,6 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps BankAccounts.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL).then(() => { setShouldShowContinueSetupButton(false); }); - fetchData(true); }; const goBack = () => { @@ -352,7 +336,7 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps } }; - const isLoading = (!!isLoadingApp || !!account?.isLoading || isReimbursementAccountLoading) && (!plaidCurrentEvent || plaidCurrentEvent === CONST.BANK_ACCOUNT.PLAID.EVENTS_NAME.EXIT); + const isLoading = (!!isLoadingApp || !!account?.isLoading || reimbursementAccount?.isLoading) && (!plaidCurrentEvent || plaidCurrentEvent === CONST.BANK_ACCOUNT.PLAID.EVENTS_NAME.EXIT); const shouldShowOfflineLoader = !( isOffline && @@ -424,53 +408,43 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps ); } - if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT) { - return ( - - ); - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY) { - return ; - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR) { - const shouldShowOnfido = onfidoToken && !achData?.isOnfidoSetupComplete; - return ( - - ); - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS) { - return ; - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT) { - return ; - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.VALIDATION) { - return ; - } - - if (currentStep === CONST.BANK_ACCOUNT.STEP.ENABLE) { - return ( - - ); + switch (currentStep) { + case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: + return ( + + ); + case CONST.BANK_ACCOUNT.STEP.REQUESTOR: + return ( + + ); + case CONST.BANK_ACCOUNT.STEP.COMPANY: + return ; + case CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS: + return ; + case CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT: + return ; + case CONST.BANK_ACCOUNT.STEP.VALIDATION: + return ; + case CONST.BANK_ACCOUNT.STEP.ENABLE: + return ( + + ); + default: + return null; } } diff --git a/src/types/onyx/ReimbursementAccount.ts b/src/types/onyx/ReimbursementAccount.ts index 186b3e6ad05b..0d5c8a83b99b 100644 --- a/src/types/onyx/ReimbursementAccount.ts +++ b/src/types/onyx/ReimbursementAccount.ts @@ -47,6 +47,9 @@ type ACHData = Partial