From 2a6276e99740955c761f50b9ab8163b0b2314568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Mon, 11 May 2026 15:26:21 +0200 Subject: [PATCH] Revert "feat: 79048 restore reverted usd flow" --- src/CONST/index.ts | 58 ----- src/ROUTES.ts | 37 +-- src/SCREENS.ts | 1 - src/components/Navigation/DebugTabView.tsx | 6 +- .../useReimbursementAccountSubmitCallback.ts | 28 --- .../ModalStackNavigators/index.tsx | 14 -- src/libs/Navigation/linkingConfig/config.ts | 4 - src/libs/Navigation/types.ts | 8 - src/libs/ReimbursementAccountUtils.ts | 25 +- .../ConnectedVerifiedBankAccount.tsx | 4 +- .../NonUSDVerifiedBankAccountFlowPage.tsx | 2 +- .../ReimbursementAccountPage.tsx | 40 +-- .../ReimbursementAccountVerifyAccountPage.tsx | 3 +- .../USD/BankInfo/BankInfo.tsx | 37 ++- .../USD/BankInfo/subSteps/Manual.tsx | 6 +- .../USD/BankInfo/subSteps/Plaid.tsx | 12 +- .../USD/BankInfo/types.ts | 6 - .../BeneficialOwnerDetailsFormPages.tsx | 123 ---------- .../BeneficialOwnersStep.tsx | 232 +++++++++--------- .../AddressUBO.tsx | 4 +- .../ConfirmationUBO.tsx | 8 +- .../DateOfBirthUBO.tsx | 4 +- .../LegalNameUBO.tsx | 4 +- .../SocialSecurityNumberUBO.tsx | 5 +- .../USD/BusinessInfo/BusinessInfo.tsx | 80 +++--- .../BusinessInfo/subSteps/AddressBusiness.tsx | 4 +- .../subSteps/ConfirmationBusiness.tsx | 12 +- .../subSteps/IncorporationCode.tsx | 4 +- .../subSteps/IncorporationDateBusiness.tsx | 4 +- .../subSteps/IncorporationStateBusiness.tsx | 4 +- .../IndustryCode/IndustryCodeSelector.tsx | 23 +- .../BusinessInfo/subSteps/NameBusiness.tsx | 5 +- .../subSteps/PhoneNumberBusiness.tsx | 5 +- .../BusinessInfo/subSteps/TaxIdBusiness.tsx | 5 +- .../subSteps/TypeBusiness/TypeBusiness.tsx | 4 +- .../BusinessInfo/subSteps/WebsiteBusiness.tsx | 5 +- .../CompleteVerification.tsx | 37 ++- .../subSteps/ConfirmAgreements.tsx | 8 +- .../ConnectBankAccount/ConnectBankAccount.tsx | 30 +-- .../components/FinishChatCard.tsx | 4 +- .../USD/Country/index.tsx | 14 +- .../Requestor/PersonalInfo/PersonalInfo.tsx | 73 ++---- .../PersonalInfo/subSteps/Address.tsx | 4 +- .../PersonalInfo/subSteps/Confirmation.tsx | 4 +- .../PersonalInfo/subSteps/DateOfBirth.tsx | 4 +- .../PersonalInfo/subSteps/FullName.tsx | 4 +- .../subSteps/SocialSecurityNumber.tsx | 5 +- .../USD/Requestor/RequestorStep.tsx | 16 +- .../VerifyIdentity/VerifyIdentity.tsx | 27 +- .../USD/USDVerifiedBankAccountFlow.tsx | 97 ++++++++ .../USD/USDVerifiedBankAccountFlowPage.tsx | 135 ---------- src/pages/ReimbursementAccount/USD/types.ts | 28 --- .../VerifiedBankAccountFlowEntryPoint.tsx | 4 +- .../WorkspaceExpensifyCardBankAccounts.tsx | 2 + .../WorkspaceExpensifyCardPageEmptyState.tsx | 3 +- .../WorkspaceTravelInvoicingSection.tsx | 3 +- ...ceTravelInvoicingSettlementAccountPage.tsx | 2 + tests/unit/ReimbursementAccountUtilsTest.ts | 9 +- 58 files changed, 500 insertions(+), 839 deletions(-) delete mode 100644 src/hooks/useReimbursementAccountSubmitCallback.ts delete mode 100644 src/pages/ReimbursementAccount/USD/BankInfo/types.ts delete mode 100644 src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormPages.tsx create mode 100644 src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx delete mode 100644 src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage.tsx delete mode 100644 src/pages/ReimbursementAccount/USD/types.ts diff --git a/src/CONST/index.ts b/src/CONST/index.ts index dba79ebefed5..714e4b4a8481 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -627,65 +627,7 @@ const CONST = { VALIDATION: 'ValidationStep', ENABLE: 'EnableStep', }, - PAGE_NAMES: { - COUNTRY: 'currency-and-country', - BANK_ACCOUNT: 'bank-info', - REQUESTOR: 'personal-info', - VERIFY_IDENTITY: 'verify-identity', - COMPANY: 'company', - BENEFICIAL_OWNERS: 'business-owner', - ACH_CONTRACT: 'ach-contract', - VALIDATION: 'validation', - ENABLE: 'enable', - }, STEP_NAMES: ['1', '2', '3', '4', '5', '6'], - BANK_INFO_STEP: { - SUB_PAGE_NAMES: { - MANUAL: 'manual', - PLAID: 'plaid', - }, - }, - PERSONAL_INFO_STEP: { - SUB_PAGE_NAMES: { - FULL_NAME: 'full-name', - DATE_OF_BIRTH: 'date-of-birth', - SSN: 'ssn', - ADDRESS: 'address', - CONFIRMATION: 'confirmation', - }, - }, - BUSINESS_INFO_STEP: { - SUB_PAGE_NAMES: { - NAME: 'name', - TAX_ID: 'tax-id', - WEBSITE: 'website', - PHONE: 'phone', - ADDRESS: 'address', - TYPE: 'type', - INCORPORATION_DATE: 'start-date', - INCORPORATION_STATE: 'state', - INCORPORATION_CODE: 'code', - CONFIRMATION: 'confirmation', - }, - }, - BENEFICIAL_OWNERS_STEP: { - SUB_PAGE_NAMES: { - IS_USER_UBO: 'is-user-ubo', - IS_ANYONE_ELSE_UBO: 'is-anyone-else-ubo', - ARE_THERE_MORE_UBOS: 'are-there-more-ubos', - UBOS_LIST: 'ubos-list', - LEGAL_NAME: 'legal-name', - DATE_OF_BIRTH: 'date-of-birth', - SSN: 'ssn', - ADDRESS: 'address', - CONFIRMATION: 'confirmation', - }, - }, - COMPLETE_VERIFICATION_STEP: { - SUB_PAGE_NAMES: { - CONFIRM_AGREEMENTS: 'confirm-agreements', - }, - }, SUBSTEP: { MANUAL: 'manual', PLAID: 'plaid', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index fe9687e1c4fa..a474bbc6a5e9 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -14,6 +14,7 @@ import type {ReplacementReason} from './libs/actions/Card'; import type {IOURequestType} from './libs/actions/IOU'; import Log from './libs/Log'; import type {RootNavigatorParamList} from './libs/Navigation/types'; +import type {ReimbursementAccountStepToOpen} from './libs/ReimbursementAccountUtils'; import StringUtils from './libs/StringUtils'; import {getUrlWithParams} from './libs/Url'; import SCREENS from './SCREENS'; @@ -623,24 +624,36 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID?: string, backTo?: string) => getUrlWithBackToParam(`bank-account/${VERIFY_ACCOUNT}?policyID=${policyID}`, backTo), }, + BANK_ACCOUNT_NEW: 'bank-account/new', BANK_ACCOUNT_PERSONAL: 'bank-account/personal', - // TODO: rename the route as no longer accepts step BANK_ACCOUNT_WITH_STEP_TO_OPEN: { - route: 'bank-account/new', - getRoute: ({policyID, bankAccountID, backTo}: {policyID: string | undefined; bankAccountID?: number; backTo?: string}) => { + route: 'bank-account/:stepToOpen?', + getRoute: ({ + policyID, + stepToOpen = '', + bankAccountID, + backTo, + subStepToOpen, + }: { + policyID: string | undefined; + stepToOpen?: ReimbursementAccountStepToOpen; + bankAccountID?: number; + backTo?: string; + subStepToOpen?: typeof CONST.BANK_ACCOUNT.STEP.COUNTRY; + }) => { if (!policyID && !bankAccountID) { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - return getUrlWithBackToParam(`bank-account/new`, backTo); + return getUrlWithBackToParam(`bank-account/${stepToOpen}`, backTo); } if (bankAccountID) { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - return getUrlWithBackToParam(`bank-account/new?bankAccountID=${bankAccountID}`, backTo); + return getUrlWithBackToParam(`bank-account/${stepToOpen}?bankAccountID=${bankAccountID}`, backTo); } // TODO this backTo comes from drilling it through bank account form screens // should be removed once https://github.com/Expensify/App/pull/72219 is resolved // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - return getUrlWithBackToParam(`bank-account/new?policyID=${policyID}`, backTo); + return getUrlWithBackToParam(`bank-account/${stepToOpen}?policyID=${policyID}${subStepToOpen ? `&subStep=${subStepToOpen}` : ''}`, backTo); }, }, BANK_ACCOUNT_ENTER_SIGNER_INFO: { @@ -672,18 +685,6 @@ const ROUTES = { return getUrlWithBackToParam(`${base}${pagePart}${subPagePart}${actionPart}${queryString}`, backTo); }, }, - BANK_ACCOUNT_USD_SETUP: { - route: 'bank-account/new/us/:page?/:subPage?/:action?', - getRoute: ({policyID, page, subPage, action, backTo}: {policyID?: string; page?: string; subPage?: string; action?: 'edit'; backTo?: string}) => { - const base = 'bank-account/new/us'; - const pagePart = page ? `/${page}` : ''; - const subPagePart = subPage ? `/${subPage}` : ''; - const actionPart = action ? `/${action}` : ''; - const queryString = policyID ? `?policyID=${policyID}` : ''; - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - return getUrlWithBackToParam(`${base}${pagePart}${subPagePart}${actionPart}${queryString}`, backTo); - }, - }, SETTINGS: 'settings', SETTINGS_PROFILE: { route: 'settings/profile', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 566dc62b8dd9..e9450573c282 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -970,7 +970,6 @@ const SCREENS = { }, DYNAMIC_FLAG_COMMENT: 'Dynamic_Flag_Comment', REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount', - REIMBURSEMENT_ACCOUNT_USD: 'Reimbursement_Account_USD', REIMBURSEMENT_ACCOUNT_NON_USD: 'Reimbursement_Account_Non_USD', REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO: 'Reimbursement_Account_Signer_Info', REFERRAL_DETAILS: 'Referral_Details', diff --git a/src/components/Navigation/DebugTabView.tsx b/src/components/Navigation/DebugTabView.tsx index b0778412dafd..536998617597 100644 --- a/src/components/Navigation/DebugTabView.tsx +++ b/src/components/Navigation/DebugTabView.tsx @@ -15,6 +15,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import {getRouteForCurrentStep as getReimbursementAccountRouteForCurrentStep} from '@libs/ReimbursementAccountUtils'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; import {getChatTabBrickRoadReportID} from '@libs/WorkspacesSettingsUtils'; import CONST from '@src/CONST'; @@ -81,7 +82,10 @@ function getSettingsRoute(status: IndicatorStatus | undefined, reimbursementAcco case CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS: return ROUTES.WORKSPACE_INITIAL.getRoute(policyIDWithErrors); case CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS: - return ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID: reimbursementAccount?.achData?.policyID}); + return ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({ + policyID: reimbursementAccount?.achData?.policyID, + stepToOpen: getReimbursementAccountRouteForCurrentStep(reimbursementAccount?.achData?.currentStep ?? CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT), + }); case CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS: return ROUTES.SETTINGS_SUBSCRIPTION.route; case CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO: diff --git a/src/hooks/useReimbursementAccountSubmitCallback.ts b/src/hooks/useReimbursementAccountSubmitCallback.ts deleted file mode 100644 index 90fdc0026166..000000000000 --- a/src/hooks/useReimbursementAccountSubmitCallback.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {useCallback, useEffect, useRef} from 'react'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useOnyx from './useOnyx'; - -/** - * Defers navigation (onSubmit) until the reimbursement account API call completes. - * Instead of navigating to the next step immediately after firing the API call, - * this hook waits for `isLoading` to go back to `false` and checks for errors. - * - * @param onSubmit - callback that navigates to the next step - * @returns markSubmitting - call this right after firing the API action - */ -export default function useReimbursementAccountSubmitCallback(onSubmit?: () => void) { - const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); - const isSubmittingRef = useRef(false); - - useEffect(() => { - if (!isSubmittingRef.current || reimbursementAccount?.isLoading || reimbursementAccount?.errors) { - return; - } - isSubmittingRef.current = false; - onSubmit?.(); - }, [reimbursementAccount?.isLoading, reimbursementAccount?.errors, onSubmit]); - - return useCallback(() => { - isSubmittingRef.current = true; - }, []); -} diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index e8c8aaea6eaa..5f02416ae24a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -3,7 +3,6 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import {isMobileChrome} from '@libs/Browser'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -63,17 +62,6 @@ const loadAttachmentModalScreen = () => require('../../../ type Screens = Partial React.ComponentType>>; -// Reimbursement Account flow animations glitch on low-end Android devices in Chrome mWeb https://github.com/Expensify/App/issues/87658 so we intentionally disable them -const IS_MOBILE_CHROME = isMobileChrome(); - -const REIMBURSEMENT_ACCOUNT_FLOW_SCREENS: Screen[] = [ - SCREENS.REIMBURSEMENT_ACCOUNT, - SCREENS.REIMBURSEMENT_ACCOUNT_USD, - SCREENS.REIMBURSEMENT_ACCOUNT_NON_USD, - SCREENS.REIMBURSEMENT_ACCOUNT_VERIFY_ACCOUNT, - SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO, -]; - const OPTIONS_PER_SCREEN: Partial> = { [SCREENS.SETTINGS.MERGE_ACCOUNTS.MERGE_RESULT]: { animationTypeForReplace: 'push', @@ -126,7 +114,6 @@ const OPTIONS_PER_SCREEN: Partial [SCREENS.WORKSPACE.DYNAMIC_CATEGORIES_IMPORTED]: { animationTypeForReplace: 'push', }, - ...(IS_MOBILE_CHROME ? Object.fromEntries(REIMBURSEMENT_ACCOUNT_FLOW_SCREENS.map((screen) => [screen, {animation: Animations.NONE}])) : {}), }; /** @@ -684,7 +671,6 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/qbd/import/QuickbooksDesktopItemsPage').default, [SCREENS.CONNECT_EXISTING_BUSINESS_BANK_ACCOUNT_ROOT]: () => require('@pages/workspace/ConnectExistingBusinessBankAccountPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../../pages/ReimbursementAccount/ReimbursementAccountPage').default, - [SCREENS.REIMBURSEMENT_ACCOUNT_USD]: () => require('../../../../pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT_NON_USD]: () => require('../../../../pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlowPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT_VERIFY_ACCOUNT]: () => require('../../../../pages/ReimbursementAccount/ReimbursementAccountVerifyAccountPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO]: () => require('../../../../pages/ReimbursementAccount/EnterSignerInfo').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index d26b86691ff7..26cacff5c40a 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1174,10 +1174,6 @@ const config: LinkingOptions['config'] = { path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, exact: true, }, - [SCREENS.REIMBURSEMENT_ACCOUNT_USD]: { - path: ROUTES.BANK_ACCOUNT_USD_SETUP.route, - exact: true, - }, [SCREENS.REIMBURSEMENT_ACCOUNT_NON_USD]: { path: ROUTES.BANK_ACCOUNT_NON_USD_SETUP.route, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 938706f634b7..e8212b24c2d3 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2329,14 +2329,6 @@ type ReimbursementAccountNavigatorParamList = { bankAccountID?: string; subStep?: typeof CONST.BANK_ACCOUNT.STEP.COUNTRY; }; - [SCREENS.REIMBURSEMENT_ACCOUNT_USD]: { - page?: string; - subPage?: string; - action?: 'edit'; - policyID?: string; - // eslint-disable-next-line no-restricted-syntax -- backTo is a temporary param will be removed after https://github.com/Expensify/App/issues/73825 is done - backTo?: Routes; - }; [SCREENS.REIMBURSEMENT_ACCOUNT_NON_USD]: { page?: string; subPage?: string; diff --git a/src/libs/ReimbursementAccountUtils.ts b/src/libs/ReimbursementAccountUtils.ts index 4bce69e731c0..e0f1dd03be60 100644 --- a/src/libs/ReimbursementAccountUtils.ts +++ b/src/libs/ReimbursementAccountUtils.ts @@ -1,6 +1,6 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; -import type {ACHDataReimbursementAccount} from '@src/types/onyx/ReimbursementAccount'; +import type {ACHDataReimbursementAccount, ReimbursementAccountStep} from '@src/types/onyx/ReimbursementAccount'; type ReimbursementAccountStepToOpen = ValueOf | ''; @@ -14,6 +14,27 @@ const REIMBURSEMENT_ACCOUNT_ROUTE_NAMES = { NEW: 'new', } as const; +function getRouteForCurrentStep(currentStep: ReimbursementAccountStep): ReimbursementAccountStepToOpen { + switch (currentStep) { + case CONST.BANK_ACCOUNT.STEP.COMPANY: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.COMPANY; + case CONST.BANK_ACCOUNT.STEP.REQUESTOR: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.PERSONAL_INFORMATION; + case CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.BENEFICIAL_OWNERS; + case CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.CONTRACT; + case CONST.BANK_ACCOUNT.STEP.VALIDATION: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.VALIDATE; + case CONST.BANK_ACCOUNT.STEP.ENABLE: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.ENABLE; + case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: + case CONST.BANK_ACCOUNT.STEP.COUNTRY: + default: + return REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW; + } +} + /** * Returns true if a VBBA exists in any state other than OPEN or LOCKED */ @@ -46,5 +67,5 @@ const hasInProgressVBBA = (achData?: ACHDataReimbursementAccount, isNonUSDWorksp return hasInProgressUSDVBBA(achData); }; -export {getBankAccountIDAsNumber, hasInProgressUSDVBBA, hasInProgressVBBA, REIMBURSEMENT_ACCOUNT_ROUTE_NAMES}; +export {getBankAccountIDAsNumber, getRouteForCurrentStep, hasInProgressUSDVBBA, hasInProgressVBBA, REIMBURSEMENT_ACCOUNT_ROUTE_NAMES}; export type {ReimbursementAccountStepToOpen}; diff --git a/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx b/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx index 50f622603918..a426ad5ffa4f 100644 --- a/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx +++ b/src/pages/ReimbursementAccount/ConnectedVerifiedBankAccount.tsx @@ -27,10 +27,10 @@ type ConnectedVerifiedBankAccountProps = { onBackButtonPress: () => void; /** Method to set the state of shouldShowConnectedVerifiedBankAccount */ - setShouldShowConnectedVerifiedBankAccount?: (shouldShowConnectedVerifiedBankAccount: boolean) => void; + setShouldShowConnectedVerifiedBankAccount: (shouldShowConnectedVerifiedBankAccount: boolean) => void; /** Method to set the state of USD bank account step */ - setUSDBankAccountStep?: (step: string | null) => void; + setUSDBankAccountStep: (step: string | null) => void; /** Whether the workspace currency is set to non USD currency */ isNonUSDWorkspace: boolean; diff --git a/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlowPage.tsx b/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlowPage.tsx index f8b065860289..8d675eac39e9 100644 --- a/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlowPage.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/NonUSDVerifiedBankAccountFlowPage.tsx @@ -121,7 +121,7 @@ function NonUSDVerifiedBankAccountFlowPage({route}: NonUSDVerifiedBankAccountFlo }, [backTo, currentPageIndex, pages, policyID]); return ( - + { - const stepToPageName: Record = { - [CONST.BANK_ACCOUNT.STEP.COUNTRY]: CONST.BANK_ACCOUNT.PAGE_NAMES.COUNTRY, - [CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT]: CONST.BANK_ACCOUNT.PAGE_NAMES.BANK_ACCOUNT, - [CONST.BANK_ACCOUNT.STEP.REQUESTOR]: CONST.BANK_ACCOUNT.PAGE_NAMES.REQUESTOR, - [CONST.BANK_ACCOUNT.STEP.COMPANY]: CONST.BANK_ACCOUNT.PAGE_NAMES.COMPANY, - [CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS]: CONST.BANK_ACCOUNT.PAGE_NAMES.BENEFICIAL_OWNERS, - [CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT]: CONST.BANK_ACCOUNT.PAGE_NAMES.ACH_CONTRACT, - [CONST.BANK_ACCOUNT.STEP.VALIDATION]: CONST.BANK_ACCOUNT.PAGE_NAMES.VALIDATION, - }; - const page = stepToPageName[currentStep] ?? CONST.BANK_ACCOUNT.PAGE_NAMES.COUNTRY; - Navigation.navigate(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID: policyIDParam, page, backTo})); + setShouldShowContinueSetupButton(false); + setUSDBankAccountStep(currentStep); }); - }, [currentStep, policyIDParam, backTo]); + }, [currentStep]); const continueNonUSDVBBASetup = () => { const {page: startPage, subPage: startSubPage} = getStartPageForContinueSetup(achData, nonUSDCountryDraftValue, policyCurrency, reimbursementAccountDraft); @@ -531,6 +529,20 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy}: Reimbursemen ); } + if (!isNonUSDSetup && USDBankAccountStep !== null) { + return ( + + ); + } + return ( { - Navigation.goBack(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID, backTo}), {compareParams: false}); + Navigation.goBack(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID, backTo, subStepToOpen: CONST.BANK_ACCOUNT.STEP.COUNTRY}), {compareParams: false}); }} /> ); diff --git a/src/pages/ReimbursementAccount/USD/BankInfo/BankInfo.tsx b/src/pages/ReimbursementAccount/USD/BankInfo/BankInfo.tsx index a5b3dc71988f..8ff529ef084d 100644 --- a/src/pages/ReimbursementAccount/USD/BankInfo/BankInfo.tsx +++ b/src/pages/ReimbursementAccount/USD/BankInfo/BankInfo.tsx @@ -2,7 +2,8 @@ import React, {useEffect, useRef} from 'react'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReimbursementAccountSubmitCallback from '@hooks/useReimbursementAccountSubmitCallback'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import getPlaidOAuthReceivedRedirectURI from '@libs/getPlaidOAuthReceivedRedirectURI'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; @@ -19,22 +20,27 @@ type BankInfoProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; - /** Current Policy ID */ policyID: string; + + /** Set the step of the USD verified bank account flow */ + setUSDBankAccountStep: (step: string | null) => void; +}; + +type BankInfoSubStepProps = SubStepProps & { + setUSDBankAccountStep: (step: string | null) => void; }; const BANK_INFO_STEP_KEYS = INPUT_IDS.BANK_INFO_STEP; +const manualSubSteps: Array> = [Manual]; +const plaidSubSteps: Array> = [Plaid]; const receivedRedirectURI = getPlaidOAuthReceivedRedirectURI(); -function BankInfo({onBackButtonPress, onSubmit, policyID}: BankInfoProps) { +function BankInfo({onBackButtonPress, policyID, setUSDBankAccountStep}: BankInfoProps) { const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); const [plaidLinkToken] = useOnyx(ONYXKEYS.RAM_ONLY_PLAID_LINK_TOKEN); const {translate} = useLocalize(); - const markSubmitting = useReimbursementAccountSubmitCallback(onSubmit); const redirectedFromPlaidToManualRef = useRef(false); const values = getSubStepValues(BANK_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount ?? {}); @@ -84,10 +90,10 @@ function BankInfo({onBackButtonPress, onSubmit, policyID}: BankInfoProps) { policyID, ); } - markSubmitting(); }; - const BankInfoPage = setupType === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID ? Plaid : Manual; + const bodyContent = setupType === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID ? plaidSubSteps : manualSubSteps; + const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent, startFrom: 0, onFinished: submit}); // Some services user connects to via Plaid return dummy account numbers and routing numbers e.g. Chase // In this case we need to redirect user to manual flow to enter real account number and routing number @@ -102,8 +108,12 @@ function BankInfo({onBackButtonPress, onSubmit, policyID}: BankInfoProps) { }, [setupType, values.bankName]); const handleBackButtonPress = () => { - onBackButtonPress(); - hideBankAccountErrors(); + if (screenIndex === 0) { + onBackButtonPress(); + hideBankAccountErrors(); + } else { + prevScreen(); + } }; return ( @@ -115,7 +125,12 @@ function BankInfo({onBackButtonPress, onSubmit, policyID}: BankInfoProps) { startStepIndex={1} stepNames={CONST.BANK_ACCOUNT.STEP_NAMES} > - + ); } diff --git a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Manual.tsx b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Manual.tsx index 53a090418c49..6e89550cb6d8 100644 --- a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Manual.tsx +++ b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Manual.tsx @@ -8,19 +8,21 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {getFieldRequiredErrors, isValidRoutingNumber} from '@libs/ValidationUtils'; import ExampleCheckImage from '@pages/ReimbursementAccount/USD/BankInfo/ExampleCheck'; -import type BankInfoSubStepProps from '@pages/ReimbursementAccount/USD/BankInfo/types'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; +type ManualProps = SubStepProps; + const BANK_INFO_STEP_KEYS = INPUT_IDS.BANK_INFO_STEP; const STEP_FIELDS = [BANK_INFO_STEP_KEYS.ROUTING_NUMBER, BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER]; -function Manual({onNext}: BankInfoSubStepProps) { +function Manual({onNext}: ManualProps) { const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); diff --git a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx index b64b07e70a45..d1719b8047fa 100644 --- a/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx +++ b/src/pages/ReimbursementAccount/USD/BankInfo/subSteps/Plaid.tsx @@ -6,17 +6,21 @@ import InputWrapper from '@components/Form/InputWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePrevious from '@hooks/usePrevious'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; -import type BankInfoSubStepProps from '@pages/ReimbursementAccount/USD/BankInfo/types'; import {setBankAccountSubStep, validatePlaidSelection} from '@userActions/BankAccounts'; import {updateReimbursementAccountDraft} from '@userActions/ReimbursementAccount'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; +type PlaidProps = SubStepProps & { + setUSDBankAccountStep: (step: string | null) => void; +}; + const BANK_INFO_STEP_KEYS = INPUT_IDS.BANK_INFO_STEP; -function Plaid({onNext}: BankInfoSubStepProps) { +function Plaid({onNext, setUSDBankAccountStep}: PlaidProps) { const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); const [plaidData] = useOnyx(ONYXKEYS.PLAID_DATA); @@ -57,10 +61,12 @@ function Plaid({onNext}: BankInfoSubStepProps) { return; } setBankAccountSubStep(null); - }, [isFocused, prevIsFocused, plaidData?.bankAccounts]); + setUSDBankAccountStep(null); + }, [isFocused, prevIsFocused, plaidData?.bankAccounts, setUSDBankAccountStep]); const handlePlaidExit = () => { setBankAccountSubStep(null); + setUSDBankAccountStep(null); }; return ( diff --git a/src/pages/ReimbursementAccount/USD/BankInfo/types.ts b/src/pages/ReimbursementAccount/USD/BankInfo/types.ts deleted file mode 100644 index e79203da7611..000000000000 --- a/src/pages/ReimbursementAccount/USD/BankInfo/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -type BankInfoSubStepProps = { - /** Continues to the next step */ - onNext: (data?: unknown) => void; -}; - -export default BankInfoSubStepProps; diff --git a/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormPages.tsx b/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormPages.tsx deleted file mode 100644 index 5ea2a967a3ae..000000000000 --- a/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormPages.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, {useCallback} from 'react'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useSubPage from '@hooks/useSubPage'; -import type {SubPageProps} from '@hooks/useSubPage/types'; -import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import AddressUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/AddressUBO'; -import ConfirmationUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/ConfirmationUBO'; -import DateOfBirthUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/DateOfBirthUBO'; -import LegalNameUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/LegalNameUBO'; -import SocialSecurityNumberUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/SocialSecurityNumberUBO'; - -const PAGE_NAMES = CONST.BANK_ACCOUNT.PAGE_NAMES; -const SUB_PAGE_NAMES = CONST.BANK_ACCOUNT.BENEFICIAL_OWNERS_STEP.SUB_PAGE_NAMES; - -type BeneficialOwnerSubPageProps = SubPageProps & { - beneficialOwnerBeingModifiedID: string; - setBeneficialOwnerBeingModifiedID?: (id: string) => void; -}; - -const pages = [ - {pageName: SUB_PAGE_NAMES.LEGAL_NAME, component: LegalNameUBO}, - {pageName: SUB_PAGE_NAMES.DATE_OF_BIRTH, component: DateOfBirthUBO}, - {pageName: SUB_PAGE_NAMES.SSN, component: SocialSecurityNumberUBO}, - {pageName: SUB_PAGE_NAMES.ADDRESS, component: AddressUBO}, - {pageName: SUB_PAGE_NAMES.CONFIRMATION, component: ConfirmationUBO}, -]; - -type BeneficialOwnerDetailsFormPagesProps = { - /** ID of current policy */ - policyID?: string; - - /** ID of the beneficial owner being modified */ - beneficialOwnerBeingModifiedID: string; - - /** Setter for the beneficial owner being modified */ - setBeneficialOwnerBeingModifiedID: (id: string) => void; - - /** Whether user is editing an already-created beneficial owner */ - isEditingCreatedBeneficialOwner: boolean; - - /** Callback triggered after the last form page is completed */ - onFinished: () => void; - - /** Back to URL for preserving navigation context */ - backTo?: string; -}; - -function BeneficialOwnerDetailsFormPages({ - policyID, - beneficialOwnerBeingModifiedID, - setBeneficialOwnerBeingModifiedID, - isEditingCreatedBeneficialOwner, - onFinished, - backTo, -}: BeneficialOwnerDetailsFormPagesProps) { - const {translate} = useLocalize(); - const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); - const hasExistingBeneficialOwners = (reimbursementAccountDraft?.beneficialOwnerKeys ?? []).length > 0; - - const buildRoute = useCallback( - (pageName: string, action?: 'edit') => ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: PAGE_NAMES.BENEFICIAL_OWNERS, subPage: pageName, action, backTo}), - [policyID, backTo], - ); - - const {CurrentPage, isEditing, currentPageName, pageIndex, prevPage, nextPage, moveTo, isRedirecting} = useSubPage({ - pages, - startFrom: 0, - onFinished, - buildRoute, - }); - - const handleBackButtonPress = useCallback(() => { - if (isEditing) { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.CONFIRMATION)); - return; - } - - if (pageIndex === 0) { - if (isEditingCreatedBeneficialOwner) { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.UBOS_LIST)); - } else if (hasExistingBeneficialOwners) { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS)); - } else { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO)); - } - } else { - prevPage(); - } - }, [buildRoute, isEditing, isEditingCreatedBeneficialOwner, pageIndex, prevPage, hasExistingBeneficialOwners]); - - if (isRedirecting) { - return ; - } - - return ( - - - - ); -} - -export default BeneficialOwnerDetailsFormPages; diff --git a/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnersStep.tsx b/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnersStep.tsx index ee0f5404c74b..83e6e31c1616 100644 --- a/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnersStep.tsx +++ b/src/pages/ReimbursementAccount/USD/BeneficialOwnerInfo/BeneficialOwnersStep.tsx @@ -1,46 +1,37 @@ import {Str} from 'expensify-common'; -import React, {useCallback, useEffect} from 'react'; +import React, {useState} from 'react'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import YesNoStep from '@components/SubStepForms/YesNoStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReimbursementAccountSubmitCallback from '@hooks/useReimbursementAccountSubmitCallback'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; import {updateBeneficialOwnersForBankAccount} from '@userActions/BankAccounts'; import {setDraftValues} from '@userActions/FormActions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import SafeString from '@src/utils/SafeString'; -import BeneficialOwnerDetailsFormPages from './BeneficialOwnerDetailsFormPages'; +import AddressUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/AddressUBO'; +import ConfirmationUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/ConfirmationUBO'; +import DateOfBirthUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/DateOfBirthUBO'; +import LegalNameUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/LegalNameUBO'; +import SocialSecurityNumberUBO from './subSteps/BeneficialOwnerDetailsFormSubSteps/SocialSecurityNumberUBO'; import CompanyOwnersListUBO from './subSteps/CompanyOwnersListUBO'; type BeneficialOwnersStepProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; - - /** Name of the current sub page */ - currentSubPage?: string; - - /** ID of current policy */ - policyID?: string; - - /** Back to URL for preserving navigation context */ - backTo?: string; }; -const PAGE_NAMES = CONST.BANK_ACCOUNT.PAGE_NAMES; -const SUB_PAGE_NAMES = CONST.BANK_ACCOUNT.BENEFICIAL_OWNERS_STEP.SUB_PAGE_NAMES; -const MAX_NUMBER_OF_UBOS = 4; +type BeneficialOwnerSubStepProps = SubStepProps & {beneficialOwnerBeingModifiedID: string; setBeneficialOwnerBeingModifiedID?: (id: string) => void}; -const OUTER_SUB_PAGES = new Set([SUB_PAGE_NAMES.IS_USER_UBO, SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO, SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS, SUB_PAGE_NAMES.UBOS_LIST]); +const SUBSTEP = CONST.BANK_ACCOUNT.BENEFICIAL_OWNER_INFO_STEP.SUBSTEP; +const MAX_NUMBER_OF_UBOS = 4; +const bodyContent: Array> = [LegalNameUBO, DateOfBirthUBO, SocialSecurityNumberUBO, AddressUBO, ConfirmationUBO]; -function BeneficialOwnersStep({onBackButtonPress, onSubmit, currentSubPage, policyID, backTo}: BeneficialOwnersStepProps) { +function BeneficialOwnersStep({onBackButtonPress}: BeneficialOwnersStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -48,53 +39,27 @@ function BeneficialOwnersStep({onBackButtonPress, onSubmit, currentSubPage, poli const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); const companyName = reimbursementAccount?.achData?.companyName ?? ''; - const markSubmitting = useReimbursementAccountSubmitCallback(onSubmit); - - // Read state from Onyx draft so it survives URL-based navigation (component remounts) - const isUserUBO = reimbursementAccountDraft?.ownsMoreThan25Percent ?? reimbursementAccount?.achData?.ownsMoreThan25Percent ?? false; - const beneficialOwners = reimbursementAccount?.achData?.beneficialOwners; - const isAnyoneElseUBO = beneficialOwners?.length ? true : (reimbursementAccountDraft?.hasOtherBeneficialOwners ?? false); - const beneficialOwnerKeys: string[] = reimbursementAccountDraft?.beneficialOwnerKeys ?? reimbursementAccount?.achData?.beneficialOwnerKeys ?? []; - // eslint-disable-next-line rulesdir/no-default-id-values - const beneficialOwnerBeingModifiedID = reimbursementAccountDraft?.ownerBeingModifiedID ?? ''; - const isEditingCreatedBeneficialOwner = reimbursementAccountDraft?.isEditingCreatedOwner ?? false; - const canAddMoreUBOS = beneficialOwnerKeys.length < (isUserUBO ? MAX_NUMBER_OF_UBOS - 1 : MAX_NUMBER_OF_UBOS); - - const hasCompletedBeneficialOwnersStep = reimbursementAccount?.achData?.ownsMoreThan25Percent !== undefined; - - // Redirect to the correct sub-page if no subPage is in the URL - useEffect(() => { - if (currentSubPage) { - return; - } - - let subPage: string = SUB_PAGE_NAMES.IS_USER_UBO; - if (isUserUBO || (isAnyoneElseUBO && beneficialOwnerKeys.length > 0)) { - subPage = SUB_PAGE_NAMES.UBOS_LIST; - } else if (hasCompletedBeneficialOwnersStep) { - subPage = SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO; - } - - Navigation.setParams({subPage} as Record); - }, [currentSubPage, policyID, backTo, isAnyoneElseUBO, beneficialOwnerKeys.length, isUserUBO, hasCompletedBeneficialOwnersStep]); - - const navigateToSubPage = useCallback( - (subPage: string) => { - Navigation.navigate(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: PAGE_NAMES.BENEFICIAL_OWNERS, subPage, backTo})); - }, - [policyID, backTo], - ); + const policyID = reimbursementAccount?.achData?.policyID; + const defaultValues = { + ownsMoreThan25Percent: reimbursementAccount?.achData?.ownsMoreThan25Percent ?? reimbursementAccountDraft?.ownsMoreThan25Percent ?? false, + hasOtherBeneficialOwners: reimbursementAccount?.achData?.hasOtherBeneficialOwners ?? reimbursementAccountDraft?.hasOtherBeneficialOwners ?? false, + beneficialOwnerKeys: reimbursementAccount?.achData?.beneficialOwnerKeys ?? reimbursementAccountDraft?.beneficialOwnerKeys ?? [], + }; - const navigateBackToSubPage = useCallback( - (subPage: string) => { - Navigation.goBack(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: PAGE_NAMES.BENEFICIAL_OWNERS, subPage, backTo})); - }, - [policyID, backTo], - ); + // We're only reading beneficialOwnerKeys from draft values because there is not option to remove UBO + // if we were to set them based on values saved in BE then there would be no option to enter different UBOs + // user would always see the same UBOs that was saved in BE when returning to this step and trying to change something + const [beneficialOwnerKeys, setBeneficialOwnerKeys] = useState(defaultValues.beneficialOwnerKeys); + const [beneficialOwnerBeingModifiedID, setBeneficialOwnerBeingModifiedID] = useState(''); + const [isEditingCreatedBeneficialOwner, setIsEditingCreatedBeneficialOwner] = useState(false); + const [isUserUBO, setIsUserUBO] = useState(defaultValues.ownsMoreThan25Percent); + const [isAnyoneElseUBO, setIsAnyoneElseUBO] = useState(defaultValues.hasOtherBeneficialOwners); + const [currentUBOSubStep, setCurrentUBOSubStep] = useState(1); + const canAddMoreUBOS = beneficialOwnerKeys.length < (isUserUBO ? MAX_NUMBER_OF_UBOS - 1 : MAX_NUMBER_OF_UBOS); const submit = () => { const beneficialOwnerFields = ['firstName', 'lastName', 'dob', 'ssnLast4', 'street', 'city', 'state', 'zipCode']; - const beneficialOwnersData = beneficialOwnerKeys.map((ownerKey) => + const beneficialOwners = beneficialOwnerKeys.map((ownerKey) => beneficialOwnerFields.reduce( (acc, fieldName) => { acc[fieldName] = reimbursementAccountDraft ? SafeString(reimbursementAccountDraft[`beneficialOwner_${ownerKey}_${fieldName}`]) : undefined; @@ -108,56 +73,75 @@ function BeneficialOwnersStep({onBackButtonPress, onSubmit, currentSubPage, poli getBankAccountIDAsNumber(reimbursementAccount?.achData), { ownsMoreThan25Percent: isUserUBO, - beneficialOwners: JSON.stringify(beneficialOwnersData), + beneficialOwners: JSON.stringify(beneficialOwners), beneficialOwnerKeys, }, policyID, ); - markSubmitting(); }; const addBeneficialOwner = (beneficialOwnerID: string) => { + // Each beneficial owner is assigned a unique key that will connect it to values in saved ONYX. + // That way we can dynamically render each Identity Form based on which keys are present in the beneficial owners array. const newBeneficialOwners = [...beneficialOwnerKeys, beneficialOwnerID]; - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {beneficialOwnerKeys: newBeneficialOwners, beneficialOwners: JSON.stringify(newBeneficialOwners)}); - }; + setBeneficialOwnerKeys(newBeneficialOwners); + setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {beneficialOwners: JSON.stringify(newBeneficialOwners)}); + }; const handleBeneficialOwnerDetailsFormSubmit = () => { const shouldAddBeneficialOwner = !beneficialOwnerKeys.find((beneficialOwnerID) => beneficialOwnerID === beneficialOwnerBeingModifiedID) && canAddMoreUBOS; - if (shouldAddBeneficialOwner && beneficialOwnerBeingModifiedID) { + if (shouldAddBeneficialOwner) { addBeneficialOwner(beneficialOwnerBeingModifiedID); } + // Because beneficialOwnerKeys array is not yet updated at this point we need to check against lower MAX_NUMBER_OF_UBOS (account for the one that is being added) const isLastUBOThatCanBeAdded = beneficialOwnerKeys.length === (isUserUBO ? MAX_NUMBER_OF_UBOS - 2 : MAX_NUMBER_OF_UBOS - 1); - const nextSubPage = isEditingCreatedBeneficialOwner || isLastUBOThatCanBeAdded ? SUB_PAGE_NAMES.UBOS_LIST : SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS; - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {isEditingCreatedOwner: false}); - navigateToSubPage(nextSubPage); + setCurrentUBOSubStep(isEditingCreatedBeneficialOwner || isLastUBOThatCanBeAdded ? SUBSTEP.UBOS_LIST : SUBSTEP.ARE_THERE_MORE_UBOS); + setIsEditingCreatedBeneficialOwner(false); }; + const { + componentToRender: BeneficialOwnerDetailsForm, + isEditing, + screenIndex, + nextScreen, + prevScreen, + moveTo, + resetScreenIndex, + goToTheLastStep, + } = useSubStep({ + bodyContent, + startFrom: 0, + onFinished: handleBeneficialOwnerDetailsFormSubmit, + }); + const prepareBeneficialOwnerDetailsForm = () => { const beneficialOwnerID = Str.guid(); - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {ownerBeingModifiedID: beneficialOwnerID}); - navigateToSubPage(SUB_PAGE_NAMES.LEGAL_NAME); + setBeneficialOwnerBeingModifiedID(beneficialOwnerID); + // Reset Beneficial Owner Details Form to first subStep + resetScreenIndex(); + setCurrentUBOSubStep(SUBSTEP.UBO_DETAILS_FORM); }; const handleNextUBOSubstep = (value: boolean) => { - if (currentSubPage === SUB_PAGE_NAMES.IS_USER_UBO) { - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {ownsMoreThan25Percent: value}); + if (currentUBOSubStep === SUBSTEP.IS_USER_UBO) { + setIsUserUBO(value); // User is an owner but there are 4 other owners already added, so we remove last one if (value && beneficialOwnerKeys.length === 4) { - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {beneficialOwnerKeys: beneficialOwnerKeys.slice(0, 3)}); + setBeneficialOwnerKeys((previousBeneficialOwners) => previousBeneficialOwners.slice(0, 3)); } - navigateToSubPage(SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO); + setCurrentUBOSubStep(SUBSTEP.IS_ANYONE_ELSE_UBO); return; } - if (currentSubPage === SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO) { - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {hasOtherBeneficialOwners: value}); + if (currentUBOSubStep === SUBSTEP.IS_ANYONE_ELSE_UBO) { + setIsAnyoneElseUBO(value); if (!canAddMoreUBOS && value) { - navigateToSubPage(SUB_PAGE_NAMES.UBOS_LIST); + setCurrentUBOSubStep(SUBSTEP.UBOS_LIST); return; } @@ -166,62 +150,66 @@ function BeneficialOwnersStep({onBackButtonPress, onSubmit, currentSubPage, poli return; } + // User is not an owner and no one else is an owner if (!isUserUBO && !value) { submit(); return; } + // User is an owner and no one else is an owner if (isUserUBO && !value) { - navigateToSubPage(SUB_PAGE_NAMES.UBOS_LIST); + setCurrentUBOSubStep(SUBSTEP.UBOS_LIST); return; } } - if (currentSubPage === SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS) { + // Are there more UBOs + if (currentUBOSubStep === SUBSTEP.ARE_THERE_MORE_UBOS) { if (value) { prepareBeneficialOwnerDetailsForm(); return; } - navigateToSubPage(SUB_PAGE_NAMES.UBOS_LIST); + setCurrentUBOSubStep(SUBSTEP.UBOS_LIST); + return; + } + + // User reached the limit of UBOs + if (currentUBOSubStep === SUBSTEP.UBO_DETAILS_FORM && !canAddMoreUBOS) { + setCurrentUBOSubStep(SUBSTEP.UBOS_LIST); } }; const handleBackButtonPress = () => { - if (currentSubPage === SUB_PAGE_NAMES.IS_USER_UBO) { + if (isEditing) { + goToTheLastStep(); + return; + } + + // User goes back to previous step + if (currentUBOSubStep === SUBSTEP.IS_USER_UBO) { onBackButtonPress(); - } else if (currentSubPage === SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO) { - navigateBackToSubPage(SUB_PAGE_NAMES.IS_USER_UBO); - } else if (currentSubPage === SUB_PAGE_NAMES.UBOS_LIST && !canAddMoreUBOS) { - navigateBackToSubPage(SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO); - } else if (currentSubPage === SUB_PAGE_NAMES.UBOS_LIST && isAnyoneElseUBO) { - navigateBackToSubPage(SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS); - } else if (currentSubPage === SUB_PAGE_NAMES.UBOS_LIST && isUserUBO && !isAnyoneElseUBO) { - navigateBackToSubPage(SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO); + // User reached limit of UBOs and goes back to initial question about additional UBOs + } else if (currentUBOSubStep === SUBSTEP.UBOS_LIST && !canAddMoreUBOS) { + setCurrentUBOSubStep(SUBSTEP.IS_ANYONE_ELSE_UBO); + // User goes back to last radio button + } else if (currentUBOSubStep === SUBSTEP.UBOS_LIST && isAnyoneElseUBO) { + setCurrentUBOSubStep(SUBSTEP.ARE_THERE_MORE_UBOS); + } else if (currentUBOSubStep === SUBSTEP.UBOS_LIST && isUserUBO && !isAnyoneElseUBO) { + setCurrentUBOSubStep(SUBSTEP.IS_ANYONE_ELSE_UBO); + // User moves between subSteps of beneficial owner details form + } else if (currentUBOSubStep === SUBSTEP.UBO_DETAILS_FORM && screenIndex > 0) { + prevScreen(); } else { - Navigation.goBack(); + setCurrentUBOSubStep((currentSubstep) => currentSubstep - 1); } }; const handleUBOEdit = (beneficialOwnerID: string) => { - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {ownerBeingModifiedID: beneficialOwnerID, isEditingCreatedOwner: true}); - navigateToSubPage(SUB_PAGE_NAMES.LEGAL_NAME); + setBeneficialOwnerBeingModifiedID(beneficialOwnerID); + setIsEditingCreatedBeneficialOwner(true); + setCurrentUBOSubStep(SUBSTEP.UBO_DETAILS_FORM); }; - // If the current sub page is not an outer page, render the details form - if (currentSubPage && !OUTER_SUB_PAGES.has(currentSubPage)) { - return ( - setDraftValues(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {ownerBeingModifiedID: id})} - isEditingCreatedBeneficialOwner={isEditingCreatedBeneficialOwner} - onFinished={handleBeneficialOwnerDetailsFormSubmit} - backTo={backTo} - /> - ); - } - return ( - {currentSubPage === SUB_PAGE_NAMES.IS_USER_UBO && ( + {currentUBOSubStep === SUBSTEP.IS_USER_UBO && ( )} - {currentSubPage === SUB_PAGE_NAMES.IS_ANYONE_ELSE_UBO && ( + {currentUBOSubStep === SUBSTEP.IS_ANYONE_ELSE_UBO && ( )} - {currentSubPage === SUB_PAGE_NAMES.ARE_THERE_MORE_UBOS && ( + {currentUBOSubStep === SUBSTEP.UBO_DETAILS_FORM && ( + + )} + + {currentUBOSubStep === SUBSTEP.ARE_THERE_MORE_UBOS && ( )} - {currentSubPage === SUB_PAGE_NAMES.UBOS_LIST && ( + {currentUBOSubStep === SUBSTEP.UBOS_LIST && ( ); } diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/BusinessInfo.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/BusinessInfo.tsx index 60e87e064fcf..be7ff00b0080 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/BusinessInfo.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/BusinessInfo.tsx @@ -1,14 +1,11 @@ import {Str} from 'expensify-common'; import lodashPick from 'lodash/pick'; import React, {useCallback, useMemo} from 'react'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReimbursementAccountSubmitCallback from '@hooks/useReimbursementAccountSubmitCallback'; -import useSubPage from '@hooks/useSubPage'; -import type {SubPageProps} from '@hooks/useSubPage/types'; -import Navigation from '@libs/Navigation/Navigation'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; import {isValidWebsite} from '@libs/ValidationUtils'; @@ -17,7 +14,6 @@ import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues import {updateCompanyInformationForBankAccount} from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; import AddressBusiness from './subSteps/AddressBusiness'; import ConfirmationBusiness from './subSteps/ConfirmationBusiness'; @@ -33,32 +29,24 @@ import WebsiteBusiness from './subSteps/WebsiteBusiness'; type BusinessInfoProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; - - /** Back to URL for preserving navigation context */ - backTo?: string; }; const BUSINESS_INFO_STEP_KEYS = INPUT_IDS.BUSINESS_INFO_STEP; -const PAGE_NAMES = CONST.BANK_ACCOUNT.PAGE_NAMES; -const SUB_PAGE_NAMES = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.SUB_PAGE_NAMES; -const pages = [ - {pageName: SUB_PAGE_NAMES.NAME, component: NameBusiness}, - {pageName: SUB_PAGE_NAMES.TAX_ID, component: TaxIdBusiness}, - {pageName: SUB_PAGE_NAMES.WEBSITE, component: WebsiteBusiness}, - {pageName: SUB_PAGE_NAMES.PHONE, component: PhoneNumberBusiness}, - {pageName: SUB_PAGE_NAMES.ADDRESS, component: AddressBusiness}, - {pageName: SUB_PAGE_NAMES.TYPE, component: TypeBusiness}, - {pageName: SUB_PAGE_NAMES.INCORPORATION_DATE, component: IncorporationDateBusiness}, - {pageName: SUB_PAGE_NAMES.INCORPORATION_STATE, component: IncorporationStateBusiness}, - {pageName: SUB_PAGE_NAMES.INCORPORATION_CODE, component: IncorporationCode}, - {pageName: SUB_PAGE_NAMES.CONFIRMATION, component: ConfirmationBusiness}, +const bodyContent: Array> = [ + NameBusiness, + TaxIdBusiness, + WebsiteBusiness, + PhoneNumberBusiness, + AddressBusiness, + TypeBusiness, + IncorporationDateBusiness, + IncorporationStateBusiness, + IncorporationCode, + ConfirmationBusiness, ]; -function BusinessInfo({onBackButtonPress, onSubmit, backTo}: BusinessInfoProps) { +function BusinessInfo({onBackButtonPress}: BusinessInfoProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); @@ -73,7 +61,6 @@ function BusinessInfo({onBackButtonPress, onSubmit, backTo}: BusinessInfoProps) const policyID = reimbursementAccount?.achData?.policyID; const bankAccountID = getBankAccountIDAsNumber(reimbursementAccount?.achData); - const markSubmitting = useReimbursementAccountSubmitCallback(onSubmit); const values = useMemo(() => getSubStepValues(BUSINESS_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const submit = useCallback( @@ -98,39 +85,29 @@ function BusinessInfo({onBackButtonPress, onSubmit, backTo}: BusinessInfoProps) const isBankAccountVerifying = reimbursementAccount?.achData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING; const startFrom = useMemo(() => (isBankAccountVerifying ? 0 : getInitialSubStepForBusinessInfo(values)), [values, isBankAccountVerifying]); - const buildRoute = useCallback( - (pageName: string, action?: 'edit') => ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: PAGE_NAMES.COMPANY, subPage: pageName, action, backTo}), - [policyID, backTo], - ); - - const {CurrentPage, isEditing, currentPageName, pageIndex, nextPage, prevPage, moveTo, isRedirecting} = useSubPage({ - pages, - startFrom, - onFinished: () => { - submit(true); - markSubmitting(); - }, - onPageChange: () => submit(false), - buildRoute, - }); + const { + componentToRender: SubStep, + isEditing, + screenIndex, + nextScreen, + prevScreen, + moveTo, + goToTheLastStep, + } = useSubStep({bodyContent, startFrom, onFinished: () => submit(true), onNextSubStep: () => submit(false)}); const handleBackButtonPress = () => { if (isEditing) { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.CONFIRMATION)); + goToTheLastStep(); return; } - if (pageIndex === 0) { + if (screenIndex === 0) { onBackButtonPress(); } else { - prevPage(); + prevScreen(); } }; - if (isRedirecting) { - return ; - } - return ( - ); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/AddressBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/AddressBusiness.tsx index 69fc14e84caa..9d68d1da372f 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/AddressBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/AddressBusiness.tsx @@ -4,7 +4,7 @@ import AddressStep from '@components/SubStepForms/AddressStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; @@ -21,7 +21,7 @@ const INPUT_KEYS = { const STEP_FIELDS = [COMPANY_BUSINESS_INFO_KEY.STREET, COMPANY_BUSINESS_INFO_KEY.CITY, COMPANY_BUSINESS_INFO_KEY.STATE, COMPANY_BUSINESS_INFO_KEY.ZIP_CODE]; -function AddressBusiness({onNext, onMove, isEditing}: SubPageProps) { +function AddressBusiness({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/ConfirmationBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/ConfirmationBusiness.tsx index 2ce528bc2118..4bdeebd55e21 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/ConfirmationBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/ConfirmationBusiness.tsx @@ -10,7 +10,7 @@ import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; @@ -35,7 +35,7 @@ function ConfirmCompanyLabel() { ); } -function ConfirmationBusiness({onNext, onMove}: SubPageProps) { +function ConfirmationBusiness({onNext, onMove}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -106,11 +106,7 @@ function ConfirmationBusiness({onNext, onMove}: SubPageProps) { /> { onMove(BUSINESS_INFO_STEP_INDEXES.COMPANY_TYPE); @@ -126,7 +122,7 @@ function ConfirmationBusiness({onNext, onMove}: SubPageProps) { /> { onMove(BUSINESS_INFO_STEP_INDEXES.INCORPORATION_STATE); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx index 8cca170661eb..579ac48913fe 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx @@ -6,7 +6,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {isValidIndustryCode} from '@libs/ValidationUtils'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -16,7 +16,7 @@ import IndustryCodeSelector from './IndustryCode/IndustryCodeSelector'; const COMPANY_INCORPORATION_CODE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_CODE; const STEP_FIELDS = [COMPANY_INCORPORATION_CODE_KEY]; -function IncorporationCode({onNext, isEditing}: SubPageProps) { +function IncorporationCode({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationDateBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationDateBusiness.tsx index 7674f9b770d6..2febf55e42b5 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationDateBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationDateBusiness.tsx @@ -8,7 +8,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors, isValidDate, isValidPastDate} from '@libs/ValidationUtils'; @@ -19,7 +19,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const COMPANY_INCORPORATION_DATE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_DATE; const STEP_FIELDS = [COMPANY_INCORPORATION_DATE_KEY]; -function IncorporationDateBusiness({onNext, isEditing}: SubPageProps) { +function IncorporationDateBusiness({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationStateBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationStateBusiness.tsx index 24d899552ea9..5d10d77469b0 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationStateBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationStateBusiness.tsx @@ -9,7 +9,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; @@ -25,7 +25,7 @@ const validate = ( translate: LocalizedTranslate, ): FormInputErrors => getFieldRequiredErrors(values, STEP_FIELDS, translate); -function IncorporationStateBusiness({onNext, isEditing}: SubPageProps) { +function IncorporationStateBusiness({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IndustryCode/IndustryCodeSelector.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IndustryCode/IndustryCodeSelector.tsx index b5ced220b5bd..1584b9e5b93f 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IndustryCode/IndustryCodeSelector.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IndustryCode/IndustryCodeSelector.tsx @@ -1,12 +1,9 @@ -import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import SelectionList from '@components/SelectionList'; import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem'; -import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; import {ALL_NAICS, NAICS, NAICS_MAPPING_WITH_ID} from '@src/NAICS'; type IndustryCodeSelectorProps = { @@ -17,25 +14,11 @@ type IndustryCodeSelectorProps = { function IndustryCodeSelector({onInputChange, value, errorText}: IndustryCodeSelectorProps) { const styles = useThemeStyles(); - const selectionListRef = useRef>(null); const [searchValue, setSearchValue] = useState(value); - const [isReady, setIsReady] = useState(false); const [shouldDisplayChildItems, setShouldDisplayChildItems] = useState(false); const {translate} = useLocalize(); - // Delay rendering the list and focusing the input until the screen transition animation completes. - useFocusEffect( - useCallback(() => { - const timeout = setTimeout(() => { - setIsReady(true); - selectionListRef.current?.focusTextInput(); - }, CONST.ANIMATED_TRANSITION); - - return () => clearTimeout(timeout); - }, []), - ); - const codeOptions = useMemo(() => { if (!searchValue) { return NAICS.map((item) => { @@ -82,7 +65,6 @@ function IndustryCodeSelector({onInputChange, value, errorText}: IndustryCodeSel onInputChange?.(val); }, value: searchValue, - disableAutoFocus: true, errorText, }), [errorText, onInputChange, searchValue, translate], @@ -91,8 +73,7 @@ function IndustryCodeSelector({onInputChange, value, errorText}: IndustryCodeSel return ( { setSearchValue(item.value); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/NameBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/NameBusiness.tsx index fd819f39875d..6e18b09b2923 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/NameBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/NameBusiness.tsx @@ -4,7 +4,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {getFieldRequiredErrors, isValidCompanyName} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -13,7 +13,7 @@ import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; const COMPANY_NAME_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_NAME; const STEP_FIELDS = [COMPANY_NAME_KEY]; -function NameBusiness({onNext, onMove, isEditing}: SubPageProps) { +function NameBusiness({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -63,7 +63,6 @@ function NameBusiness({onNext, onMove, isEditing}: SubPageProps) { shouldUseDefaultValue={shouldDisableCompanyName} disabled={shouldDisableCompanyName} shouldShowHelpLinks={false} - shouldDelayAutoFocus /> ); } diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/PhoneNumberBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/PhoneNumberBusiness.tsx index 85dec7f7c1f0..4437c83e6228 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/PhoneNumberBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/PhoneNumberBusiness.tsx @@ -5,7 +5,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors, isValidUSPhone} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; @@ -16,7 +16,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const COMPANY_PHONE_NUMBER_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_PHONE; const STEP_FIELDS = [COMPANY_PHONE_NUMBER_KEY]; -function PhoneNumberBusiness({onNext, onMove, isEditing}: SubPageProps) { +function PhoneNumberBusiness({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -67,7 +67,6 @@ function PhoneNumberBusiness({onNext, onMove, isEditing}: SubPageProps) { defaultValue={defaultCompanyPhoneNumber} shouldShowHelpLinks={false} placeholder={translate('common.phoneNumberPlaceholder')} - shouldDelayAutoFocus /> ); } diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TaxIdBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TaxIdBusiness.tsx index 5e3e9b13f73e..94a85398e8fb 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TaxIdBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TaxIdBusiness.tsx @@ -5,7 +5,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors, isValidTaxID} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; @@ -15,7 +15,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const COMPANY_TAX_ID_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_TAX_ID; const STEP_FIELDS = [COMPANY_TAX_ID_KEY]; -function TaxIdBusiness({onNext, onMove, isEditing}: SubPageProps) { +function TaxIdBusiness({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -77,7 +77,6 @@ function TaxIdBusiness({onNext, onMove, isEditing}: SubPageProps) { shouldShowHelpLinks={false} placeholder={translate('businessInfoStep.taxIDNumberPlaceholder')} inputMode={CONST.INPUT_MODE.NUMERIC} - shouldDelayAutoFocus /> ); } diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TypeBusiness/TypeBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TypeBusiness/TypeBusiness.tsx index 0154ec35c188..f8a296965496 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TypeBusiness/TypeBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/TypeBusiness/TypeBusiness.tsx @@ -7,7 +7,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; @@ -19,7 +19,7 @@ import BusinessTypePicker from './BusinessTypePicker'; const COMPANY_INCORPORATION_TYPE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_TYPE; const STEP_FIELDS = [COMPANY_INCORPORATION_TYPE_KEY]; -function TypeBusiness({onNext, isEditing}: SubPageProps) { +function TypeBusiness({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/WebsiteBusiness.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/WebsiteBusiness.tsx index 7214ee4aa687..8a15cbcf8908 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/WebsiteBusiness.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/WebsiteBusiness.tsx @@ -6,7 +6,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {getDefaultCompanyWebsite} from '@libs/BankAccountUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors, isValidWebsite} from '@libs/ValidationUtils'; @@ -19,7 +19,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const COMPANY_WEBSITE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_WEBSITE; const STEP_FIELDS = [COMPANY_WEBSITE_KEY]; -function WebsiteBusiness({onNext, onMove, isEditing}: SubPageProps) { +function WebsiteBusiness({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const isLoadingReimbursementAccount = isLoadingOnyxValue(reimbursementAccountResult); @@ -74,7 +74,6 @@ function WebsiteBusiness({onNext, onMove, isEditing}: SubPageProps) { defaultValue={defaultCompanyWebsite} inputMode={CONST.INPUT_MODE.URL} shouldShowHelpLinks={false} - shouldDelayAutoFocus /> ); } diff --git a/src/pages/ReimbursementAccount/USD/CompleteVerification/CompleteVerification.tsx b/src/pages/ReimbursementAccount/USD/CompleteVerification/CompleteVerification.tsx index 4110007599d6..8b99de40b19c 100644 --- a/src/pages/ReimbursementAccount/USD/CompleteVerification/CompleteVerification.tsx +++ b/src/pages/ReimbursementAccount/USD/CompleteVerification/CompleteVerification.tsx @@ -1,8 +1,10 @@ import React, {useCallback, useMemo} from 'react'; +import type {ComponentType} from 'react'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReimbursementAccountSubmitCallback from '@hooks/useReimbursementAccountSubmitCallback'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; import {acceptACHContractForBankAccount} from '@userActions/BankAccounts'; @@ -14,14 +16,12 @@ import ConfirmAgreements from './subSteps/ConfirmAgreements'; type CompleteVerificationProps = { /** Handles back button press */ onBackButtonPress: () => void; - - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; }; const COMPLETE_VERIFICATION_KEYS = INPUT_IDS.COMPLETE_VERIFICATION; +const bodyContent: Array> = [ConfirmAgreements]; -function CompleteVerification({onBackButtonPress, onSubmit}: CompleteVerificationProps) { +function CompleteVerification({onBackButtonPress}: CompleteVerificationProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -31,7 +31,6 @@ function CompleteVerification({onBackButtonPress, onSubmit}: CompleteVerificatio const values = useMemo(() => getSubStepValues(COMPLETE_VERIFICATION_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const policyID = reimbursementAccount?.achData?.policyID; const bankAccountID = getBankAccountIDAsNumber(reimbursementAccount?.achData); - const markSubmitting = useReimbursementAccountSubmitCallback(onSubmit); const submit = useCallback(() => { acceptACHContractForBankAccount( @@ -44,8 +43,22 @@ function CompleteVerification({onBackButtonPress, onSubmit}: CompleteVerificatio policyID, policyID ? lastPaymentMethod?.[policyID] : undefined, ); - markSubmitting(); - }, [bankAccountID, values.isAuthorizedToUseBankAccount, values.certifyTrueInformation, values.acceptTermsAndConditions, policyID, lastPaymentMethod, markSubmitting]); + }, [bankAccountID, values.isAuthorizedToUseBankAccount, values.certifyTrueInformation, values.acceptTermsAndConditions, policyID, lastPaymentMethod]); + + const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo, goToTheLastStep} = useSubStep({bodyContent, startFrom: 0, onFinished: submit}); + + const handleBackButtonPress = () => { + if (isEditing) { + goToTheLastStep(); + return; + } + + if (screenIndex === 0) { + onBackButtonPress(); + } else { + prevScreen(); + } + }; return ( - + ); } diff --git a/src/pages/ReimbursementAccount/USD/CompleteVerification/subSteps/ConfirmAgreements.tsx b/src/pages/ReimbursementAccount/USD/CompleteVerification/subSteps/ConfirmAgreements.tsx index ad825930e2f1..0799cece1418 100644 --- a/src/pages/ReimbursementAccount/USD/CompleteVerification/subSteps/ConfirmAgreements.tsx +++ b/src/pages/ReimbursementAccount/USD/CompleteVerification/subSteps/ConfirmAgreements.tsx @@ -7,12 +7,15 @@ import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import {getFieldRequiredErrors, isRequiredFulfilled} from '@libs/ValidationUtils'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; +type ConfirmAgreementsProps = SubStepProps; + const COMPLETE_VERIFICATION_KEYS = INPUT_IDS.COMPLETE_VERIFICATION; const STEP_FIELDS = [ INPUT_IDS.COMPLETE_VERIFICATION.IS_AUTHORIZED_TO_USE_BANK_ACCOUNT, @@ -35,11 +38,6 @@ function TermsAndConditionsLabel() { return ; } -type ConfirmAgreementsProps = { - /** Continues to the next step */ - onNext: () => void; -}; - function ConfirmAgreements({onNext}: ConfirmAgreementsProps) { const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [reimbursementAccountDraft] = useOnyx(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT); diff --git a/src/pages/ReimbursementAccount/USD/ConnectBankAccount/ConnectBankAccount.tsx b/src/pages/ReimbursementAccount/USD/ConnectBankAccount/ConnectBankAccount.tsx index 640215e572f3..5d44b1b79949 100644 --- a/src/pages/ReimbursementAccount/USD/ConnectBankAccount/ConnectBankAccount.tsx +++ b/src/pages/ReimbursementAccount/USD/ConnectBankAccount/ConnectBankAccount.tsx @@ -1,7 +1,6 @@ import {hasSeenTourSelector} from '@selectors/Onboarding'; import React from 'react'; import {View} from 'react-native'; -import ConfirmationPage from '@components/ConfirmationPage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -9,14 +8,10 @@ import TextLink from '@components/TextLink'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; -import {isFullScreenName} from '@navigation/helpers/isNavigatorName'; -import Navigation from '@navigation/Navigation'; import ConnectedVerifiedBankAccount from '@pages/ReimbursementAccount/ConnectedVerifiedBankAccount'; import {navigateToConciergeChat} from '@userActions/Report'; import CONST from '@src/CONST'; -import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import BankAccountValidationForm from './components/BankAccountValidationForm'; import FinishChatCard from './components/FinishChatCard'; @@ -26,16 +21,15 @@ type ConnectBankAccountProps = { onBackButtonPress: () => void; /** Method to set the state of shouldShowConnectedVerifiedBankAccount */ - setShouldShowConnectedVerifiedBankAccount?: (shouldShowConnectedVerifiedBankAccount: boolean) => void; + setShouldShowConnectedVerifiedBankAccount: (shouldShowConnectedVerifiedBankAccount: boolean) => void; /** Method to set the state of shouldShowConnectedVerifiedBankAccount */ - setUSDBankAccountStep?: (step: string | null) => void; + setUSDBankAccountStep: (step: string | null) => void; }; function ConnectBankAccount({onBackButtonPress, setShouldShowConnectedVerifiedBankAccount, setUSDBankAccountStep}: ConnectBankAccountProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const topmostFullScreenRoute = useRootNavigationState((state) => state?.routes.findLast((lastRoute) => isFullScreenName(lastRoute.name))); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${reimbursementAccount?.achData?.policyID}`); @@ -51,28 +45,10 @@ function ConnectBankAccount({onBackButtonPress, setShouldShowConnectedVerifiedBa // If a user tries to navigate directly to the validate page we'll show them the EnableStep if (bankAccountState === CONST.BANK_ACCOUNT.STATE.OPEN) { - if (topmostFullScreenRoute?.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR) { - return ( - - Navigation.dismissModal()} - /> - Navigation.dismissModal()} - /> - - ); - } return ( Navigation.dismissModal()} + onBackButtonPress={onBackButtonPress} setShouldShowConnectedVerifiedBankAccount={setShouldShowConnectedVerifiedBankAccount} setUSDBankAccountStep={setUSDBankAccountStep} isNonUSDWorkspace={false} diff --git a/src/pages/ReimbursementAccount/USD/ConnectBankAccount/components/FinishChatCard.tsx b/src/pages/ReimbursementAccount/USD/ConnectBankAccount/components/FinishChatCard.tsx index 9493fad76a0d..d1add2f1e6bd 100644 --- a/src/pages/ReimbursementAccount/USD/ConnectBankAccount/components/FinishChatCard.tsx +++ b/src/pages/ReimbursementAccount/USD/ConnectBankAccount/components/FinishChatCard.tsx @@ -26,7 +26,7 @@ type FinishChatCardProps = { requiresTwoFactorAuth: boolean; /** Method to set the state of USD bank account step */ - setUSDBankAccountStep?: (step: string | null) => void; + setUSDBankAccountStep: (step: string | null) => void; }; function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount, setUSDBankAccountStep}: FinishChatCardProps) { @@ -78,7 +78,7 @@ function FinishChatCard({requiresTwoFactorAuth, reimbursementAccount, setUSDBank title={translate('workspace.bankAccount.updateDetails')} onPress={() => { setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL).then(() => { - setUSDBankAccountStep?.(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + setUSDBankAccountStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); }); }} diff --git a/src/pages/ReimbursementAccount/USD/Country/index.tsx b/src/pages/ReimbursementAccount/USD/Country/index.tsx index ded09899c388..b4a28c913745 100644 --- a/src/pages/ReimbursementAccount/USD/Country/index.tsx +++ b/src/pages/ReimbursementAccount/USD/Country/index.tsx @@ -7,20 +7,24 @@ type CountryProps = { /** Handles back button press */ onBackButtonPress: () => void; - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; - /** Array of step names */ stepNames: readonly string[]; + /** Method to set the state of setUSDBankAccountStep */ + setUSDBankAccountStep?: (step: string | null) => void; + /** ID of current policy */ policyID: string | undefined; }; -function Country({onBackButtonPress, onSubmit, stepNames, policyID}: CountryProps) { +function Country({onBackButtonPress, stepNames, setUSDBankAccountStep, policyID}: CountryProps) { const submit = () => { + if (!setUSDBankAccountStep) { + return; + } + + setUSDBankAccountStep(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT); goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT); - onSubmit?.(); }; return ( diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/PersonalInfo.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/PersonalInfo.tsx index a0c39597cf35..bd548f912ff0 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/PersonalInfo.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/PersonalInfo.tsx @@ -1,21 +1,17 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useMemo} from 'react'; import type {View} from 'react-native'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReimbursementAccountSubmitCallback from '@hooks/useReimbursementAccountSubmitCallback'; -import useSubPage from '@hooks/useSubPage'; -import type {SubPageProps} from '@hooks/useSubPage/types'; -import Navigation from '@libs/Navigation/Navigation'; +import useSubStep from '@hooks/useSubStep'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {getBankAccountIDAsNumber} from '@libs/ReimbursementAccountUtils'; import getInitialSubStepForPersonalInfo from '@pages/ReimbursementAccount/USD/utils/getInitialSubStepForPersonalInfo'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; import {updatePersonalInformationForBankAccount} from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; import Address from './subSteps/Address'; import Confirmation from './subSteps/Confirmation'; @@ -27,29 +23,14 @@ type PersonalInfoProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; - /** Reference to the outer element */ ref?: ForwardedRef; - - /** Back to URL for preserving navigation context */ - backTo?: string; }; const PERSONAL_INFO_STEP_KEYS = INPUT_IDS.PERSONAL_INFO_STEP; -const PAGE_NAMES = CONST.BANK_ACCOUNT.PAGE_NAMES; -const SUB_PAGE_NAMES = CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.SUB_PAGE_NAMES; - -const pages = [ - {pageName: SUB_PAGE_NAMES.FULL_NAME, component: FullName}, - {pageName: SUB_PAGE_NAMES.DATE_OF_BIRTH, component: DateOfBirth}, - {pageName: SUB_PAGE_NAMES.SSN, component: SocialSecurityNumber}, - {pageName: SUB_PAGE_NAMES.ADDRESS, component: Address}, - {pageName: SUB_PAGE_NAMES.CONFIRMATION, component: Confirmation}, -]; +const bodyContent: Array> = [FullName, DateOfBirth, SocialSecurityNumber, Address, Confirmation]; -function PersonalInfo({onBackButtonPress, onSubmit, ref, backTo}: PersonalInfoProps) { +function PersonalInfo({onBackButtonPress, ref}: PersonalInfoProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -58,7 +39,6 @@ function PersonalInfo({onBackButtonPress, onSubmit, ref, backTo}: PersonalInfoPr const policyID = reimbursementAccount?.achData?.policyID; const values = useMemo(() => getSubStepValues(PERSONAL_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const bankAccountID = getBankAccountIDAsNumber(reimbursementAccount?.achData); - const markSubmitting = useReimbursementAccountSubmitCallback(onSubmit); const submit = useCallback( (isConfirmPage: boolean) => { updatePersonalInformationForBankAccount(bankAccountID, {...values}, policyID, isConfirmPage); @@ -68,32 +48,26 @@ function PersonalInfo({onBackButtonPress, onSubmit, ref, backTo}: PersonalInfoPr const isBankAccountVerifying = reimbursementAccount?.achData?.state === CONST.BANK_ACCOUNT.STATE.VERIFYING; const startFrom = useMemo(() => (isBankAccountVerifying ? 0 : getInitialSubStepForPersonalInfo(values)), [values, isBankAccountVerifying]); - const buildRoute = useCallback( - (pageName: string, action?: 'edit') => ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: PAGE_NAMES.REQUESTOR, subPage: pageName, action, backTo}), - [policyID, backTo], - ); - - const {CurrentPage, isEditing, currentPageName, pageIndex, nextPage, prevPage, moveTo, isRedirecting} = useSubPage({ - pages, - startFrom, - onFinished: () => { - submit(true); - markSubmitting(); - }, - onPageChange: () => submit(false), - buildRoute, - }); + const { + componentToRender: SubStep, + isEditing, + screenIndex, + nextScreen, + prevScreen, + moveTo, + goToTheLastStep, + } = useSubStep({bodyContent, startFrom, onFinished: () => submit(true), onNextSubStep: () => submit(false)}); const handleBackButtonPress = () => { if (isEditing) { - Navigation.goBack(buildRoute(SUB_PAGE_NAMES.CONFIRMATION)); + goToTheLastStep(); return; } - if (pageIndex === 0) { + if (screenIndex === 0) { onBackButtonPress(); } else { - prevPage(); + prevScreen(); } }; @@ -108,16 +82,11 @@ function PersonalInfo({onBackButtonPress, onSubmit, ref, backTo}: PersonalInfoPr startStepIndex={2} stepNames={CONST.BANK_ACCOUNT.STEP_NAMES} > - {isRedirecting ? ( - - ) : ( - - )} + ); } diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx index 39c912e0ce38..cc63886621ce 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Address.tsx @@ -4,7 +4,7 @@ import AddressStep from '@components/SubStepForms/AddressStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +22,7 @@ const INPUT_KEYS = { const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.STREET, PERSONAL_INFO_STEP_KEY.CITY, PERSONAL_INFO_STEP_KEY.STATE, PERSONAL_INFO_STEP_KEY.ZIP_CODE]; -function Address({onNext, onMove, isEditing}: SubPageProps) { +function Address({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx index e9c1a8749b9c..17c29db3ce12 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/Confirmation.tsx @@ -2,7 +2,7 @@ import React, {useMemo} from 'react'; import ConfirmationStep from '@components/SubStepForms/ConfirmationStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import {getLatestErrorMessage} from '@libs/ErrorUtils'; import getSubStepValues from '@pages/ReimbursementAccount/utils/getSubStepValues'; import CONST from '@src/CONST'; @@ -12,7 +12,7 @@ import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; const PERSONAL_INFO_STEP_KEYS = INPUT_IDS.PERSONAL_INFO_STEP; const PERSONAL_INFO_STEP_INDEXES = CONST.REIMBURSEMENT_ACCOUNT.SUBSTEP_INDEX.PERSONAL_INFO; -function Confirmation({onNext, onMove, isEditing}: SubPageProps) { +function Confirmation({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx index 54adf2a4be87..cde22e01ed97 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/DateOfBirth.tsx @@ -4,7 +4,7 @@ import DateOfBirthStep from '@components/SubStepForms/DateOfBirthStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import HelpLinks from '@pages/ReimbursementAccount/USD/Requestor/PersonalInfo/HelpLinks'; @@ -16,7 +16,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const PERSONAL_INFO_DOB_KEY = INPUT_IDS.PERSONAL_INFO_STEP.DOB; const STEP_FIELDS = [PERSONAL_INFO_DOB_KEY]; -function DateOfBirth({onNext, onMove, isEditing}: SubPageProps) { +function DateOfBirth({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx index 6093b3c2a77a..a1f841963792 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/FullName.tsx @@ -3,7 +3,7 @@ import FullNameStep from '@components/SubStepForms/FullNameStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; @@ -11,7 +11,7 @@ import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.FIRST_NAME, PERSONAL_INFO_STEP_KEY.LAST_NAME]; -function FullName({onNext, onMove, isEditing}: SubPageProps) { +function FullName({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); diff --git a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx index 6e7384be05ab..ff9d07476a6a 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/PersonalInfo/subSteps/SocialSecurityNumber.tsx @@ -5,7 +5,7 @@ import SingleFieldStep from '@components/SubStepForms/SingleFieldStep'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; -import type {SubPageProps} from '@hooks/useSubPage/types'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getFieldRequiredErrors, isValidSSNLastFour} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; @@ -16,7 +16,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.SSN_LAST_4]; -function SocialSecurityNumber({onNext, onMove, isEditing}: SubPageProps) { +function SocialSecurityNumber({onNext, onMove, isEditing}: SubStepProps) { const {translate} = useLocalize(); const [reimbursementAccount, reimbursementAccountResult] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); @@ -68,7 +68,6 @@ function SocialSecurityNumber({onNext, onMove, isEditing}: SubPageProps) { maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.SSN} enabledWhenOffline forwardedFSClass={CONST.FULLSTORY.CLASS.MASK} - shouldDelayAutoFocus /> ); } diff --git a/src/pages/ReimbursementAccount/USD/Requestor/RequestorStep.tsx b/src/pages/ReimbursementAccount/USD/Requestor/RequestorStep.tsx index f02b5186081d..4a9de8d65704 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/RequestorStep.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/RequestorStep.tsx @@ -2,28 +2,28 @@ import React from 'react'; import type {ForwardedRef} from 'react'; import type {View} from 'react-native'; import PersonalInfo from './PersonalInfo/PersonalInfo'; +import VerifyIdentity from './VerifyIdentity/VerifyIdentity'; type RequestorStepProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - /** Handles submit button press (URL-based navigation) */ - onSubmit?: () => void; + /** If we should show Onfido flow */ + shouldShowOnfido: boolean; /** Reference to the outer element */ ref?: ForwardedRef; - - /** Back to URL for preserving navigation context */ - backTo?: string; }; -function RequestorStep({onBackButtonPress, onSubmit, ref, backTo}: RequestorStepProps) { +function RequestorStep({shouldShowOnfido, onBackButtonPress, ref}: RequestorStepProps) { + if (shouldShowOnfido) { + return ; + } + return ( ); } diff --git a/src/pages/ReimbursementAccount/USD/Requestor/VerifyIdentity/VerifyIdentity.tsx b/src/pages/ReimbursementAccount/USD/Requestor/VerifyIdentity/VerifyIdentity.tsx index c739dd63a433..5947ef3f01f8 100644 --- a/src/pages/ReimbursementAccount/USD/Requestor/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/ReimbursementAccount/USD/Requestor/VerifyIdentity/VerifyIdentity.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {useCallback, useState} from 'react'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import Onfido from '@components/Onfido'; @@ -8,21 +8,18 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import Growl from '@libs/Growl'; -import {clearOnfidoToken, updateReimbursementAccountDraft, verifyIdentityForBankAccount} from '@userActions/BankAccounts'; +import {clearOnfidoToken, goToWithdrawalAccountSetupStep, updateReimbursementAccountDraft, verifyIdentityForBankAccount} from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; type VerifyIdentityProps = { /** Goes to the previous step */ onBackButtonPress: () => void; - - /** Navigates to the next step */ - onSubmit?: () => void; }; const ONFIDO_ERROR_DISPLAY_DURATION = 10000; -function VerifyIdentity({onBackButtonPress, onSubmit}: VerifyIdentityProps) { +function VerifyIdentity({onBackButtonPress}: VerifyIdentityProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -34,37 +31,25 @@ function VerifyIdentity({onBackButtonPress, onSubmit}: VerifyIdentityProps) { const policyID = reimbursementAccount?.achData?.policyID; const bankAccountID = reimbursementAccount?.achData?.bankAccountID; - const isOnfidoAlreadyComplete = useRef(reimbursementAccount?.achData?.isOnfidoSetupComplete); - const onSubmitRef = useRef(onSubmit); - - // If Onfido is already complete (e.g. direct URL navigation), skip to next step - useEffect(() => { - if (!isOnfidoAlreadyComplete.current) { - return; - } - onSubmitRef.current?.(); - }, []); - const handleOnfidoSuccess = useCallback( (onfidoData: OnfidoData) => { verifyIdentityForBankAccount(Number(bankAccountID), {...onfidoData, applicantID: onfidoApplicantID}, policyID); updateReimbursementAccountDraft({isOnfidoSetupComplete: true}); - onSubmit?.(); }, - [bankAccountID, onfidoApplicantID, policyID, onSubmit], + [bankAccountID, onfidoApplicantID, policyID], ); const handleOnfidoError = () => { // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); clearOnfidoToken(); - onBackButtonPress(); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); }; const handleOnfidoUserExit = (isUserInitiated?: boolean) => { if (isUserInitiated) { clearOnfidoToken(); - onBackButtonPress(); + goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); } else { setOnfidoKey(Math.floor(Math.random() * 1000000)); } diff --git a/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx b/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx new file mode 100644 index 000000000000..c13784998b2a --- /dev/null +++ b/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlow.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import {View} from 'react-native'; +import useOnyx from '@hooks/useOnyx'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import BankInfo from './BankInfo/BankInfo'; +import BeneficialOwnersStep from './BeneficialOwnerInfo/BeneficialOwnersStep'; +import BusinessInfo from './BusinessInfo/BusinessInfo'; +import CompleteVerification from './CompleteVerification/CompleteVerification'; +import ConnectBankAccount from './ConnectBankAccount/ConnectBankAccount'; +import Country from './Country'; +import RequestorStep from './Requestor/RequestorStep'; + +type USDVerifiedBankAccountFlowProps = { + USDBankAccountStep: string; + policyID: string | undefined; + onBackButtonPress: () => void; + requestorStepRef: React.RefObject; + onfidoToken: string; + setUSDBankAccountStep: (step: string | null) => void; + setShouldShowConnectedVerifiedBankAccount: (shouldShowConnectedVerifiedBankAccount: boolean) => void; +}; + +function USDVerifiedBankAccountFlow({ + USDBankAccountStep, + policyID = '', + onBackButtonPress, + requestorStepRef, + onfidoToken, + setUSDBankAccountStep, + setShouldShowConnectedVerifiedBankAccount, +}: USDVerifiedBankAccountFlowProps) { + const styles = useThemeStyles(); + const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); + + let CurrentStep: React.JSX.Element | null; + switch (USDBankAccountStep) { + case CONST.BANK_ACCOUNT.STEP.COUNTRY: + CurrentStep = ( + + ); + break; + case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: + CurrentStep = ( + + ); + break; + case CONST.BANK_ACCOUNT.STEP.REQUESTOR: + CurrentStep = ( + + ); + break; + case CONST.BANK_ACCOUNT.STEP.COMPANY: + CurrentStep = ; + break; + case CONST.BANK_ACCOUNT.STEP.BENEFICIAL_OWNERS: + CurrentStep = ; + break; + case CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT: + CurrentStep = ; + break; + case CONST.BANK_ACCOUNT.STEP.VALIDATION: + CurrentStep = ( + + ); + break; + default: + CurrentStep = null; + break; + } + + if (CurrentStep) { + return {CurrentStep}; + } + + return null; +} + +export default USDVerifiedBankAccountFlow; diff --git a/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage.tsx b/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage.tsx deleted file mode 100644 index d721d8155e1c..000000000000 --- a/src/pages/ReimbursementAccount/USD/USDVerifiedBankAccountFlowPage.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, {useCallback, useMemo, useRef} from 'react'; -import {View} from 'react-native'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {ReimbursementAccountNavigatorParamList} from '@libs/Navigation/types'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; -import BankInfo from './BankInfo/BankInfo'; -import BeneficialOwnersStep from './BeneficialOwnerInfo/BeneficialOwnersStep'; -import BusinessInfo from './BusinessInfo/BusinessInfo'; -import CompleteVerification from './CompleteVerification/CompleteVerification'; -import ConnectBankAccount from './ConnectBankAccount/ConnectBankAccount'; -import Country from './Country'; -import RequestorStep from './Requestor/RequestorStep'; -import VerifyIdentity from './Requestor/VerifyIdentity/VerifyIdentity'; -import type USDPageProps from './types'; - -const PAGE_NAMES = CONST.BANK_ACCOUNT.PAGE_NAMES; -const BANK_INFO_SUB_PAGES = CONST.BANK_ACCOUNT.BANK_INFO_STEP.SUB_PAGE_NAMES; -const PERSONAL_INFO_SUB_PAGES = CONST.BANK_ACCOUNT.PERSONAL_INFO_STEP.SUB_PAGE_NAMES; -const BUSINESS_INFO_SUB_PAGES = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.SUB_PAGE_NAMES; -const BENEFICIAL_OWNERS_SUB_PAGES = CONST.BANK_ACCOUNT.BENEFICIAL_OWNERS_STEP.SUB_PAGE_NAMES; -const COMPLETE_VERIFICATION_SUB_PAGES = CONST.BANK_ACCOUNT.COMPLETE_VERIFICATION_STEP.SUB_PAGE_NAMES; - -type PageEntry = { - pageName: string; - component: React.ComponentType; - firstSubPage?: string; - lastSubPage?: string; -}; - -const pages: PageEntry[] = [ - {pageName: PAGE_NAMES.COUNTRY, component: Country as React.ComponentType}, - {pageName: PAGE_NAMES.BANK_ACCOUNT, component: BankInfo as React.ComponentType, firstSubPage: BANK_INFO_SUB_PAGES.PLAID, lastSubPage: BANK_INFO_SUB_PAGES.PLAID}, - { - pageName: PAGE_NAMES.REQUESTOR, - component: RequestorStep as React.ComponentType, - firstSubPage: PERSONAL_INFO_SUB_PAGES.FULL_NAME, - lastSubPage: PERSONAL_INFO_SUB_PAGES.CONFIRMATION, - }, - {pageName: PAGE_NAMES.VERIFY_IDENTITY, component: VerifyIdentity as React.ComponentType}, - { - pageName: PAGE_NAMES.COMPANY, - component: BusinessInfo as React.ComponentType, - firstSubPage: BUSINESS_INFO_SUB_PAGES.NAME, - lastSubPage: BUSINESS_INFO_SUB_PAGES.CONFIRMATION, - }, - { - pageName: PAGE_NAMES.BENEFICIAL_OWNERS, - component: BeneficialOwnersStep as React.ComponentType, - firstSubPage: BENEFICIAL_OWNERS_SUB_PAGES.IS_USER_UBO, - lastSubPage: undefined, - }, - { - pageName: PAGE_NAMES.ACH_CONTRACT, - component: CompleteVerification as React.ComponentType, - firstSubPage: COMPLETE_VERIFICATION_SUB_PAGES.CONFIRM_AGREEMENTS, - lastSubPage: COMPLETE_VERIFICATION_SUB_PAGES.CONFIRM_AGREEMENTS, - }, - {pageName: PAGE_NAMES.VALIDATION, component: ConnectBankAccount as React.ComponentType}, -]; - -type USDVerifiedBankAccountFlowPageProps = PlatformStackScreenProps; - -function USDVerifiedBankAccountFlowPage({route}: USDVerifiedBankAccountFlowPageProps) { - const styles = useThemeStyles(); - const policyID = route.params?.policyID; - const currentPage = route.params?.page; - const currentSubPage = route.params?.subPage; - const backTo = route.params?.backTo; - - const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); - - const requestorStepRef = useRef(null); - const isOnfidoSetupComplete = reimbursementAccount?.achData?.isOnfidoSetupComplete; - - const currentPageIndex = useMemo(() => { - const index = pages.findIndex((p) => p.pageName === currentPage); - return index >= 0 ? index : 0; - }, [currentPage]); - - const currentEntry = pages.at(currentPageIndex); - const CurrentPage = currentEntry?.component ?? (Country as React.ComponentType); - const isRequestorStep = currentEntry?.pageName === PAGE_NAMES.REQUESTOR; - - const shouldSkipVerifyIdentity = useCallback((pageName?: string) => pageName === PAGE_NAMES.VERIFY_IDENTITY && isOnfidoSetupComplete, [isOnfidoSetupComplete]); - - const onSubmit = useCallback(() => { - let nextIndex = currentPageIndex + 1; - if (shouldSkipVerifyIdentity(pages.at(nextIndex)?.pageName)) { - nextIndex += 1; - } - if (nextIndex >= pages.length) { - Navigation.goBack(); - return; - } - const nextPage = pages.at(nextIndex); - Navigation.navigate(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: nextPage?.pageName, subPage: nextPage?.firstSubPage, backTo})); - }, [backTo, currentPageIndex, policyID, shouldSkipVerifyIdentity]); - - const onBackButtonPress = useCallback(() => { - let prevIndex = currentPageIndex - 1; - if (shouldSkipVerifyIdentity(pages.at(prevIndex)?.pageName)) { - prevIndex -= 1; - } - if (prevIndex < 0) { - Navigation.goBack(); - return; - } - const prevPage = pages.at(prevIndex); - Navigation.goBack(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: prevPage?.pageName, subPage: prevPage?.lastSubPage, backTo})); - }, [backTo, currentPageIndex, policyID, shouldSkipVerifyIdentity]); - - return ( - - - - ); -} - -USDVerifiedBankAccountFlowPage.displayName = 'USDVerifiedBankAccountFlowPage'; - -export default USDVerifiedBankAccountFlowPage; diff --git a/src/pages/ReimbursementAccount/USD/types.ts b/src/pages/ReimbursementAccount/USD/types.ts deleted file mode 100644 index c808a68bff8f..000000000000 --- a/src/pages/ReimbursementAccount/USD/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type {ForwardedRef} from 'react'; -import type {View} from 'react-native'; -import type {Route} from '@src/ROUTES'; - -type USDPageProps = { - /** Handles submit button press */ - onSubmit: () => void; - - /** Handles back button press */ - onBackButtonPress: () => void; - - /** ID of current policy */ - policyID?: string; - - /** Name of the current sub page */ - currentSubPage?: string; - - /** Array of step names for the progress indicator */ - stepNames?: readonly string[]; - - /** Reference to the outer element (used by RequestorStep) */ - ref?: ForwardedRef; - - /** Back to URL for preserving navigation context */ - backTo?: Route; -}; - -export default USDPageProps; diff --git a/src/pages/ReimbursementAccount/VerifiedBankAccountFlowEntryPoint.tsx b/src/pages/ReimbursementAccount/VerifiedBankAccountFlowEntryPoint.tsx index 8e6a747efcc5..4da763c2981f 100644 --- a/src/pages/ReimbursementAccount/VerifiedBankAccountFlowEntryPoint.tsx +++ b/src/pages/ReimbursementAccount/VerifiedBankAccountFlowEntryPoint.tsx @@ -123,10 +123,10 @@ function VerifiedBankAccountFlowEntryPoint({ const prepareNextStep = useCallback( (setupType: ValueOf) => { setBankAccountSubStep(setupType); + setUSDBankAccountStep(CONST.BANK_ACCOUNT.STEP.COUNTRY); goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.COUNTRY); - Navigation.navigate(ROUTES.BANK_ACCOUNT_USD_SETUP.getRoute({policyID, page: CONST.BANK_ACCOUNT.PAGE_NAMES.COUNTRY, backTo})); }, - [policyID, backTo], + [setUSDBankAccountStep], ); /** diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 24dd5d84896f..dc08a6534505 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -19,6 +19,7 @@ import {getLastFourDigits} from '@libs/BankAccountUtils'; import {getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from '@libs/CardUtils'; import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import {REIMBURSEMENT_ACCOUNT_ROUTE_NAMES} from '@libs/ReimbursementAccountUtils'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -66,6 +67,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA Navigation.navigate( ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({ policyID, + stepToOpen: REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW, backTo: ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID), }), ); diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx index 063813a70834..2c694a0253c8 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx @@ -20,7 +20,7 @@ import {getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; -import {hasInProgressUSDVBBA} from '@libs/ReimbursementAccountUtils'; +import {hasInProgressUSDVBBA, REIMBURSEMENT_ACCOUNT_ROUTE_NAMES} from '@libs/ReimbursementAccountUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -71,6 +71,7 @@ function WorkspaceExpensifyCardPageEmptyState({route, policy}: WorkspaceExpensif Navigation.navigate( ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({ policyID: policy?.id, + stepToOpen: REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW, backTo: ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policy?.id), }), ); diff --git a/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx b/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx index a2f57f08248d..1b2f0312a243 100644 --- a/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx @@ -29,7 +29,7 @@ import {getLastFourDigits} from '@libs/BankAccountUtils'; import {getCardSettings, getEligibleBankAccountsForCard} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import {areTravelPersonalDetailsMissing} from '@libs/PersonalDetailsUtils'; -import {hasInProgressUSDVBBA} from '@libs/ReimbursementAccountUtils'; +import {hasInProgressUSDVBBA, REIMBURSEMENT_ACCOUNT_ROUTE_NAMES} from '@libs/ReimbursementAccountUtils'; import { getIsTravelInvoicingEnabled, getTravelInvoicingCardSettingsKey, @@ -173,6 +173,7 @@ function WorkspaceTravelInvoicingSection({policyID}: WorkspaceTravelInvoicingSec Navigation.navigate( ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({ policyID, + stepToOpen: REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW, backTo: ROUTES.WORKSPACE_TRAVEL.getRoute(policyID), }), ); diff --git a/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx b/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx index 981ab69e16ff..1203dfce00b2 100644 --- a/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx @@ -15,6 +15,7 @@ import {configureTravelInvoicingForPolicy, setTravelInvoicingSettlementAccount} import {getLastFourDigits} from '@libs/BankAccountUtils'; import {getCardSettings, getEligibleBankAccountsForCard} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import {REIMBURSEMENT_ACCOUNT_ROUTE_NAMES} from '@libs/ReimbursementAccountUtils'; import {getIsTravelInvoicingEnabled, getTravelInvoicingCardSettingsKey} from '@libs/TravelInvoicingUtils'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -96,6 +97,7 @@ function WorkspaceTravelInvoicingSettlementAccountPage({route}: WorkspaceTravelI Navigation.navigate( ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({ policyID, + stepToOpen: REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW, backTo: ROUTES.WORKSPACE_TRAVEL_SETTINGS_ACCOUNT.getRoute(policyID), }), ); diff --git a/tests/unit/ReimbursementAccountUtilsTest.ts b/tests/unit/ReimbursementAccountUtilsTest.ts index e2f3be2e8703..b531357e9865 100644 --- a/tests/unit/ReimbursementAccountUtilsTest.ts +++ b/tests/unit/ReimbursementAccountUtilsTest.ts @@ -1,12 +1,19 @@ import Onyx from 'react-native-onyx'; import CONST from '../../src/CONST'; -import {getBankAccountIDAsNumber} from '../../src/libs/ReimbursementAccountUtils'; +import {getBankAccountIDAsNumber, getRouteForCurrentStep, REIMBURSEMENT_ACCOUNT_ROUTE_NAMES} from '../../src/libs/ReimbursementAccountUtils'; import ONYXKEYS from '../../src/ONYXKEYS'; import type {ACHDataReimbursementAccount} from '../../src/types/onyx/ReimbursementAccount'; Onyx.init({keys: ONYXKEYS}); describe('ReimbursementAccountUtils', () => { + describe('getRouteForCurrentStep', () => { + it("should return 'new' step if 'BankAccountStep' or '' is provided", () => { + expect(getRouteForCurrentStep(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT)).toEqual(REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW); + expect(getRouteForCurrentStep('')).toEqual(REIMBURSEMENT_ACCOUNT_ROUTE_NAMES.NEW); + }); + }); + describe('getBankAccountIDAsNumber', () => { it('should return DEFAULT_NUMBER_ID when achData is undefined', () => { // Given no ACH data