From 6d34d9cce57a4eecc0f6c38998a17b872c378974 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:30:03 +0000 Subject: [PATCH 01/24] adding routes and screens and refactoring plaid an assignee step --- src/ROUTES.ts | 36 ++++++++++ src/SCREENS.ts | 8 +++ .../ModalStackNavigators/index.tsx | 9 +++ src/libs/Navigation/linkingConfig/config.ts | 18 +++++ src/libs/Navigation/types.ts | 28 ++++++++ .../addNew/PlaidConnectionStep.tsx | 10 ++- .../assignCard/AssignCardFeedPage.tsx | 71 +++++-------------- .../companyCards/assignCard/AssigneeStep.tsx | 22 +++--- 8 files changed, 136 insertions(+), 66 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index edf204001c26..31fa6067f6b7 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2058,6 +2058,42 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card`, backTo), }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/assignee', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/assignee`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT : { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/select', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/select`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date-step', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date-step`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/name', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/name`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/confirmation', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/confirmation`, backTo), + }, + WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/plaid-connection', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/plaid-connection`, backTo), + }, WORKSPACE_COMPANY_CARDS_TRANSACTION_START_DATE: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2609ed8ff807..b75f8dc4ca9c 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -550,6 +550,14 @@ const SCREENS = { PROFILE: 'Workspace_Overview', COMPANY_CARDS: 'Workspace_CompanyCards', COMPANY_CARDS_ASSIGN_CARD: 'Workspace_CompanyCards_AssignCard', + + COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: 'Workspace_CompanyCards_AssignCard_Assignee', + COMPANY_CARDS_ASSIGN_CARD_SELECT: 'Workspace_CompanyCards_AssignCard_Select', + COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP: 'Workspace_CompanyCards_AssignCard_TransactionStartDateStep', + COMPANY_CARDS_ASSIGN_CARD_NAME: 'Workspace_CompanyCards_AssignCard_CardNameStep', + COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION: 'Workspace_CompanyCards_AssignCard_ConfirmationStep', + COMPANY_CARDS_PLAID_CONNECTION: 'Workspace_CompanyCards_PlaidConnectionStep', + COMPANY_CARDS_TRANSACTION_START_DATE: 'Workspace_CompanyCards_TransactionStartDate', COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed', COMPANY_CARDS_BANK_CONNECTION: 'Workspace_CompanyCards_BankConnection', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 120151d88a2d..3b9b9d1b354a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -669,6 +669,15 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default, [SCREENS.WORKSPACE.INVOICES_VERIFY_ACCOUNT]: () => require('../../../../pages/workspace/invoices/WorkspaceInvoicesVerifyAccountPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: () => require('../../../../pages/workspace/companyCards/assignCard/AssignCardFeedPage').default, + + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: () => require('../../../../pages/workspace/companyCards/assignCard/AssigneeStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: () => require('../../../../pages/workspace/companyCards/assignCard/CardSelectionStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: () => + require('../../../../pages/workspace/companyCards/assignCard/TransactionStartDateStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: () => require('../../../../pages/workspace/companyCards/assignCard/CardNameStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: () => require('../../../../pages/workspace/companyCards/assignCard/ConfirmationStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: () => require('../../../../pages/workspace/companyCards/addNew/PlaidConnectionStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: () => require('../../../../pages/workspace/companyCards/BankConnection').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: () => require('../../../../pages/workspace/companyCards/addNew/AddNewCardPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index bf0708fb8ef3..03277dfcd0aa 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -739,6 +739,24 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.route, }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { + path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: { + path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: { + path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { + path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { + path : ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.route, + }, [SCREENS.WORKSPACE.COMPANY_CARDS_TRANSACTION_START_DATE]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_TRANSACTION_START_DATE.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index decbe39265c4..c1fbdbfe22ed 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1048,6 +1048,34 @@ type SettingsNavigatorParamList = { feed: CompanyCardFeed; backTo?: Routes; }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { + policyID: string; + feed: CompanyCardFeed; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { + policyID: string; + feed: CompanyCardFeed; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: { + policyID: string; + feed: CompanyCardFeed; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { + policyID: string; + feed: CompanyCardFeed; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { + policyID: string; + feed: CompanyCardFeed; + }; [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: { policyID: string; }; diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index d77bb54536ea..a6140ace7cc6 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -22,10 +22,14 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CompanyCardFeed} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type SCREENS from '@src/SCREENS'; +import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; +import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; -function PlaidConnectionStep({feed, policyID}: {feed?: CompanyCardFeed; policyID?: string}) { +type PlaidConnectionStepProps = PlatformStackScreenProps; + +function PlaidConnectionStep({route}: PlaidConnectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); @@ -39,6 +43,8 @@ function PlaidConnectionStep({feed, policyID}: {feed?: CompanyCardFeed; policyID // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors).at(0) as string) : ''; const {isOffline} = useNetwork(); + const feed = route.params?.feed; + const policyID = route.params?.policyID; const domain = getDomainNameForPolicy(policyID); const isAuthenticatedWithPlaid = useCallback(() => !!plaidData?.bankAccounts?.length || !isEmptyObject(plaidData?.errors), [plaidData]); diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 6c3e0845a66a..1fd65e303714 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -6,8 +6,6 @@ import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; -import PlaidConnectionStep from '@pages/workspace/companyCards/addNew/PlaidConnectionStep'; -import BankConnection from '@pages/workspace/companyCards/BankConnection'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; @@ -15,11 +13,9 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; -import AssigneeStep from './AssigneeStep'; -import CardNameStep from './CardNameStep'; -import CardSelectionStep from './CardSelectionStep'; -import ConfirmationStep from './ConfirmationStep'; -import TransactionStartDateStep from './TransactionStartDateStep'; +import Navigation from '@libs/Navigation/Navigation'; +import ROUTES from '@src/ROUTES'; + type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; @@ -54,58 +50,29 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { switch (currentStep) { case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.ASSIGNEE: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.CARD: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.CARD_NAME: - return ; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; case CONST.COMPANY_CARD.STEP.CONFIRMATION: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed,shouldUseBackToParam ? backTo : undefined)); + return null; default: - return ( - - ); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed, Navigation.getActiveRoute())); + return null; } } diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 96737cd32af3..c20dedda2a3a 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -1,6 +1,5 @@ import React, {useMemo, useState} from 'react'; import {Keyboard} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import * as Expensicons from '@components/Icon/Expensicons'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; @@ -24,28 +23,27 @@ import Navigation from '@navigation/Navigation'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; +import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; +import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; +import type SCREENS from '@src/SCREENS'; const MINIMUM_MEMBER_TO_SHOW_SEARCH = 8; -type AssigneeStepProps = { - /** The policy that the card will be issued under */ - policy: OnyxEntry; +type AssigneeStepProps = PlatformStackScreenProps; - /** Selected feed */ - feed: OnyxTypes.CompanyCardFeed; -}; - -function AssigneeStep({policy, feed}: AssigneeStepProps) { +function AssigneeStep({route}: AssigneeStepProps) { const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [countryCode] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); - const [list] = useCardsList(policy?.id, feed); - const [cardFeeds] = useCardFeeds(policy?.id); + const policyID = route.params?.policyID; + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const feed = route.params?.feed; + const [list] = useCardsList(policyID, feed); + const [cardFeeds] = useCardFeeds(policyID); const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); const isEditing = assignCard?.isEditing; From 5956f529cbffd7fc5cf45abc73b3072afe60ec32 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Thu, 23 Oct 2025 15:14:16 +0000 Subject: [PATCH 02/24] finshing screens for assign flow and adding hook for navigation based on step change in onyx --- src/ROUTES.ts | 14 ++--- src/SCREENS.ts | 2 +- src/libs/Navigation/linkingConfig/config.ts | 10 ++-- .../addNew/PlaidConnectionStep.tsx | 6 +-- .../assignCard/AssignCardFeedPage.tsx | 39 +++++++------- .../companyCards/assignCard/AssigneeStep.tsx | 14 +++-- .../companyCards/assignCard/CardNameStep.tsx | 15 +++--- .../assignCard/CardSelectionStep.tsx | 20 +++---- .../assignCard/ConfirmationStep.tsx | 23 ++++---- .../TransactionStartDateSelectorPage.tsx | 2 +- .../assignCard/TransactionStartDateStep.tsx | 20 ++++--- src/pages/workspace/companyCards/utils.tsx | 54 ++++++++++++++++++- 12 files changed, 142 insertions(+), 77 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 31fa6067f6b7..d2e6eba31a88 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2060,37 +2060,37 @@ const ROUTES = { }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/assignee', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/assignee`, backTo), }, - WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT : { + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/select', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/select`, backTo), }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date-step', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date-step`, backTo), }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/name', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/name`, backTo), }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/confirmation', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/confirmation`, backTo), }, WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/plaid-connection', - + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/plaid-connection`, backTo), }, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b75f8dc4ca9c..0f8b4c75ca73 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -557,7 +557,7 @@ const SCREENS = { COMPANY_CARDS_ASSIGN_CARD_NAME: 'Workspace_CompanyCards_AssignCard_CardNameStep', COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION: 'Workspace_CompanyCards_AssignCard_ConfirmationStep', COMPANY_CARDS_PLAID_CONNECTION: 'Workspace_CompanyCards_PlaidConnectionStep', - + COMPANY_CARDS_TRANSACTION_START_DATE: 'Workspace_CompanyCards_TransactionStartDate', COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed', COMPANY_CARDS_BANK_CONNECTION: 'Workspace_CompanyCards_BankConnection', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 03277dfcd0aa..32d69534e017 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -743,19 +743,19 @@ const config: LinkingOptions['config'] = { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { - path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.route, + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: { - path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.route, + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: { - path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.route, + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { - path : ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.route, + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { - path : ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.route, + path: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.route, }, [SCREENS.WORKSPACE.COMPANY_CARDS_TRANSACTION_START_DATE]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_TRANSACTION_START_DATE.route, diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index a6140ace7cc6..6fc86496098b 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -15,6 +15,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@libs/actions/CompanyCards'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import {handleRestrictedEvent} from '@userActions/App'; @@ -22,10 +24,8 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type SCREENS from '@src/SCREENS'; -import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; -import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type PlaidConnectionStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 1fd65e303714..154779b079b2 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -4,6 +4,7 @@ import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; import ScreenWrapper from '@components/ScreenWrapper'; import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; +import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -11,11 +12,9 @@ import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullsc import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; -import Navigation from '@libs/Navigation/Navigation'; -import ROUTES from '@src/ROUTES'; - type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; @@ -48,31 +47,35 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { ); } + if (!policyID) { + return null; + } + switch (currentStep) { case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); + break; case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, feed)); + break; case CONST.COMPANY_CARD.STEP.ASSIGNEE: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed)); + break; case CONST.COMPANY_CARD.STEP.CARD: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed)); + break; case CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed)); + break; case CONST.COMPANY_CARD.STEP.CARD_NAME: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed)); + break; case CONST.COMPANY_CARD.STEP.CONFIRMATION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed,shouldUseBackToParam ? backTo : undefined)); - return null; + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed, shouldUseBackToParam ? backTo : undefined)); + break; default: Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed, Navigation.getActiveRoute())); - return null; + break; } } diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index c20dedda2a3a..ac30d26b6a1c 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -15,18 +15,20 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {isDeletedPolicyEmployee} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import Navigation from '@navigation/Navigation'; +import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; -import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; -import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; import type SCREENS from '@src/SCREENS'; +import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; +import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; const MINIMUM_MEMBER_TO_SHOW_SEARCH = 8; @@ -41,7 +43,7 @@ function AssigneeStep({route}: AssigneeStepProps) { const [countryCode] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const policyID = route.params?.policyID; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); - const feed = route.params?.feed; + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const [list] = useCardsList(policyID, feed); const [cardFeeds] = useCardFeeds(policyID); const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); @@ -58,6 +60,8 @@ function AssigneeStep({route}: AssigneeStepProps) { setShouldShowError(false); }; + useAssignCardStepNavigation(policyID, feed, route.params?.backTo); + const submit = () => { let nextStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; if (selectedMember === assignCard?.data?.email) { @@ -101,7 +105,7 @@ function AssigneeStep({route}: AssigneeStepProps) { }); return; } - Navigation.goBack(); + Navigation.dismissModal(); }; const shouldShowSearchInput = policy?.employeeList && Object.keys(policy.employeeList).length >= MINIMUM_MEMBER_TO_SHOW_SEARCH; diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 9cc69d794657..006e1ded35a7 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -11,26 +11,29 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {addErrorMessage} from '@libs/ErrorUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/EditExpensifyCardNameForm'; -type CardNameStepProps = { - /** Current policy id */ - policyID: string | undefined; -}; +type CardNameStepProps = PlatformStackScreenProps; -function CardNameStep({policyID}: CardNameStepProps) { +function CardNameStep({route}: CardNameStepProps) { const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); const styles = useThemeStyles(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); - + const policyID = route.params?.policyID; const data = assignCard?.data; + useAssignCardStepNavigation(policyID, undefined, route.params?.backTo); + const submit = (values: FormOnyxValues) => { setAssignCardStepAndData({ currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 6777a066d6fc..c5d97e1a0d5c 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -20,27 +20,27 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; import {getBankName, getCardFeedIcon, getCustomOrFormattedFeedName, getFilteredCardList, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; +import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; -type CardSelectionStepProps = { - /** Selected feed */ - feed: CompanyCardFeed; +type CardSelectionStepProps = PlatformStackScreenProps; - /** Current policy id */ - policyID: string | undefined; -}; - -function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { +function CardSelectionStep({route}: CardSelectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const illustrations = useThemeIllustrations(); const [searchText, setSearchText] = useState(''); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; + const policyID = route.params?.policyID; const [list] = useCardsList(policyID, feed); const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [cardFeeds] = useCardFeeds(policyID); @@ -75,6 +75,8 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { ), })); + useAssignCardStepNavigation(policyID, feed, route.params?.backTo); + const handleBackButtonPress = () => { if (isEditing) { setAssignCardStepAndData({ diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 470ea0ea3562..dd1ec24b451b 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -19,29 +19,26 @@ import Navigation from '@navigation/Navigation'; import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {CompanyCardFeed, CurrencyList} from '@src/types/onyx'; import type {AssignCardStep} from '@src/types/onyx/AssignCard'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; +import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; +import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; +import { useAssignCardStepNavigation } from "@pages/workspace/companyCards/utils"; -type ConfirmationStepProps = { - /** Current policy id */ - policyID: string | undefined; - /** Route to go back to */ - backTo?: Route; +type ConfirmationStepProps = PlatformStackScreenProps; - /** Selected feed */ - feed: CompanyCardFeed; -}; - -function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { +function ConfirmationStep({route}: ConfirmationStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); + const policyID = route.params?.policyID; + const backTo = route.params?.backTo; + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); @@ -52,7 +49,9 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { const data = assignCard?.data; const cardholderName = getPersonalDetailByEmail(data?.email ?? '')?.displayName ?? ''; - const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((route) => isFullScreenName(route.name))); + const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((_route) => isFullScreenName(_route.name))); + + useAssignCardStepNavigation(policyID, feed, backTo); useEffect(() => { if (!assignCard?.isAssigned) { diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorPage.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorPage.tsx index e350b4cc9722..aff31260605f 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorPage.tsx @@ -34,7 +34,7 @@ function TransactionStartDateSelectorPage({route}: TransactionStartDateSelectorM getFieldRequiredErrors(values, [INPUT_IDS.START_DATE]); const goBack = () => { - Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, route.params.feed, route.params.backTo)); + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, route.params.feed, route.params.backTo)); }; const submit = (values: FormOnyxValues) => { diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index e6b4ad06b026..e0ae306d1712 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -11,24 +11,26 @@ import useLocalize from '@hooks/useLocalize'; 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 {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; +import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Route} from '@src/ROUTES'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; -type TransactionStartDateStepProps = { - policyID: string | undefined; - feed: CompanyCardFeed; - backTo?: Route; -}; +type TransactionStartDateStepProps = PlatformStackScreenProps; -function TransactionStartDateStep({policyID, feed, backTo}: TransactionStartDateStepProps) { +function TransactionStartDateStep({route}: TransactionStartDateStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; + const policyID = route.params?.policyID; + const backTo = route.params?.backTo; const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const isEditing = assignCard?.isEditing; const data = assignCard?.data; @@ -37,6 +39,8 @@ function TransactionStartDateStep({policyID, feed, backTo}: TransactionStartDate const [dateOptionSelected, setDateOptionSelected] = useState(data?.dateOption ?? CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING); const startDate = assignCard?.startDate ?? data?.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING); + useAssignCardStepNavigation(policyID, feed, backTo); + const handleBackButtonPress = () => { if (isEditing) { setAssignCardStepAndData({ diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index 7e5d2331e430..dc498793b576 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -1,12 +1,26 @@ +import {useEffect, useRef} from 'react'; import type {ValueOf} from 'type-fest'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {SelectorType} from '@components/SelectionScreen'; +import useOnyx from '@hooks/useOnyx'; +import Log from '@libs/Log'; +import Navigation from '@libs/Navigation/Navigation'; import {findSelectedBankAccountWithDefaultSelect, findSelectedVendorWithDefaultSelect, getCurrentConnectionName, getSageIntacctNonReimbursableActiveDefaultVendor} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Card, Policy} from '@src/types/onyx'; +import type {Card, CompanyCardFeed, Policy} from '@src/types/onyx'; import type {Account, PolicyConnectionName} from '@src/types/onyx/Policy'; +type AssignCardRoute = + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME + | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION; + type ExportIntegration = { title?: string; description?: string; @@ -357,5 +371,41 @@ function getExportMenuItem( } } +function useAssignCardStepNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined ) { + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); + const currentStep = assignCard?.currentStep; + const previousStepRef = useRef(currentStep); + + useEffect(() => { + // Skip if currentStep hasn't changed + if (currentStep === previousStepRef.current) { + return; + } + + previousStepRef.current = currentStep; + + if (!policyID || !currentStep || !feed) { + return; + } + + // Map steps to routes + const stepRoutes: Record = { + [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE, + [CONST.COMPANY_CARD.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION, + [CONST.COMPANY_CARD.STEP.PLAID_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION, + [CONST.COMPANY_CARD.STEP.CARD]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT, + [CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP, + [CONST.COMPANY_CARD.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME, + [CONST.COMPANY_CARD.STEP.CONFIRMATION]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION, + }; + + + const targetRoute: AssignCardRoute = stepRoutes[currentStep]; + if (targetRoute) { + Navigation.navigate(targetRoute.getRoute(policyID, feed, backTo ?? '')); + } + }, [currentStep, policyID, feed, backTo]); +} + // eslint-disable-next-line import/prefer-default-export -export {getExportMenuItem}; +export {getExportMenuItem, useAssignCardStepNavigation}; From b607e3b28b84cc6a920bf9cc3d678462cf528a23 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 28 Oct 2025 18:01:00 +0000 Subject: [PATCH 03/24] merge main --- .devcontainer.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .devcontainer.json diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 000000000000..6690457a0cd8 --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,3 @@ +{ + "postCreateCommand": "echo '127.0.0.1 dev.new.expensify.com' | sudo tee -a /etc/hosts" +} From 68edb0bd40302bfc83ee203bdeea30953bfb3018 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:27:00 +0000 Subject: [PATCH 04/24] resolve plaidConnectionStep & TransactionStartDateStep conflict with main --- .devcontainer.json | 3 --- .../companyCards/addNew/PlaidConnectionStep.tsx | 11 ++++++++++- .../assignCard/TransactionStartDateStep.tsx | 11 +++++++---- src/pages/workspace/companyCards/utils.tsx | 2 -- 4 files changed, 17 insertions(+), 10 deletions(-) delete mode 100644 .devcontainer.json diff --git a/.devcontainer.json b/.devcontainer.json deleted file mode 100644 index 6690457a0cd8..000000000000 --- a/.devcontainer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "postCreateCommand": "echo '127.0.0.1 dev.new.expensify.com' | sudo tee -a /etc/hosts" -} diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 19b27398f3dd..648e9d950b09 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -26,8 +26,16 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {CompanyCardFeed} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import SCREENS from '@src/SCREENS'; +import { Route } from '@src/ROUTES'; -function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeed; policyID?: string; onExit?: () => void}) { +type PlaidLinkProps = { + onExit?: () => void; +} + +type PlaidConnectionStepProps = PlatformStackScreenProps & PlaidLinkProps; + +function PlaidConnectionStep({route,onExit}: PlaidConnectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); @@ -41,6 +49,7 @@ function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeed; // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors).at(0) as string) : ''; const {isOffline} = useNetwork(); + const {policyID, feed} = route.params; const domain = getDomainNameForPolicy(policyID); const isAuthenticatedWithPlaid = useCallback(() => !!plaidData?.bankAccounts?.length || !isEmptyObject(plaidData?.errors), [plaidData]); diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index f73c5550a1d9..5e64ed05f7c1 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -18,13 +18,16 @@ import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, { Route } from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; type TransactionStartDateStepProps = PlatformStackScreenProps; -type TransactionStartDateSelectionListFooterProps = TransactionStartDateStepProps & { +type TransactionStartDateSelectionListFooterProps = { + policyID: string | undefined; + feed: CompanyCardFeed; + backTo?: Route; dateOptionSelected: string; startDate: string; }; @@ -53,7 +56,7 @@ function TransactionStartDateSelectionListFooter({dateOptionSelected, startDate, ); } -function TransactionStartDateStep({policyID, feed, backTo}: TransactionStartDateStepProps) { +function TransactionStartDateStep({route}: TransactionStartDateStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -61,7 +64,7 @@ function TransactionStartDateStep({policyID, feed, backTo}: TransactionStartDate const isEditing = assignCard?.isEditing; const data = assignCard?.data; const assigneeDisplayName = getPersonalDetailByEmail(data?.email ?? '')?.displayName ?? ''; - + const {policyID, feed, backTo} = route.params; const [dateOptionSelected, setDateOptionSelected] = useState(data?.dateOption ?? CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.FROM_BEGINNING); const startDate = assignCard?.startDate ?? data?.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING); diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index dc498793b576..5b97ebeffc67 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -377,7 +377,6 @@ function useAssignCardStepNavigation(policyID: string | undefined, feed: Company const previousStepRef = useRef(currentStep); useEffect(() => { - // Skip if currentStep hasn't changed if (currentStep === previousStepRef.current) { return; } @@ -388,7 +387,6 @@ function useAssignCardStepNavigation(policyID: string | undefined, feed: Company return; } - // Map steps to routes const stepRoutes: Record = { [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE, [CONST.COMPANY_CARD.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION, From 1e043308b293c66a8f1dc1b866ed551af16235e5 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Sun, 9 Nov 2025 01:10:05 +0000 Subject: [PATCH 05/24] assign flow --- src/libs/Navigation/types.ts | 1 + .../addNew/PlaidConnectionStep.tsx | 8 +-- .../assignCard/AssignCardFeedPage.tsx | 57 ++++--------------- .../companyCards/assignCard/AssigneeStep.tsx | 6 +- .../companyCards/assignCard/CardNameStep.tsx | 6 +- .../assignCard/CardSelectionStep.tsx | 4 +- .../assignCard/ConfirmationStep.tsx | 9 ++- .../assignCard/TransactionStartDateStep.tsx | 7 ++- src/pages/workspace/companyCards/utils.tsx | 44 +++++++------- 9 files changed, 59 insertions(+), 83 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 584c3e001af9..63da778dc470 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1191,6 +1191,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: { policyID: string; + feed: CompanyCardFeed; backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 648e9d950b09..67a49d305d08 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -24,18 +24,16 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CompanyCardFeed} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import SCREENS from '@src/SCREENS'; -import { Route } from '@src/ROUTES'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type PlaidLinkProps = { onExit?: () => void; -} +}; type PlaidConnectionStepProps = PlatformStackScreenProps & PlaidLinkProps; -function PlaidConnectionStep({route,onExit}: PlaidConnectionStepProps) { +function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 154779b079b2..36f961b95423 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -15,19 +15,15 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; +import {useAssignCardNavigation} from '../utils'; type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); - const currentStep = assignCard?.currentStep; - const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const backTo = route.params?.backTo; const policyID = policy?.id; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true}); - const firstAssigneeEmail = useInitial(assignCard?.data?.email); - const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === assignCard?.data?.email; useEffect(() => { return () => { @@ -35,48 +31,19 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }; }, []); - if (isActingAsDelegate) { - return ( - - - - ); - } - - if (!policyID) { - return null; + if (!isActingAsDelegate) { + useAssignCardNavigation(policyID, feed, backTo, true); } - switch (currentStep) { - case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, feed, Navigation.getActiveRoute())); - break; - case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, feed)); - break; - case CONST.COMPANY_CARD.STEP.ASSIGNEE: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed)); - break; - case CONST.COMPANY_CARD.STEP.CARD: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed)); - break; - case CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed)); - break; - case CONST.COMPANY_CARD.STEP.CARD_NAME: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed)); - break; - case CONST.COMPANY_CARD.STEP.CONFIRMATION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed, shouldUseBackToParam ? backTo : undefined)); - break; - default: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed, Navigation.getActiveRoute())); - break; - } + return ( + + + + ); } AssignCardFeedPage.displayName = 'AssignCardFeedPage'; diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index ba132790d6dd..cdf73644d184 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -15,6 +15,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; +import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; @@ -22,7 +23,7 @@ import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {isDeletedPolicyEmployee} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import Navigation from '@navigation/Navigation'; -import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -59,8 +60,9 @@ function AssigneeStep({route}: AssigneeStepProps) { setSelectedMember(assignee.login ?? ''); setShouldShowError(false); }; + Log.hmmm(`AssignCard Data Step Data: ${JSON.stringify(assignCard?.data)}`); - useAssignCardStepNavigation(policyID, feed, route.params?.backTo); + useAssignCardNavigation(policyID, feed, route.params?.backTo); const submit = () => { let nextStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 006e1ded35a7..0ecde820ef7f 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -15,12 +15,13 @@ import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavig import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/EditExpensifyCardNameForm'; +import type {CompanyCardFeed} from '@src/types/onyx'; type CardNameStepProps = PlatformStackScreenProps; @@ -30,9 +31,10 @@ function CardNameStep({route}: CardNameStepProps) { const styles = useThemeStyles(); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const policyID = route.params?.policyID; + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const data = assignCard?.data; - useAssignCardStepNavigation(policyID, undefined, route.params?.backTo); + useAssignCardNavigation(policyID, feed, route.params?.backTo); const submit = (values: FormOnyxValues) => { setAssignCardStepAndData({ diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index a3c3b45ea3ae..a53eb185051b 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -24,7 +24,7 @@ import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavig import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; -import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -74,7 +74,7 @@ function CardSelectionStep({route}: CardSelectionStepProps) { ), })); - useAssignCardStepNavigation(policyID, feed, route.params?.backTo); + useAssignCardNavigation(policyID, feed, route.params?.backTo); const handleBackButtonPress = () => { if (isEditing) { diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index dd1ec24b451b..da43ff57000d 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -14,8 +14,11 @@ import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; import {getPlaidCountry, getPlaidInstitutionId, isSelectedFeedExpired, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import {assignWorkspaceCompanyCard, clearAssignCardStepAndData, setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -24,10 +27,6 @@ import SCREENS from '@src/SCREENS'; import type {CompanyCardFeed, CurrencyList} from '@src/types/onyx'; import type {AssignCardStep} from '@src/types/onyx/AssignCard'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; -import type { PlatformStackScreenProps } from '@libs/Navigation/PlatformStackNavigation/types'; -import type { SettingsNavigatorParamList } from '@libs/Navigation/types'; -import { useAssignCardStepNavigation } from "@pages/workspace/companyCards/utils"; - type ConfirmationStepProps = PlatformStackScreenProps; @@ -51,7 +50,7 @@ function ConfirmationStep({route}: ConfirmationStepProps) { const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((_route) => isFullScreenName(_route.name))); - useAssignCardStepNavigation(policyID, feed, backTo); + useAssignCardNavigation(policyID, feed, backTo); useEffect(() => { if (!assignCard?.isAssigned) { diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index 6bd0e750d75e..3b59fff35253 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -13,12 +13,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; -import {useAssignCardStepNavigation} from '@pages/workspace/companyCards/utils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; -import type SCREENS from '@src/SCREENS'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; + type TransactionStartDateStepProps = PlatformStackScreenProps; function TransactionStartDateStep({route}: TransactionStartDateStepProps) { @@ -34,7 +35,7 @@ function TransactionStartDateStep({route}: TransactionStartDateStepProps) { const [errorText, setErrorText] = useState(''); const [startDate, setStartDate] = useState(() => assignCard?.startDate ?? data?.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING)); - useAssignCardStepNavigation(policyID, feed, backTo); + useAssignCardNavigation(policyID, feed, backTo); const handleBackButtonPress = () => { if (isEditing) { diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index 5b97ebeffc67..3a18a4da1aa8 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -2,6 +2,7 @@ import {useEffect, useRef} from 'react'; import type {ValueOf} from 'type-fest'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {SelectorType} from '@components/SelectionScreen'; +import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -13,13 +14,13 @@ import type {Card, CompanyCardFeed, Policy} from '@src/types/onyx'; import type {Account, PolicyConnectionName} from '@src/types/onyx/Policy'; type AssignCardRoute = - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME - | typeof ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION; + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType; type ExportIntegration = { title?: string; @@ -371,13 +372,15 @@ function getExportMenuItem( } } -function useAssignCardStepNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined ) { +function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined, isStartStep: boolean = false) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); const currentStep = assignCard?.currentStep; const previousStepRef = useRef(currentStep); + const firstAssigneeEmail = useInitial(assignCard?.data?.email); + const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === assignCard?.data?.email; useEffect(() => { - if (currentStep === previousStepRef.current) { + if (currentStep === previousStepRef.current && !isStartStep) { return; } @@ -388,22 +391,25 @@ function useAssignCardStepNavigation(policyID: string | undefined, feed: Company } const stepRoutes: Record = { - [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE, - [CONST.COMPANY_CARD.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION, - [CONST.COMPANY_CARD.STEP.PLAID_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION, - [CONST.COMPANY_CARD.STEP.CARD]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT, - [CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP, - [CONST.COMPANY_CARD.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME, - [CONST.COMPANY_CARD.STEP.CONFIRMATION]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION, + [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute( + policyID, + feed, + ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, feed), + ), + [CONST.COMPANY_CARD.STEP.PLAID_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.CARD]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.CONFIRMATION]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed, shouldUseBackToParam ? backTo : ''), }; - const targetRoute: AssignCardRoute = stepRoutes[currentStep]; if (targetRoute) { - Navigation.navigate(targetRoute.getRoute(policyID, feed, backTo ?? '')); + Navigation.navigate(targetRoute); } }, [currentStep, policyID, feed, backTo]); } // eslint-disable-next-line import/prefer-default-export -export {getExportMenuItem, useAssignCardStepNavigation}; +export {getExportMenuItem, useAssignCardNavigation}; From e1dae0d8219ec2999eb168d6a7c804667b40f888 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 10 Nov 2025 04:38:18 +0000 Subject: [PATCH 06/24] add flow --- src/ROUTES.ts | 60 ++++++++++ src/SCREENS.ts | 10 ++ .../ModalStackNavigators/index.tsx | 12 ++ src/libs/Navigation/linkingConfig/config.ts | 30 +++++ src/libs/Navigation/types.ts | 41 +++++++ .../companyCards/addNew/AddNewCardPage.tsx | 111 +++--------------- .../companyCards/addNew/AmexCustomFeed.tsx | 11 +- .../addNew/CardInstructionsStep.tsx | 15 ++- .../companyCards/addNew/CardNameStep.tsx | 11 +- .../companyCards/addNew/CardTypeStep.tsx | 11 +- .../companyCards/addNew/DetailsStep.tsx | 11 +- .../addNew/DirectStatementCloseDatePage.tsx | 15 ++- .../addNew/PlaidConnectionStep.tsx | 41 +++++-- .../companyCards/addNew/SelectBankStep.tsx | 12 +- .../companyCards/addNew/SelectCountryStep.tsx | 14 +-- .../companyCards/addNew/SelectFeedType.tsx | 11 +- .../addNew/StatementCloseDateStep.tsx | 14 ++- .../assignCard/AssignCardFeedPage.tsx | 24 ++-- .../companyCards/assignCard/AssigneeStep.tsx | 1 - .../assignCard/ConfirmationStep.tsx | 1 + src/pages/workspace/companyCards/utils.tsx | 61 +++++++++- src/pages/workspace/withPolicy.tsx | 3 +- 22 files changed, 373 insertions(+), 147 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 45ce887297b9..1f185b5620f4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2087,6 +2087,66 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed`, backTo), }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY: { + route: 'workspaces/:policyID/company-cards/add-card-feed/select-country', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-country`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK: { + route: 'workspaces/:policyID/company-cards/add-card-feed/select-bank', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-bank`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE: { + route: 'workspaces/:policyID/company-cards/add-card-feed/select-feed-type', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-feed-type`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE: { + route: 'workspaces/:policyID/company-cards/add-card-feed/card-type', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-type`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS: { + route: 'workspaces/:policyID/company-cards/add-card-feed/card-instructions', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-instructions`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME: { + route: 'workspaces/:policyID/company-cards/add-card-feed/card-name', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-name`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS: { + route: 'workspaces/:policyID/company-cards/add-card-feed/card-details', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-details`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED: { + route: 'workspaces/:policyID/company-cards/add-card-feed/amex-custom-feed', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/amex-custom-feed`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE: { + route: 'workspaces/:policyID/company-cards/add-card-feed/select-statement-close-date', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-statement-close-date`, backTo), + }, + WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE: { + route: 'workspaces/:policyID/company-cards/add-card-feed/select-direct-statement-close-date', + + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-direct-statement-close-date`, backTo), + }, WORKSPACE_COMPANY_CARDS_SELECT_FEED: { route: 'workspaces/:policyID/company-cards/select-feed', getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/select-feed` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index d97bcd3c9892..c71529a979f9 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -573,6 +573,16 @@ const SCREENS = { COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed', COMPANY_CARDS_BANK_CONNECTION: 'Workspace_CompanyCards_BankConnection', COMPANY_CARDS_ADD_NEW: 'Workspace_CompanyCards_New', + COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY: 'Workspace_CompanyCards_AddNew_SelectCountry', + COMPANY_CARDS_ADD_NEW_SELECT_BANK: 'Workspace_CompanyCards_AddNew_SelectBank', + COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE: 'Workspace_CompanyCards_AddNew_SelectFeedType', + COMPANY_CARDS_ADD_NEW_CARD_TYPE: 'Workspace_CompanyCards_AddNew_CardType', + COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS: 'Workspace_CompanyCards_AddNew_CardInstructions', + COMPANY_CARDS_ADD_NEW_CARD_NAME: 'Workspace_CompanyCards_AddNew_CardName', + COMPANY_CARDS_ADD_NEW_CARD_DETAILS: 'Workspace_CompanyCards_AddNew_CardDetails', + COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED: 'Workspace_CompanyCards_AddNew_AmexCustomFeed', + COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE: 'Workspace_CompanyCards_AddNew_SelectStatementCloseDate', + COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE: 'Workspace_CompanyCards_AddNew_SelectDirectStatementCloseDate', COMPANY_CARDS_TYPE: 'Workspace_CompanyCards_Type', COMPANY_CARDS_INSTRUCTIONS: 'Workspace_CompanyCards_Instructions', COMPANY_CARDS_NAME: 'Workspace_CompanyCards_Name', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index a7ac59212271..7c5c6a14293d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -698,6 +698,18 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: () => require('../../../../pages/workspace/companyCards/BankConnection').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: () => require('../../../../pages/workspace/companyCards/addNew/AddNewCardPage').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY]: () => require('../../../../pages/workspace/companyCards/addNew/SelectCountryStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_BANK]: () => require('../../../../pages/workspace/companyCards/addNew/SelectBankStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE]: () => require('../../../../pages/workspace/companyCards/addNew/SelectFeedType').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_TYPE]: () => require('../../../../pages/workspace/companyCards/addNew/CardTypeStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS]: () => require('../../../../pages/workspace/companyCards/addNew/CardInstructionsStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_NAME]: () => require('../../../../pages/workspace/companyCards/addNew/CardNameStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_DETAILS]: () => require('../../../../pages/workspace/companyCards/addNew/DetailsStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED]: () => require('../../../../pages/workspace/companyCards/addNew/AmexCustomFeed').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE]: () => + require('../../../../pages/workspace/companyCards/addNew/StatementCloseDateStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE]: () => + require('../../../../pages/workspace/companyCards/addNew/DirectStatementCloseDatePage').default, [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage').default, [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage').default, [SCREENS.WORKSPACE.COMPANY_CARD_EXPORT]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index b8ee28b82f09..03c6b97076e1 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -741,6 +741,36 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.route, }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_BANK]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_TYPE]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_NAME]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_DETAILS]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.route, + }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.route, + }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 63da778dc470..67c93d06b980 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1202,6 +1202,7 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { policyID: string; feed: CompanyCardFeed; + backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: { policyID: string; @@ -2263,6 +2264,46 @@ type WorkspaceSplitNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_BANK]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_TYPE]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_NAME]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_DETAILS]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE]: { + policyID: string; + backTo?: Routes; + }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE]: { + policyID: string; + backTo?: Routes; + }; [SCREENS.WORKSPACE.PER_DIEM]: { policyID: string; }; diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index 2ccca7c9828f..d76e14e8b5e7 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -1,49 +1,28 @@ import {isActingAsDelegateSelector} from '@selectors/Account'; -import React, {useEffect, useState} from 'react'; -import {View} from 'react-native'; -import ConfirmModal from '@components/ConfirmModal'; +import React, {useEffect} from 'react'; import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import usePermissions from '@hooks/usePermissions'; -import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; -import {navigateToConciergeChat} from '@libs/actions/Report'; -import BankConnection from '@pages/workspace/companyCards/BankConnection'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAddNewCardFlow, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import AmexCustomFeed from './AmexCustomFeed'; -import CardInstructionsStep from './CardInstructionsStep'; -import CardNameStep from './CardNameStep'; -import CardTypeStep from './CardTypeStep'; -import DetailsStep from './DetailsStep'; -import DirectStatementCloseDateStep from './DirectStatementCloseDatePage'; -import PlaidConnectionStep from './PlaidConnectionStep'; -import SelectBankStep from './SelectBankStep'; -import SelectCountryStep from './SelectCountryStep'; -import SelectFeedType from './SelectFeedType'; -import StatementCloseDateStep from './StatementCloseDateStep'; +import SCREENS from '@src/SCREENS'; +import {useAddNewCardNavigation} from '../utils'; -function AddNewCardPage({policy}: WithPolicyAndFullscreenLoadingProps) { +type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; + +function AddNewCardPage({policy, route}: AddNewCardPageProps) { const policyID = policy?.id; - const styles = useThemeStyles(); + const backTo = route.params?.backTo; const workspaceAccountID = useWorkspaceAccountID(policyID); - const [addNewCardFeed, addNewCardFeedMetadata] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: false}); - const {currentStep} = addNewCardFeed ?? {}; - const {isBetaEnabled} = usePermissions(); - const [isModalVisible, setIsModalVisible] = useState(false); - const {translate} = useLocalize(); - const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: false}); - const isAddCardFeedLoading = isLoadingOnyxValue(addNewCardFeedMetadata); - useEffect(() => { return () => { clearAddNewCardFlow(); @@ -60,9 +39,7 @@ function AddNewCardPage({policy}: WithPolicyAndFullscreenLoadingProps) { openPolicyAddCardFeedPage(policyID); }, [workspaceAccountID, policyID]); - if (isAddCardFeedLoading) { - return ; - } + useAddNewCardNavigation(policyID, backTo, true); if (isActingAsDelegate) { return ( @@ -76,68 +53,14 @@ function AddNewCardPage({policy}: WithPolicyAndFullscreenLoadingProps) { ); } - let CurrentStep: React.JSX.Element; - switch (currentStep) { - case CONST.COMPANY_CARDS.STEP.SELECT_BANK: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.CARD_TYPE: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.BANK_CONNECTION: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.CARD_NAME: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.CARD_DETAILS: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.PLAID_CONNECTION: - CurrentStep = setIsModalVisible(true)} />; - break; - case CONST.COMPANY_CARDS.STEP.SELECT_STATEMENT_CLOSE_DATE: - CurrentStep = ; - break; - case CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE: - CurrentStep = ; - break; - default: - CurrentStep = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) ? : ; - break; - } - return ( - <> - - {CurrentStep} - - setIsModalVisible(false)} - onConfirm={() => { - setIsModalVisible(false); - navigateToConciergeChat(); - }} - /> - + + + ); } diff --git a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx index 0f1decdf67cf..44289b9503e5 100644 --- a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx +++ b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx @@ -11,14 +11,23 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import * as CompanyCards from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; -function AmexCustomFeed() { +type AmexCustomFeedProps = PlatformStackScreenProps; + +function AmexCustomFeed({route}: AmexCustomFeedProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const [typeSelected, setTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index ef882baee50e..a91f126769b6 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -14,16 +14,16 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAddNewCompanyCardStepAndData} from '@libs/actions/CompanyCards'; import {getBankName} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import Parser from '@libs/Parser'; import Navigation from '@navigation/Navigation'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; import type {CardFeedProvider} from '@src/types/onyx/CardFeeds'; -type CardInstructionsStepProps = { - policyID?: string; -}; - function getCardInstructionHeader(feedProvider: CardFeedProvider) { if (feedProvider === CONST.COMPANY_CARD.FEED_BANK_NAME.VISA) { return 'workspace.companyCards.addNewCard.enableFeed.visa'; @@ -35,9 +35,14 @@ function getCardInstructionHeader(feedProvider: CardFeedProvider) { return 'workspace.companyCards.addNewCard.enableFeed.heading'; } -function CardInstructionsStep({policyID}: CardInstructionsStepProps) { +type CardInstructionsStepProps = PlatformStackScreenProps; + +function CardInstructionsStep({route}: CardInstructionsStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const {isOffline} = useNetwork(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx index 1b3762fb6323..4b17c40f835f 100644 --- a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx @@ -11,17 +11,26 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {addErrorMessage} from '@libs/ErrorUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddNewCardFeedForm'; -function CardNameStep() { +type CardNameStepProps = PlatformStackScreenProps; + +function CardNameStep({route}: CardNameStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const validate = (values: FormOnyxValues): FormInputErrors => { const errors = getFieldRequiredErrors(values, [INPUT_IDS.CARD_TITLE]); diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index d11aba623a8a..222e702fd1f0 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -15,10 +15,14 @@ import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; import {isPlaidSupportedCountry} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import variables from '@styles/variables'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; import type {CardFeedProvider} from '@src/types/onyx/CardFeeds'; type AvailableCompanyCardTypes = { @@ -83,13 +87,18 @@ function getAvailableCompanyCardTypes({translate, typeSelected, styles, canUsePl ]; } -function CardTypeStep() { +type CardTypeStepProps = PlatformStackScreenProps; + +function CardTypeStep({route}: CardTypeStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const [typeSelected, setTypeSelected] = useState(); const [isError, setIsError] = useState(false); const {isBetaEnabled} = usePermissions(); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const data = getAvailableCompanyCardTypes({translate, typeSelected, styles: styles.mr3, canUsePlaidCompanyCards: isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS)}); const {bankName, selectedBank, feedType} = addNewCard?.data ?? {}; const isOtherBankSelected = selectedBank === CONST.COMPANY_CARDS.BANKS.OTHER; diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index beb78e58876f..fbe5d8696297 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -15,20 +15,29 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import variables from '@styles/variables'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddNewCardFeedForm'; -function DetailsStep() { +type DetailsStepProps = PlatformStackScreenProps; + +function DetailsStep({route}: DetailsStepProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: false}); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const feedProvider = addNewCard?.data?.feedType; const isStripeFeedProvider = feedProvider === CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index ae70fbe3136f..6eac06e89451 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -7,19 +7,24 @@ import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {StatementPeriodEnd, StatementPeriodEndDay} from '@src/types/onyx/CardFeeds'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -type DirectStatementCloseDateStepProps = { - /** ID of the current policy */ - policyID?: string; -}; -function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepProps) { +type DirectStatementCloseDateStepProps = PlatformStackScreenProps; + +function DirectStatementCloseDateStep({route}: DirectStatementCloseDateStepProps) { const {translate} = useLocalize(); + const policyID = route.params?.policyID; const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); + + useAddNewCardNavigation(policyID, route.params?.backTo); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const workspaceAccountID = useWorkspaceAccountID(policyID); diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 67a49d305d08..982c218b53e4 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -1,9 +1,10 @@ -import React, {useCallback, useEffect, useRef} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {LinkSuccessMetadata} from 'react-native-plaid-link-sdk'; import type {PlaidLinkOnSuccessMetadata} from 'react-plaid-link/src/types'; import ActivityIndicator from '@components/ActivityIndicator'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import PlaidLink from '@components/PlaidLink'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -13,12 +14,14 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@libs/actions/CompanyCards'; +import {navigateToConciergeChat} from '@libs/actions/Report'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; +import {useAddNewCardNavigation, useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import {handleRestrictedEvent} from '@userActions/App'; import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; @@ -27,13 +30,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type PlaidLinkProps = { - onExit?: () => void; -}; +type PlaidConnectionStepProps = PlatformStackScreenProps; -type PlaidConnectionStepProps = PlatformStackScreenProps & PlaidLinkProps; - -function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { +function PlaidConnectionStep({route}: PlaidConnectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); @@ -49,6 +48,11 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { const {isOffline} = useNetwork(); const {policyID, feed} = route.params; const domain = getDomainNameForPolicy(policyID); + const [isExitModalVisible, setIsExitModalVisible] = useState(false); + + // Use the appropriate navigation hook based on whether we're in the assign card flow or add new card flow + useAssignCardNavigation(policyID, feed, route.params?.backTo); + useAddNewCardNavigation(policyID, route.params?.backTo); const isAuthenticatedWithPlaid = useCallback(() => !!plaidData?.bankAccounts?.length || !isEmptyObject(plaidData?.errors), [plaidData]); @@ -105,7 +109,7 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { const handleBackButtonPress = () => { if (feed) { - Navigation.goBack(); + Navigation.dismissModal(); return; } setAddNewCompanyCardStepAndData({step: isUSCountry ? CONST.COMPANY_CARDS.STEP.SELECT_BANK : CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); @@ -153,6 +157,7 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION, }); }); + Log.hmmm('1- Importing Plaid accounts in assign card flow'); return; } setAssignCardStepAndData({ @@ -164,6 +169,7 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { }, currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION, }); + Log.hmmm('2- Moving to bank connection step in assign card flow'); return; } @@ -176,6 +182,7 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { plaidAccounts: metadata?.accounts, }, }); + Log.hmmm('3- Moving to select statement close date step in add new card flow'); }} onError={handlePlaidLinkError} onEvent={(event) => { @@ -188,7 +195,7 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { // User prematurely exited the Plaid flow // eslint-disable-next-line react/jsx-props-no-multi-spaces onExit={() => { - onExit?.(); + setIsExitModalVisible(true); handleBackButtonPress(); }} /> @@ -224,6 +231,22 @@ function PlaidConnectionStep({route, onExit}: PlaidConnectionStepProps) { ) : ( {renderPlaidLink()} )} + { + setIsExitModalVisible(false); + Navigation.dismissModal(); + }} + onConfirm={() => { + setIsExitModalVisible(false); + navigateToConciergeChat(); + }} + /> ); } diff --git a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx index 3f39c4da9e72..e310449ac5e7 100644 --- a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx @@ -1,4 +1,3 @@ -import {useRoute} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; @@ -15,21 +14,26 @@ import usePermissions from '@hooks/usePermissions'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {getBankCardDetailsImage, getCorrectStepForPlaidSelectedBank, getCorrectStepForSelectedBank} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import Navigation from '@navigation/Navigation'; -import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import variables from '@styles/variables'; import {clearAddNewCardFlow, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -function SelectBankStep() { +type SelectBankStepProps = PlatformStackScreenProps; + +function SelectBankStep({route}: SelectBankStepProps) { const {translate} = useLocalize(); - const route = useRoute>(); const styles = useThemeStyles(); const illustrations = useThemeIllustrations(); const {isBetaEnabled} = usePermissions(); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const [bankSelected, setBankSelected] = useState | null>(); diff --git a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx index 7f3952e9f6b7..be65d4dbb192 100644 --- a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx @@ -1,4 +1,3 @@ -import {useRoute} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -13,12 +12,13 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {getPlaidCountry, isPlaidSupportedCountry} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import searchOptions from '@libs/searchOptions'; import type {Option} from '@libs/searchOptions'; import StringUtils from '@libs/StringUtils'; import Navigation from '@navigation/Navigation'; -import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import {clearAddNewCardFlow, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -27,15 +27,15 @@ import type SCREENS from '@src/SCREENS'; import type {CurrencyList} from '@src/types/onyx'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; -type CountryStepProps = { - policyID?: string; -}; +type CountryStepProps = PlatformStackScreenProps; -function SelectCountryStep({policyID}: CountryStepProps) { +function SelectCountryStep({route}: CountryStepProps) { const {translate} = useLocalize(); - const route = useRoute>(); const styles = useThemeStyles(); + const policyID = route.params?.policyID; const policy = usePolicy(policyID); + + useAddNewCardNavigation(policyID, route.params?.backTo); const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx index 5dbf6075beb9..957cdacdb67e 100644 --- a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx @@ -13,14 +13,23 @@ import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; import {isPlaidSupportedCountry} from '@libs/CardUtils'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; -function SelectFeedType() { +type SelectFeedTypeProps = PlatformStackScreenProps; + +function SelectFeedType({route}: SelectFeedTypeProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); + const policyID = route.params?.policyID; + + useAddNewCardNavigation(policyID, route.params?.backTo); const [typeSelected, setTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); const {isBetaEnabled} = usePermissions(); diff --git a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx index 045b784c01d5..8589fde73306 100644 --- a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx +++ b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx @@ -5,24 +5,28 @@ import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import {addNewCompanyCardsFeed, setAddNewCompanyCardStepAndData} from '@libs/actions/CompanyCards'; import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {StatementPeriodEnd, StatementPeriodEndDay} from '@src/types/onyx/CardFeeds'; -type StatementCloseDateStepProps = { - /** ID of the current policy */ - policyID: string | undefined; -}; +type StatementCloseDateStepProps = PlatformStackScreenProps; -function StatementCloseDateStep({policyID}: StatementCloseDateStepProps) { +function StatementCloseDateStep({route}: StatementCloseDateStepProps) { const {translate} = useLocalize(); const {isBetaEnabled} = usePermissions(); + const policyID = route.params?.policyID; const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: false}); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds] = useCardFeeds(policyID); + useAddNewCardNavigation(policyID, route.params?.backTo); + const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!addNewCard?.data?.publicToken; const submit = useCallback( diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 36f961b95423..b194dac592b7 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -1,10 +1,9 @@ import {isActingAsDelegateSelector} from '@selectors/Account'; import React, {useEffect} from 'react'; import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; -import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; -import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -12,7 +11,6 @@ import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullsc import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; import {useAssignCardNavigation} from '../utils'; @@ -31,17 +29,23 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }; }, []); + useAssignCardNavigation(policyID, feed, backTo, true); + if (!isActingAsDelegate) { - useAssignCardNavigation(policyID, feed, backTo, true); + return ( + + + + ); } return ( - - + + ); } diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index cdf73644d184..6ad81feec86f 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -60,7 +60,6 @@ function AssigneeStep({route}: AssigneeStepProps) { setSelectedMember(assignee.login ?? ''); setShouldShowError(false); }; - Log.hmmm(`AssignCard Data Step Data: ${JSON.stringify(assignCard?.data)}`); useAssignCardNavigation(policyID, feed, route.params?.backTo); diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index da43ff57000d..29a856cb92e9 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -88,6 +88,7 @@ function ConfirmationStep({route}: ConfirmationStepProps) { return; } assignWorkspaceCompanyCard(policyID, {...data, bankName}); + Navigation.dismissModal(); }; const editStep = (step: AssignCardStep) => { diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index 3a18a4da1aa8..4620c7574c65 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -4,6 +4,7 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {SelectorType} from '@components/SelectionScreen'; import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; +import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import {findSelectedBankAccountWithDefaultSelect, findSelectedVendorWithDefaultSelect, getCurrentConnectionName, getSageIntacctNonReimbursableActiveDefaultVendor} from '@libs/PolicyUtils'; @@ -22,6 +23,20 @@ type AssignCardRoute = | ReturnType | ReturnType; +type AddNewCardRoute = + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType; + type ExportIntegration = { title?: string; description?: string; @@ -411,5 +426,49 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard }, [currentStep, policyID, feed, backTo]); } +function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | undefined, isStartStep: boolean = false) { + const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const currentStep = addNewCard?.currentStep; + const previousStepRef = useRef(currentStep); + const {isBetaEnabled} = usePermissions(); + const defaultStep = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) ? CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY : CONST.COMPANY_CARDS.STEP.SELECT_BANK; + + useEffect(() => { + if (currentStep === previousStepRef.current && !isStartStep) { + return; + } + + previousStepRef.current = currentStep; + + if (!policyID) { + return; + } + + const stepRoutes: Record = { + [CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.SELECT_BANK]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.CARD_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute( + policyID, + addNewCard?.data?.selectedBank ?? '', + ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID, backTo), + ), + [CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.CARD_DETAILS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.PLAID_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, addNewCard?.data?.selectedBank ?? ''), + [CONST.COMPANY_CARDS.STEP.SELECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(policyID, backTo), + }; + + const targetRoute: AddNewCardRoute = currentStep ? stepRoutes[currentStep] : stepRoutes[defaultStep]; + if (targetRoute) { + Navigation.navigate(targetRoute); + } + }, [currentStep, policyID, backTo, isStartStep, addNewCard?.data?.selectedBank]); +} + // eslint-disable-next-line import/prefer-default-export -export {getExportMenuItem, useAssignCardNavigation}; +export {getExportMenuItem, useAssignCardNavigation, useAddNewCardNavigation}; diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index af306528aec6..0466e9048610 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -49,7 +49,8 @@ type PolicyRouteName = | typeof SCREENS.WORKSPACE.ACCOUNTING.CARD_RECONCILIATION | typeof SCREENS.WORKSPACE.RULES | typeof SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW - | typeof SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD; + | typeof SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD + | typeof SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW; type PolicyRoute = PlatformStackRouteProp; From 737fc8246cb7f0430edc7d6f7d6b6b46407c6646 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:33:07 +0000 Subject: [PATCH 07/24] add unit tests --- .../companyCards/addNew/AddNewCardPage.tsx | 6 +- tests/unit/CompanyCardsNavigationTest.ts | 511 ++++++++++++++++++ 2 files changed, 512 insertions(+), 5 deletions(-) create mode 100644 tests/unit/CompanyCardsNavigationTest.ts diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index d76e14e8b5e7..a472393799ef 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -54,11 +54,7 @@ function AddNewCardPage({policy, route}: AddNewCardPageProps) { } return ( - + ); diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts new file mode 100644 index 000000000000..9f0f4d8e82a8 --- /dev/null +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -0,0 +1,511 @@ +import {act, renderHook} from '@testing-library/react-native'; +import Onyx from 'react-native-onyx'; +import Navigation from '@libs/Navigation/Navigation'; +import {useAddNewCardNavigation, useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; + +// Mock Navigation +jest.mock('@libs/Navigation/Navigation', () => ({ + navigate: jest.fn(), +})); + +// Mock usePermissions hook +const mockIsBetaEnabled = jest.fn(); +jest.mock('@hooks/usePermissions', () => ({ + __esModule: true, + default: () => ({ + isBetaEnabled: mockIsBetaEnabled, + }), +})); + +const POLICY_ID = 'test-policy-id'; +const FEED = 'cdf'; +const BACK_TO = '/test/back/to'; + +describe('useAssignCardNavigation', () => { + beforeAll(() => { + Onyx.init({keys: ONYXKEYS}); + return waitForBatchedUpdates(); + }); + + beforeEach(() => { + jest.clearAllMocks(); + return Onyx.clear(); + }); + + it('should not navigate when step has not changed', async () => { + const currentStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Wait for effects to run + await waitForBatchedUpdates(); + + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('should navigate to ASSIGNEE step when step changes', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.CARD, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Change the step + await act(async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(POLICY_ID, FEED)); + }); + + it('should navigate to CARD step when step changes', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Change the step + await act(async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.CARD, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(POLICY_ID, FEED)); + }); + + it('should navigate to CONFIRMATION step with backTo parameter', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Change the step + await act(async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(POLICY_ID, FEED, BACK_TO)); + }); + + it('should navigate to BANK_CONNECTION step', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Change the step + await act(async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith( + ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(POLICY_ID, FEED, ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(POLICY_ID, FEED)), + ); + }); + + it('should navigate to PLAID_CONNECTION step', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + + // Change the step + await act(async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.PLAID_CONNECTION, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(POLICY_ID, FEED)); + }); + + it('should navigate immediately when isStartStep is true', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(POLICY_ID, FEED)); + }); + + it('should not navigate when policyID is undefined', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(undefined, FEED, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('should not navigate when feed is undefined', async () => { + await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { + currentStep: CONST.COMPANY_CARD.STEP.ASSIGNEE, + data: {email: 'test@example.com'}, + }); + + renderHook(() => useAssignCardNavigation(POLICY_ID, undefined, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); +}); + +describe('useAddNewCardNavigation', () => { + beforeAll(() => { + Onyx.init({keys: ONYXKEYS}); + return waitForBatchedUpdates(); + }); + + beforeEach(() => { + jest.clearAllMocks(); + mockIsBetaEnabled.mockReturnValue(false); + return Onyx.clear(); + }); + + it('should not navigate when step has not changed', async () => { + const currentStep = CONST.COMPANY_CARDS.STEP.SELECT_BANK; + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep, + data: {selectedBank: 'Stripe'}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('should navigate to SELECT_BANK step by default when PLAID beta is disabled', async () => { + mockIsBetaEnabled.mockReturnValue(false); + + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: undefined, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to SELECT_COUNTRY step by default when PLAID beta is enabled', async () => { + mockIsBetaEnabled.mockReturnValue(true); + + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: undefined, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to SELECT_COUNTRY step when step changes', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to SELECT_BANK step when step changes', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to SELECT_FEED_TYPE step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to CARD_TYPE step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_TYPE, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to BANK_CONNECTION step with selected bank', async () => { + const selectedBank = 'Stripe'; + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_TYPE, + data: {selectedBank}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.BANK_CONNECTION, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith( + ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(POLICY_ID, selectedBank, ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(POLICY_ID, BACK_TO)), + ); + }); + + it('should navigate to CARD_INSTRUCTIONS step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_TYPE, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to CARD_NAME step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_NAME, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to CARD_DETAILS step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_NAME, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_DETAILS, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to AMEX_CUSTOM_FEED step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to PLAID_CONNECTION step with selected bank', async () => { + const selectedBank = 'Chase'; + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE, + data: {selectedBank}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.PLAID_CONNECTION, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(POLICY_ID, selectedBank)); + }); + + it('should navigate to SELECT_STATEMENT_CLOSE_DATE step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_DETAILS, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_STATEMENT_CLOSE_DATE, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate to SELECT_DIRECT_STATEMENT_CLOSE_DATE step', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.CARD_DETAILS, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + + await act(async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE, + }); + }); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should navigate immediately when isStartStep is true', async () => { + mockIsBetaEnabled.mockReturnValue(false); + + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + }); + + it('should not navigate when policyID is undefined', async () => { + await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { + currentStep: CONST.COMPANY_CARDS.STEP.SELECT_BANK, + data: {}, + }); + + renderHook(() => useAddNewCardNavigation(undefined, BACK_TO, true)); + + await waitForBatchedUpdates(); + + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); +}); From e00a27e4acbc74bfdb20322f7d770493df1abc4a Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:38:30 +0000 Subject: [PATCH 08/24] eslint --- .../companyCards/addNew/AddNewCardPage.tsx | 8 +++++--- .../companyCards/addNew/AmexCustomFeed.tsx | 10 +++++----- .../companyCards/addNew/CardInstructionsStep.tsx | 2 +- .../workspace/companyCards/addNew/CardNameStep.tsx | 2 +- .../workspace/companyCards/addNew/CardTypeStep.tsx | 2 +- .../workspace/companyCards/addNew/DetailsStep.tsx | 2 +- .../addNew/DirectStatementCloseDatePage.tsx | 2 +- .../companyCards/addNew/PlaidConnectionStep.tsx | 2 +- .../companyCards/addNew/SelectFeedType.tsx | 2 +- .../companyCards/addNew/StatementCloseDateStep.tsx | 2 +- .../companyCards/assignCard/AssignCardFeedPage.tsx | 2 +- .../companyCards/assignCard/AssigneeStep.tsx | 3 +-- .../companyCards/assignCard/CardNameStep.tsx | 2 +- src/pages/workspace/companyCards/utils.tsx | 13 ++++++------- tests/unit/CompanyCardsNavigationTest.ts | 1 - 15 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index a472393799ef..2e6ef8cc5b64 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -12,8 +12,8 @@ import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPol import {clearAddNewCardFlow, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; -import {useAddNewCardNavigation} from '../utils'; +import type SCREENS from '@src/SCREENS'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; @@ -54,7 +54,9 @@ function AddNewCardPage({policy, route}: AddNewCardPageProps) { } return ( - + ); diff --git a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx index 44289b9503e5..e5303651425d 100644 --- a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx +++ b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx @@ -14,17 +14,17 @@ import useThemeStyles from '@hooks/useThemeStyles'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; -import * as CompanyCards from '@userActions/CompanyCards'; +import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; type AmexCustomFeedProps = PlatformStackScreenProps; function AmexCustomFeed({route}: AmexCustomFeedProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const policyID = route.params?.policyID; useAddNewCardNavigation(policyID, route.params?.backTo); @@ -36,7 +36,7 @@ function AmexCustomFeed({route}: AmexCustomFeedProps) { setHasError(true); return; } - CompanyCards.setAddNewCompanyCardStepAndData({ + setAddNewCompanyCardStepAndData({ step: typeSelected === CONST.COMPANY_CARDS.AMEX_CUSTOM_FEED.CORPORATE ? CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS : CONST.COMPANY_CARDS.STEP.BANK_CONNECTION, data: { feedType: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, @@ -50,7 +50,7 @@ function AmexCustomFeed({route}: AmexCustomFeedProps) { }, [addNewCard?.data.selectedAmexCustomFeed]); const handleBackButtonPress = () => { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); }; const data = [ diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index a91f126769b6..dc4d6c874bf8 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -21,7 +21,7 @@ import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import type {CardFeedProvider} from '@src/types/onyx/CardFeeds'; function getCardInstructionHeader(feedProvider: CardFeedProvider) { diff --git a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx index 4b17c40f835f..11c25b02d5cb 100644 --- a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx @@ -18,7 +18,7 @@ import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddNewCardFeedForm'; type CardNameStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index 222e702fd1f0..2aa8005f9005 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -22,7 +22,7 @@ import variables from '@styles/variables'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import type {CardFeedProvider} from '@src/types/onyx/CardFeeds'; type AvailableCompanyCardTypes = { diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index fbe5d8696297..9c8241fa7445 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -23,7 +23,7 @@ import variables from '@styles/variables'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddNewCardFeedForm'; type DetailsStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index 6eac06e89451..6961aa880e4e 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -13,7 +13,7 @@ import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import type {StatementPeriodEnd, StatementPeriodEndDay} from '@src/types/onyx/CardFeeds'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 982c218b53e4..1df6480b8035 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -27,7 +27,7 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type PlaidConnectionStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx index 957cdacdb67e..afef104010a2 100644 --- a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx @@ -19,7 +19,7 @@ import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; type SelectFeedTypeProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx index 8589fde73306..4440cf822860 100644 --- a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx +++ b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx @@ -12,7 +12,7 @@ import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspac import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; +import type SCREENS from '@src/SCREENS'; import type {StatementPeriodEnd, StatementPeriodEndDay} from '@src/types/onyx/CardFeeds'; type StatementCloseDateStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index b194dac592b7..9d3f439302ca 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -13,7 +13,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; -import {useAssignCardNavigation} from '../utils'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 6ad81feec86f..ab11b1073f45 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -15,7 +15,6 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; -import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; @@ -43,7 +42,7 @@ function AssigneeStep({route}: AssigneeStepProps) { const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const policyID = route.params?.policyID; - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true}); const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const [list] = useCardsList(policy?.id, feed); const [cardFeeds] = useCardFeeds(policy?.id); diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index 0ecde820ef7f..fe81d4ec1c8e 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -29,7 +29,7 @@ function CardNameStep({route}: CardNameStepProps) { const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); const styles = useThemeStyles(); - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const policyID = route.params?.policyID; const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const data = assignCard?.data; diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index 4620c7574c65..fb7dedb8d391 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -5,7 +5,6 @@ import type {SelectorType} from '@components/SelectionScreen'; import useInitial from '@hooks/useInitial'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; -import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import {findSelectedBankAccountWithDefaultSelect, findSelectedVendorWithDefaultSelect, getCurrentConnectionName, getSageIntacctNonReimbursableActiveDefaultVendor} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; @@ -387,8 +386,8 @@ function getExportMenuItem( } } -function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined, isStartStep: boolean = false) { - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD); +function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined, isStartStep = false) { + const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; const previousStepRef = useRef(currentStep); const firstAssigneeEmail = useInitial(assignCard?.data?.email); @@ -423,11 +422,11 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard if (targetRoute) { Navigation.navigate(targetRoute); } - }, [currentStep, policyID, feed, backTo]); + }, [currentStep, policyID, feed, backTo, isStartStep, shouldUseBackToParam]); } -function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | undefined, isStartStep: boolean = false) { - const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); +function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | undefined, isStartStep = false) { + const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const currentStep = addNewCard?.currentStep; const previousStepRef = useRef(currentStep); const {isBetaEnabled} = usePermissions(); @@ -467,7 +466,7 @@ function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | if (targetRoute) { Navigation.navigate(targetRoute); } - }, [currentStep, policyID, backTo, isStartStep, addNewCard?.data?.selectedBank]); + }, [currentStep, policyID, backTo, isStartStep, addNewCard?.data?.selectedBank, defaultStep]); } // eslint-disable-next-line import/prefer-default-export diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts index 9f0f4d8e82a8..439aef473ffa 100644 --- a/tests/unit/CompanyCardsNavigationTest.ts +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -15,7 +15,6 @@ jest.mock('@libs/Navigation/Navigation', () => ({ // Mock usePermissions hook const mockIsBetaEnabled = jest.fn(); jest.mock('@hooks/usePermissions', () => ({ - __esModule: true, default: () => ({ isBetaEnabled: mockIsBetaEnabled, }), From 23b3b079f39bc09c396bb67cde66c38e34ca329b Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:05:32 +0000 Subject: [PATCH 09/24] fix tests & lint --- .../companyCards/addNew/AddNewCardPage.tsx | 6 ++--- .../assignCard/AssignCardFeedPage.tsx | 2 +- .../assignCard/CardSelectionStep.tsx | 2 +- tests/ui/AssignCardFeedPage.tsx | 26 +++++++++++++++++++ tests/unit/CompanyCardsNavigationTest.ts | 8 +++--- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index 2e6ef8cc5b64..bf5fd413b58a 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -7,13 +7,13 @@ import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; +import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAddNewCardFlow, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; @@ -54,9 +54,7 @@ function AddNewCardPage({policy, route}: AddNewCardPageProps) { } return ( - + ); diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 9d3f439302ca..dcebbef3ce9c 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -6,6 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useOnyx from '@hooks/useOnyx'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; +import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; @@ -13,7 +14,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; -import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index a53eb185051b..5d3c2ba8b7fe 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -29,7 +29,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; +import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; type CardSelectionStepProps = PlatformStackScreenProps; diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 9ea1d8ae6151..7d1157409f5c 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -13,6 +13,7 @@ import Navigation from '@libs/Navigation/Navigation'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AssignCardFeedPage from '@pages/workspace/companyCards/assignCard/AssignCardFeedPage'; +import ConfirmationStep from '@pages/workspace/companyCards/assignCard/ConfirmationStep'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -66,6 +67,10 @@ const renderPage = (initialRouteName: typeof SCREENS.WORKSPACE.COMPANY_CARDS_ASS component={AssignCardFeedPage} initialParams={initialParams} /> + @@ -110,8 +115,16 @@ describe('AssignCardFeedPage', () => { // Add mock policy and mock the assign card details await act(async () => { + await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + 1234: { + accountID: 1234, + login: 'testaccount+1@gmail.com', + displayName: 'Test User 1', + }, + }); await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { data: { bankName: 'vcf', @@ -177,8 +190,21 @@ describe('AssignCardFeedPage', () => { // Add mock policy and mock the assign card details await act(async () => { + await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + 1234: { + accountID: 1234, + login: 'testaccount+1@gmail.com', + displayName: 'Test User 1', + }, + 5678: { + accountID: 5678, + login: 'testaccount+2@gmail.com', + displayName: 'Test User 2', + }, + }); await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { data: { bankName: 'vcf', diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts index 439aef473ffa..8b0059aee093 100644 --- a/tests/unit/CompanyCardsNavigationTest.ts +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -14,11 +14,11 @@ jest.mock('@libs/Navigation/Navigation', () => ({ // Mock usePermissions hook const mockIsBetaEnabled = jest.fn(); -jest.mock('@hooks/usePermissions', () => ({ - default: () => ({ +jest.mock('@hooks/usePermissions', () => + jest.fn(() => ({ isBetaEnabled: mockIsBetaEnabled, - }), -})); + })), +); const POLICY_ID = 'test-policy-id'; const FEED = 'cdf'; From dc173a224c59959ac46543c69bf435469e9252a3 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:32:07 +0000 Subject: [PATCH 10/24] fix test assign card page --- tests/ui/AssignCardFeedPage.tsx | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 7d1157409f5c..3c2ff045770c 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -115,11 +115,12 @@ describe('AssignCardFeedPage', () => { // Add mock policy and mock the assign card details await act(async () => { + await Onyx.merge(ONYXKEYS.HAS_LOADED_APP, true); await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - 1234: { + '1234': { accountID: 1234, login: 'testaccount+1@gmail.com', displayName: 'Test User 1', @@ -150,10 +151,13 @@ describe('AssignCardFeedPage', () => { await waitForBatchedUpdatesWithAct(); - // Verify that Assign card button is visible on the screen - await waitFor(() => { - expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); - }); + // Wait for navigation to ConfirmationStep and verify that Assign card button is visible on the screen + await waitFor( + () => { + expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); + }, + {timeout: 5000}, + ); // Click the Assign Card button const assignCardButton = screen.getByTestId('assignCardButtonTestID'); @@ -190,16 +194,17 @@ describe('AssignCardFeedPage', () => { // Add mock policy and mock the assign card details await act(async () => { + await Onyx.merge(ONYXKEYS.HAS_LOADED_APP, true); await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - 1234: { + '1234': { accountID: 1234, login: 'testaccount+1@gmail.com', displayName: 'Test User 1', }, - 5678: { + '5678': { accountID: 5678, login: 'testaccount+2@gmail.com', displayName: 'Test User 2', @@ -229,6 +234,14 @@ describe('AssignCardFeedPage', () => { await waitForBatchedUpdatesWithAct(); + // Wait for navigation to ConfirmationStep + await waitFor( + () => { + expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); + }, + {timeout: 5000}, + ); + // Mock the action of changing the assignee of the card await act(async () => { await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { @@ -238,11 +251,6 @@ describe('AssignCardFeedPage', () => { }); }); - // Verify that Assign card button is visible on the screen - await waitFor(() => { - expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); - }); - // Click the Assign Card button const assignCardButton = screen.getByTestId('assignCardButtonTestID'); From a6d8c94576a29d4484f92409603cbdb38ca722be Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:45:24 +0000 Subject: [PATCH 11/24] fix AssignCardFeedPage test again --- .../assignCard/AssignCardFeedPage.tsx | 4 +- tests/ui/AssignCardFeedPage.tsx | 143 +++++------------- 2 files changed, 37 insertions(+), 110 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index dcebbef3ce9c..4c5215e17991 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -31,7 +31,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { useAssignCardNavigation(policyID, feed, backTo, true); - if (!isActingAsDelegate) { + if (isActingAsDelegate) { return ( - + ); } diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 3c2ff045770c..a648b1802a14 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -1,6 +1,6 @@ import {PortalProvider} from '@gorhom/portal'; import {NavigationContainer} from '@react-navigation/native'; -import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-native'; +import {act, render, screen, waitFor} from '@testing-library/react-native'; import React from 'react'; import Onyx from 'react-native-onyx'; import ComposeProviders from '@components/ComposeProviders'; @@ -13,7 +13,6 @@ import Navigation from '@libs/Navigation/Navigation'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AssignCardFeedPage from '@pages/workspace/companyCards/assignCard/AssignCardFeedPage'; -import ConfirmationStep from '@pages/workspace/companyCards/assignCard/ConfirmationStep'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -67,10 +66,6 @@ const renderPage = (initialRouteName: typeof SCREENS.WORKSPACE.COMPANY_CARDS_ASS component={AssignCardFeedPage} initialParams={initialParams} /> - @@ -102,30 +97,19 @@ describe('AssignCardFeedPage', () => { jest.clearAllMocks(); }); - it('should navigate to the member details page as the assignee email has not changed', async () => { + it('should render loading indicator and call navigation to confirmation step', async () => { // Sign in as a test user before running the test. await TestHelper.signInWithTestUser(); - const goBack = jest.spyOn(Navigation, 'goBack'); + const navigateSpy = jest.spyOn(Navigation, 'navigate'); const policy = { ...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, - areTagsEnabled: true, - requiresTag: true, }; - // Add mock policy and mock the assign card details + // Set up Onyx data BEFORE rendering await act(async () => { - await Onyx.merge(ONYXKEYS.HAS_LOADED_APP, true); await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - '1234': { - accountID: 1234, - login: 'testaccount+1@gmail.com', - displayName: 'Test User 1', - }, - }); await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { data: { bankName: 'vcf', @@ -137,138 +121,81 @@ describe('AssignCardFeedPage', () => { dateOption: 'fromBeginning', startDate: '2024-12-27', }, - currentStep: 'Confirmation', + currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, isEditing: false, }); }); + await waitForBatchedUpdatesWithAct(); - // Render the page with the specified policyID and backTo param + // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, - backTo: ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy?.id, 1234), + backTo: ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy.id, 1234), }); - await waitForBatchedUpdatesWithAct(); - - // Wait for navigation to ConfirmationStep and verify that Assign card button is visible on the screen - await waitFor( - () => { - expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); - }, - {timeout: 5000}, - ); - - // Click the Assign Card button - const assignCardButton = screen.getByTestId('assignCardButtonTestID'); - - // Create a mock event object that matches GestureResponderEvent. - const mockEvent = { - nativeEvent: {}, - type: 'press', - target: assignCardButton, - currentTarget: assignCardButton, - }; - fireEvent.press(assignCardButton, mockEvent); + // Verify the page renders with loading indicator + await waitFor(() => { + expect(screen.getByTestId('AssignCardFeedPage')).toBeOnTheScreen(); + expect(screen.getByTestId('assign-card-loading-indicator')).toBeOnTheScreen(); + }); - // Verify that we navigate to the member details page as the card assignee has not changed + // Verify navigation was called to the confirmation step await waitFor(() => { - expect(goBack).toHaveBeenCalledWith(ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy.id, 1234)); + expect(navigateSpy).toHaveBeenCalled(); }); + // Verify the navigation call contains the correct route parts + const navigationCall = navigateSpy.mock.calls[0]?.[0]; + expect(navigationCall).toContain('company-cards'); + expect(navigationCall).toContain('assign-card/confirmation'); + expect(navigationCall).toContain(policy.id); + expect(navigationCall).toContain('backTo'); + // Unmount the component after assertions to clean up. unmount(); await waitForBatchedUpdatesWithAct(); }); - it('should navigate to the company cards page as the assignee email has changed', async () => { + it('should show delegate no access message when user is acting as delegate', async () => { // Sign in as a test user before running the test. await TestHelper.signInWithTestUser(); - const navigate = jest.spyOn(Navigation, 'navigate'); + const navigateSpy = jest.spyOn(Navigation, 'navigate'); const policy = { ...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, - areTagsEnabled: true, - requiresTag: true, }; - // Add mock policy and mock the assign card details + // Set up Onyx data with delegate access await act(async () => { - await Onyx.merge(ONYXKEYS.HAS_LOADED_APP, true); await Onyx.merge(ONYXKEYS.IS_LOADING_REPORT_DATA, false); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); - await Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - '1234': { - accountID: 1234, - login: 'testaccount+1@gmail.com', - displayName: 'Test User 1', - }, - '5678': { - accountID: 5678, - login: 'testaccount+2@gmail.com', - displayName: 'Test User 2', + await Onyx.merge(ONYXKEYS.ACCOUNT, { + delegatedAccess: { + delegate: 'delegate@example.com', }, }); await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { data: { bankName: 'vcf', email: 'testaccount+1@gmail.com', - cardName: "Test 1's card", - cardNumber: '490901XXXXXX1234', - // cspell:disable-next-line - encryptedCardNumber: 'v12:74E3CA3C4C0FA02FDCF754FDSFDSF', - dateOption: 'fromBeginning', - startDate: '2024-12-27', }, - currentStep: 'Confirmation', - isEditing: false, + currentStep: CONST.COMPANY_CARD.STEP.CONFIRMATION, }); }); - // Render the page with the specified policyID and backTo param + await waitForBatchedUpdatesWithAct(); + + // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, - backTo: ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy?.id, 1234), }); - await waitForBatchedUpdatesWithAct(); - - // Wait for navigation to ConfirmationStep - await waitFor( - () => { - expect(screen.getByTestId('assignCardButtonTestID')).toBeOnTheScreen(); - }, - {timeout: 5000}, - ); - - // Mock the action of changing the assignee of the card - await act(async () => { - await Onyx.merge(ONYXKEYS.ASSIGN_CARD, { - data: { - email: 'testaccount+2@gmail.com', - }, - }); - }); - - // Click the Assign Card button - const assignCardButton = screen.getByTestId('assignCardButtonTestID'); - - // Create a mock event object that matches GestureResponderEvent - const mockEvent = { - nativeEvent: {}, - type: 'press', - target: assignCardButton, - currentTarget: assignCardButton, - }; - fireEvent.press(assignCardButton, mockEvent); - - await waitForBatchedUpdatesWithAct(); - - // Verify that we navigate to the company cards page as the card assignee has changed + // Verify the page renders await waitFor(() => { - expect(navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy.id), {forceReplace: true}); + expect(screen.getByTestId('AssignCardFeedPage')).toBeOnTheScreen(); }); + // Unmount the component after assertions to clean up. unmount(); await waitForBatchedUpdatesWithAct(); From 242b68053ff4a3e8256c13ac6dfb1963e7a0e956 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 11 Nov 2025 21:45:09 +0000 Subject: [PATCH 12/24] lint --- tests/ui/AssignCardFeedPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index a648b1802a14..96c1b80aed3f 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -146,7 +146,7 @@ describe('AssignCardFeedPage', () => { }); // Verify the navigation call contains the correct route parts - const navigationCall = navigateSpy.mock.calls[0]?.[0]; + const navigationCall = navigateSpy.mock.calls.at(0)?.[0]; expect(navigationCall).toContain('company-cards'); expect(navigationCall).toContain('assign-card/confirmation'); expect(navigationCall).toContain(policy.id); @@ -160,7 +160,6 @@ describe('AssignCardFeedPage', () => { it('should show delegate no access message when user is acting as delegate', async () => { // Sign in as a test user before running the test. await TestHelper.signInWithTestUser(); - const navigateSpy = jest.spyOn(Navigation, 'navigate'); const policy = { ...LHNTestUtils.getFakePolicy(), role: CONST.POLICY.ROLE.ADMIN, From 72cdc21fd8f4b85fad4728614b9ec2acbda6ca2c Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Thu, 13 Nov 2025 03:50:30 +0000 Subject: [PATCH 13/24] remove the deprecated backTo --- src/ROUTES.ts | 64 ++++---------- src/libs/Navigation/types.ts | 18 ---- .../companyCards/addNew/AddNewCardPage.tsx | 8 +- .../companyCards/addNew/AmexCustomFeed.tsx | 2 +- .../addNew/CardInstructionsStep.tsx | 2 +- .../companyCards/addNew/CardNameStep.tsx | 2 +- .../companyCards/addNew/CardTypeStep.tsx | 2 +- .../companyCards/addNew/DetailsStep.tsx | 2 +- .../addNew/DirectStatementCloseDatePage.tsx | 2 +- .../addNew/PlaidConnectionStep.tsx | 13 ++- .../companyCards/addNew/SelectBankStep.tsx | 7 +- .../companyCards/addNew/SelectCountryStep.tsx | 6 +- .../companyCards/addNew/SelectFeedType.tsx | 2 +- .../addNew/StatementCloseDateStep.tsx | 2 +- .../assignCard/AssignCardFeedPage.tsx | 2 +- .../companyCards/assignCard/AssigneeStep.tsx | 2 +- .../companyCards/assignCard/CardNameStep.tsx | 2 +- .../assignCard/CardSelectionStep.tsx | 2 +- .../assignCard/ConfirmationStep.tsx | 9 +- .../assignCard/TransactionStartDateStep.tsx | 4 +- src/pages/workspace/companyCards/utils.tsx | 32 +++---- tests/ui/AssignCardFeedPage.tsx | 1 - tests/unit/CompanyCardsNavigationTest.ts | 83 +++++++++---------- 23 files changed, 103 insertions(+), 166 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6905fce97195..81a0c4f5cb86 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2119,63 +2119,43 @@ const ROUTES = { }, WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY: { route: 'workspaces/:policyID/company-cards/add-card-feed/select-country', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-country`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/select-country` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK: { route: 'workspaces/:policyID/company-cards/add-card-feed/select-bank', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-bank`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/select-bank` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE: { route: 'workspaces/:policyID/company-cards/add-card-feed/select-feed-type', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-feed-type`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/select-feed-type` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE: { route: 'workspaces/:policyID/company-cards/add-card-feed/card-type', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-type`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/card-type` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS: { route: 'workspaces/:policyID/company-cards/add-card-feed/card-instructions', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-instructions`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/card-instructions` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME: { route: 'workspaces/:policyID/company-cards/add-card-feed/card-name', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-name`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/card-name` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS: { route: 'workspaces/:policyID/company-cards/add-card-feed/card-details', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/card-details`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/card-details` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED: { route: 'workspaces/:policyID/company-cards/add-card-feed/amex-custom-feed', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/amex-custom-feed`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/amex-custom-feed` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE: { route: 'workspaces/:policyID/company-cards/add-card-feed/select-statement-close-date', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-statement-close-date`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/select-statement-close-date` as const, }, WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE: { route: 'workspaces/:policyID/company-cards/add-card-feed/select-direct-statement-close-date', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/add-card-feed/select-direct-statement-close-date`, backTo), + getRoute: (policyID: string) => `workspaces/${policyID}/company-cards/add-card-feed/select-direct-statement-close-date` as const, }, WORKSPACE_COMPANY_CARDS_SELECT_FEED: { route: 'workspaces/:policyID/company-cards/select-feed', @@ -2189,39 +2169,27 @@ const ROUTES = { }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/assignee', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/assignee`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/assignee` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/select', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/select`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/select` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/transaction-start-date-step', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date-step`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/transaction-start-date-step` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/name', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/name`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/name` as const, }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/confirmation', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/confirmation`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/confirmation` as const, }, WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/plaid-connection', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (policyID: string, feed: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/company-cards/${feed}/assign-card/plaid-connection`, backTo), + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/plaid-connection` as const, }, WORKSPACE_COMPANY_CARD_DETAILS: { route: 'workspaces/:policyID/company-cards/:bank/:cardID', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index cfce4774bed8..d46a0f7feb2f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1181,32 +1181,26 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_NAME]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { policyID: string; feed: CompanyCardFeed; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: { policyID: string; @@ -2265,48 +2259,36 @@ type WorkspaceSplitNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: { policyID: string; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_BANK]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_TYPE]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_NAME]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_CARD_DETAILS]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE]: { policyID: string; - backTo?: Routes; }; [SCREENS.WORKSPACE.PER_DIEM]: { policyID: string; diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index bf5fd413b58a..f4eda3178ec6 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -10,22 +10,22 @@ import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import {clearAddNewCardFlow, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; +import {clearAddNewCardFlow, clearAssignCardStepAndData, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; -function AddNewCardPage({policy, route}: AddNewCardPageProps) { +function AddNewCardPage({policy}: AddNewCardPageProps) { const policyID = policy?.id; - const backTo = route.params?.backTo; const workspaceAccountID = useWorkspaceAccountID(policyID); const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: false}); useEffect(() => { return () => { clearAddNewCardFlow(); + clearAssignCardStepAndData(); }; }, []); @@ -39,7 +39,7 @@ function AddNewCardPage({policy, route}: AddNewCardPageProps) { openPolicyAddCardFeedPage(policyID); }, [workspaceAccountID, policyID]); - useAddNewCardNavigation(policyID, backTo, true); + useAddNewCardNavigation(policyID, true); if (isActingAsDelegate) { return ( diff --git a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx index e5303651425d..740615b9336d 100644 --- a/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx +++ b/src/pages/workspace/companyCards/addNew/AmexCustomFeed.tsx @@ -27,7 +27,7 @@ function AmexCustomFeed({route}: AmexCustomFeedProps) { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const [typeSelected, setTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index dc4d6c874bf8..618b255472bf 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -42,7 +42,7 @@ function CardInstructionsStep({route}: CardInstructionsStepProps) { const styles = useThemeStyles(); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const {isOffline} = useNetwork(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx index 11c25b02d5cb..b949edadc964 100644 --- a/src/pages/workspace/companyCards/addNew/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardNameStep.tsx @@ -30,7 +30,7 @@ function CardNameStep({route}: CardNameStepProps) { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const validate = (values: FormOnyxValues): FormInputErrors => { const errors = getFieldRequiredErrors(values, [INPUT_IDS.CARD_TITLE]); diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index 2aa8005f9005..22528c532286 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -98,7 +98,7 @@ function CardTypeStep({route}: CardTypeStepProps) { const {isBetaEnabled} = usePermissions(); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const data = getAvailableCompanyCardTypes({translate, typeSelected, styles: styles.mr3, canUsePlaidCompanyCards: isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS)}); const {bankName, selectedBank, feedType} = addNewCard?.data ?? {}; const isOtherBankSelected = selectedBank === CONST.COMPANY_CARDS.BANKS.OTHER; diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index 9c8241fa7445..ddd54f5e6715 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -37,7 +37,7 @@ function DetailsStep({route}: DetailsStepProps) { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: false}); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const feedProvider = addNewCard?.data?.feedType; const isStripeFeedProvider = feedProvider === CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index 6961aa880e4e..bfa62716cb2f 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -24,7 +24,7 @@ function DirectStatementCloseDateStep({route}: DirectStatementCloseDateStepProps const policyID = route.params?.policyID; const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const workspaceAccountID = useWorkspaceAccountID(policyID); diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 1df6480b8035..dafa657cd261 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -51,8 +51,8 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { const [isExitModalVisible, setIsExitModalVisible] = useState(false); // Use the appropriate navigation hook based on whether we're in the assign card flow or add new card flow - useAssignCardNavigation(policyID, feed, route.params?.backTo); - useAddNewCardNavigation(policyID, route.params?.backTo); + useAssignCardNavigation(policyID, feed); + useAddNewCardNavigation(policyID); const isAuthenticatedWithPlaid = useCallback(() => !!plaidData?.bankAccounts?.length || !isEmptyObject(plaidData?.errors), [plaidData]); @@ -107,8 +107,8 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { previousNetworkState.current = isOffline; }, [addNewCard?.data?.selectedCountry, domain, feed, isAuthenticatedWithPlaid, isOffline]); - const handleBackButtonPress = () => { - if (feed) { + const handleBackButtonPress = (showingExitModal: boolean = false) => { + if (feed && !showingExitModal) { Navigation.dismissModal(); return; } @@ -157,7 +157,6 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION, }); }); - Log.hmmm('1- Importing Plaid accounts in assign card flow'); return; } setAssignCardStepAndData({ @@ -169,7 +168,6 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { }, currentStep: CONST.COMPANY_CARD.STEP.BANK_CONNECTION, }); - Log.hmmm('2- Moving to bank connection step in assign card flow'); return; } @@ -182,7 +180,6 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { plaidAccounts: metadata?.accounts, }, }); - Log.hmmm('3- Moving to select statement close date step in add new card flow'); }} onError={handlePlaidLinkError} onEvent={(event) => { @@ -196,7 +193,7 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { // eslint-disable-next-line react/jsx-props-no-multi-spaces onExit={() => { setIsExitModalVisible(true); - handleBackButtonPress(); + handleBackButtonPress(true); }} /> ); diff --git a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx index e310449ac5e7..ca15909ab5cd 100644 --- a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx @@ -22,6 +22,7 @@ import variables from '@styles/variables'; import {clearAddNewCardFlow, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; type SelectBankStepProps = PlatformStackScreenProps; @@ -64,14 +65,10 @@ function SelectBankStep({route}: SelectBankStepProps) { }, [addNewCard?.data.selectedBank]); const handleBackButtonPress = () => { - if (route?.params?.backTo) { - Navigation.navigate(route.params.backTo); - return; - } if (isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS)) { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE, data: {selectedBank: null}}); } else { - Navigation.goBack(); + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID)); } }; diff --git a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx index be65d4dbb192..63c7bc8a486a 100644 --- a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx @@ -77,11 +77,7 @@ function SelectCountryStep({route}: CountryStepProps) { }, [getCountry]); const handleBackButtonPress = () => { - if (route?.params?.backTo) { - Navigation.navigate(route.params.backTo); - return; - } - Navigation.goBack(); + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID)); }; const onSelectionChange = useCallback((country: Option) => { diff --git a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx index afef104010a2..38c79982b628 100644 --- a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx @@ -29,7 +29,7 @@ function SelectFeedType({route}: SelectFeedTypeProps) { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const [typeSelected, setTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); const {isBetaEnabled} = usePermissions(); diff --git a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx index 4440cf822860..547d84733072 100644 --- a/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx +++ b/src/pages/workspace/companyCards/addNew/StatementCloseDateStep.tsx @@ -25,7 +25,7 @@ function StatementCloseDateStep({route}: StatementCloseDateStepProps) { const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds] = useCardFeeds(policyID); - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!addNewCard?.data?.publicToken; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 4c5215e17991..92924526a51d 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -29,7 +29,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { }; }, []); - useAssignCardNavigation(policyID, feed, backTo, true); + useAssignCardNavigation(policyID, feed, true); if (isActingAsDelegate) { return ( diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index ab11b1073f45..484061cd27bd 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -60,7 +60,7 @@ function AssigneeStep({route}: AssigneeStepProps) { setShouldShowError(false); }; - useAssignCardNavigation(policyID, feed, route.params?.backTo); + useAssignCardNavigation(policyID, feed); const submit = () => { let nextStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; diff --git a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx index fe81d4ec1c8e..2e9c2d4c86ba 100644 --- a/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardNameStep.tsx @@ -34,7 +34,7 @@ function CardNameStep({route}: CardNameStepProps) { const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const data = assignCard?.data; - useAssignCardNavigation(policyID, feed, route.params?.backTo); + useAssignCardNavigation(policyID, feed); const submit = (values: FormOnyxValues) => { setAssignCardStepAndData({ diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 5d3c2ba8b7fe..de81dd723670 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -74,7 +74,7 @@ function CardSelectionStep({route}: CardSelectionStepProps) { ), })); - useAssignCardNavigation(policyID, feed, route.params?.backTo); + useAssignCardNavigation(policyID, feed); const handleBackButtonPress = () => { if (isEditing) { diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 29a856cb92e9..b9a8564dee98 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -36,7 +36,6 @@ function ConfirmationStep({route}: ConfirmationStepProps) { const {isOffline} = useNetwork(); const policyID = route.params?.policyID; - const backTo = route.params?.backTo; const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); @@ -50,7 +49,7 @@ function ConfirmationStep({route}: ConfirmationStepProps) { const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((_route) => isFullScreenName(_route.name))); - useAssignCardNavigation(policyID, feed, backTo); + useAssignCardNavigation(policyID, feed); useEffect(() => { if (!assignCard?.isAssigned) { @@ -58,14 +57,14 @@ function ConfirmationStep({route}: ConfirmationStepProps) { } const lastRoute = currentFullScreenRoute?.state?.routes.at(-1); - if (backTo ?? lastRoute?.name === SCREENS.WORKSPACE.COMPANY_CARDS) { - Navigation.goBack(backTo); + if (lastRoute?.name === SCREENS.WORKSPACE.COMPANY_CARDS) { + Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); } else { Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID), {forceReplace: true}); } // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => clearAssignCardStepAndData()); - }, [assignCard, backTo, policyID, currentFullScreenRoute?.state?.routes]); + }, [assignCard, policyID, currentFullScreenRoute?.state?.routes]); const submit = () => { if (!policyID) { diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index 3b59fff35253..896edae63edf 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -30,12 +30,12 @@ function TransactionStartDateStep({route}: TransactionStartDateStepProps) { const isEditing = assignCard?.isEditing; const data = assignCard?.data; const assigneeDisplayName = getPersonalDetailByEmail(data?.email ?? '')?.displayName ?? ''; - const {policyID, feed, backTo} = route.params; + const {policyID, feed} = route.params; const [dateOptionSelected, setDateOptionSelected] = useState(data?.dateOption ?? CONST.COMPANY_CARD.TRANSACTION_START_DATE_OPTIONS.CUSTOM); const [errorText, setErrorText] = useState(''); const [startDate, setStartDate] = useState(() => assignCard?.startDate ?? data?.startDate ?? format(new Date(), CONST.DATE.FNS_FORMAT_STRING)); - useAssignCardNavigation(policyID, feed, backTo); + useAssignCardNavigation(policyID, feed); const handleBackButtonPress = () => { if (isEditing) { diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index fb7dedb8d391..a8c8e70eaabe 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -386,7 +386,7 @@ function getExportMenuItem( } } -function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, backTo?: string | undefined, isStartStep = false) { +function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, isStartStep = false) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; const previousStepRef = useRef(currentStep); @@ -415,17 +415,17 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard [CONST.COMPANY_CARD.STEP.CARD]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.getRoute(policyID, feed), [CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP.getRoute(policyID, feed), [CONST.COMPANY_CARD.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_NAME.getRoute(policyID, feed), - [CONST.COMPANY_CARD.STEP.CONFIRMATION]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed, shouldUseBackToParam ? backTo : ''), + [CONST.COMPANY_CARD.STEP.CONFIRMATION]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(policyID, feed), }; const targetRoute: AssignCardRoute = stepRoutes[currentStep]; if (targetRoute) { Navigation.navigate(targetRoute); } - }, [currentStep, policyID, feed, backTo, isStartStep, shouldUseBackToParam]); + }, [currentStep, policyID, feed, isStartStep, shouldUseBackToParam]); } -function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | undefined, isStartStep = false) { +function useAddNewCardNavigation(policyID: string | undefined, isStartStep = false) { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const currentStep = addNewCard?.currentStep; const previousStepRef = useRef(currentStep); @@ -444,29 +444,29 @@ function useAddNewCardNavigation(policyID: string | undefined, backTo?: string | } const stepRoutes: Record = { - [CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.SELECT_BANK]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.CARD_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.SELECT_BANK]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.CARD_TYPE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(policyID), [CONST.COMPANY_CARDS.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute( policyID, addNewCard?.data?.selectedBank ?? '', - ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID, backTo), + ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID), ), - [CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.CARD_DETAILS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.CARD_INSTRUCTIONS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.CARD_NAME]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.CARD_DETAILS]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(policyID), [CONST.COMPANY_CARDS.STEP.PLAID_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_PLAID_CONNECTION.getRoute(policyID, addNewCard?.data?.selectedBank ?? ''), - [CONST.COMPANY_CARDS.STEP.SELECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(policyID, backTo), - [CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(policyID, backTo), + [CONST.COMPANY_CARDS.STEP.SELECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(policyID), + [CONST.COMPANY_CARDS.STEP.SELECT_DIRECT_STATEMENT_CLOSE_DATE]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(policyID), }; const targetRoute: AddNewCardRoute = currentStep ? stepRoutes[currentStep] : stepRoutes[defaultStep]; if (targetRoute) { Navigation.navigate(targetRoute); } - }, [currentStep, policyID, backTo, isStartStep, addNewCard?.data?.selectedBank, defaultStep]); + }, [currentStep, policyID, isStartStep, addNewCard?.data?.selectedBank, defaultStep]); } // eslint-disable-next-line import/prefer-default-export diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 96c1b80aed3f..3d1fd570a8bd 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -150,7 +150,6 @@ describe('AssignCardFeedPage', () => { expect(navigationCall).toContain('company-cards'); expect(navigationCall).toContain('assign-card/confirmation'); expect(navigationCall).toContain(policy.id); - expect(navigationCall).toContain('backTo'); // Unmount the component after assertions to clean up. unmount(); diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts index 8b0059aee093..03600d297e96 100644 --- a/tests/unit/CompanyCardsNavigationTest.ts +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -22,7 +22,6 @@ jest.mock('@hooks/usePermissions', () => const POLICY_ID = 'test-policy-id'; const FEED = 'cdf'; -const BACK_TO = '/test/back/to'; describe('useAssignCardNavigation', () => { beforeAll(() => { @@ -42,7 +41,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Wait for effects to run await waitForBatchedUpdates(); @@ -56,7 +55,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Change the step await act(async () => { @@ -76,7 +75,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Change the step await act(async () => { @@ -96,7 +95,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Change the step await act(async () => { @@ -107,7 +106,7 @@ describe('useAssignCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(POLICY_ID, FEED, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION.getRoute(POLICY_ID, FEED)); }); it('should navigate to BANK_CONNECTION step', async () => { @@ -116,7 +115,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Change the step await act(async () => { @@ -138,7 +137,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, false)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, false)); // Change the step await act(async () => { @@ -158,7 +157,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, BACK_TO, true)); + renderHook(() => useAssignCardNavigation(POLICY_ID, FEED, true)); await waitForBatchedUpdates(); @@ -171,7 +170,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(undefined, FEED, BACK_TO, true)); + renderHook(() => useAssignCardNavigation(undefined, FEED, true)); await waitForBatchedUpdates(); @@ -184,7 +183,7 @@ describe('useAssignCardNavigation', () => { data: {email: 'test@example.com'}, }); - renderHook(() => useAssignCardNavigation(POLICY_ID, undefined, BACK_TO, true)); + renderHook(() => useAssignCardNavigation(POLICY_ID, undefined, true)); await waitForBatchedUpdates(); @@ -211,7 +210,7 @@ describe('useAddNewCardNavigation', () => { data: {selectedBank: 'Stripe'}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await waitForBatchedUpdates(); @@ -226,11 +225,11 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, true)); await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID)); }); it('should navigate to SELECT_COUNTRY step by default when PLAID beta is enabled', async () => { @@ -241,11 +240,11 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, true)); await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID)); }); it('should navigate to SELECT_COUNTRY step when step changes', async () => { @@ -254,7 +253,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -264,7 +263,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(POLICY_ID)); }); it('should navigate to SELECT_BANK step when step changes', async () => { @@ -273,7 +272,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -283,7 +282,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID)); }); it('should navigate to SELECT_FEED_TYPE step', async () => { @@ -292,7 +291,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -302,7 +301,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_FEED_TYPE.getRoute(POLICY_ID)); }); it('should navigate to CARD_TYPE step', async () => { @@ -311,7 +310,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -321,7 +320,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_TYPE.getRoute(POLICY_ID)); }); it('should navigate to BANK_CONNECTION step with selected bank', async () => { @@ -331,7 +330,7 @@ describe('useAddNewCardNavigation', () => { data: {selectedBank}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -342,7 +341,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); expect(Navigation.navigate).toHaveBeenCalledWith( - ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(POLICY_ID, selectedBank, ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(POLICY_ID, BACK_TO)), + ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(POLICY_ID, selectedBank, ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(POLICY_ID)), ); }); @@ -352,7 +351,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -362,7 +361,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_INSTRUCTIONS.getRoute(POLICY_ID)); }); it('should navigate to CARD_NAME step', async () => { @@ -371,7 +370,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -381,7 +380,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_NAME.getRoute(POLICY_ID)); }); it('should navigate to CARD_DETAILS step', async () => { @@ -390,7 +389,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -400,7 +399,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_CARD_DETAILS.getRoute(POLICY_ID)); }); it('should navigate to AMEX_CUSTOM_FEED step', async () => { @@ -409,7 +408,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -419,7 +418,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_AMEX_CUSTOM_FEED.getRoute(POLICY_ID)); }); it('should navigate to PLAID_CONNECTION step with selected bank', async () => { @@ -429,7 +428,7 @@ describe('useAddNewCardNavigation', () => { data: {selectedBank}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -448,7 +447,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -458,7 +457,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID)); }); it('should navigate to SELECT_DIRECT_STATEMENT_CLOSE_DATE step', async () => { @@ -467,7 +466,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, false)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, false)); await act(async () => { await Onyx.merge(ONYXKEYS.ADD_NEW_COMPANY_CARD, { @@ -477,7 +476,7 @@ describe('useAddNewCardNavigation', () => { await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_DIRECT_STATEMENT_CLOSE_DATE.getRoute(POLICY_ID)); }); it('should navigate immediately when isStartStep is true', async () => { @@ -488,11 +487,11 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(POLICY_ID, BACK_TO, true)); + renderHook(() => useAddNewCardNavigation(POLICY_ID, true)); await waitForBatchedUpdates(); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID, BACK_TO)); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(POLICY_ID)); }); it('should not navigate when policyID is undefined', async () => { @@ -501,7 +500,7 @@ describe('useAddNewCardNavigation', () => { data: {}, }); - renderHook(() => useAddNewCardNavigation(undefined, BACK_TO, true)); + renderHook(() => useAddNewCardNavigation(undefined, true)); await waitForBatchedUpdates(); From 614416a7e8f76fa7d1b3be8bb0f93455a8d4f942 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:47:23 +0000 Subject: [PATCH 14/24] eslint --- .../workspace/companyCards/addNew/PlaidConnectionStep.tsx | 2 +- src/pages/workspace/companyCards/addNew/SelectBankStep.tsx | 2 +- src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx | 4 ++-- .../workspace/companyCards/assignCard/AssignCardFeedPage.tsx | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index dafa657cd261..f61660b474f8 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -107,7 +107,7 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { previousNetworkState.current = isOffline; }, [addNewCard?.data?.selectedCountry, domain, feed, isAuthenticatedWithPlaid, isOffline]); - const handleBackButtonPress = (showingExitModal: boolean = false) => { + const handleBackButtonPress = (showingExitModal = false) => { if (feed && !showingExitModal) { Navigation.dismissModal(); return; diff --git a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx index ca15909ab5cd..2244e1012739 100644 --- a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx @@ -34,7 +34,7 @@ function SelectBankStep({route}: SelectBankStepProps) { const {isBetaEnabled} = usePermissions(); const policyID = route.params?.policyID; - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const [bankSelected, setBankSelected] = useState | null>(); diff --git a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx index 63c7bc8a486a..3faafeca7873 100644 --- a/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectCountryStep.tsx @@ -35,7 +35,7 @@ function SelectCountryStep({route}: CountryStepProps) { const policyID = route.params?.policyID; const policy = usePolicy(policyID); - useAddNewCardNavigation(policyID, route.params?.backTo); + useAddNewCardNavigation(policyID); const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); @@ -77,7 +77,7 @@ function SelectCountryStep({route}: CountryStepProps) { }, [getCountry]); const handleBackButtonPress = () => { - Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID)); + Navigation.dismissModal(); }; const onSelectionChange = useCallback((country: Option) => { diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 92924526a51d..1090094cfd3e 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -19,7 +19,6 @@ type AssignCardFeedPageProps = PlatformStackScreenProps Date: Fri, 14 Nov 2025 03:25:10 +0000 Subject: [PATCH 15/24] fix exitmodal disapearing on small screen --- .../workspace/companyCards/addNew/PlaidConnectionStep.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index f61660b474f8..ad9e8017b867 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -108,7 +108,11 @@ function PlaidConnectionStep({route}: PlaidConnectionStepProps) { }, [addNewCard?.data?.selectedCountry, domain, feed, isAuthenticatedWithPlaid, isOffline]); const handleBackButtonPress = (showingExitModal = false) => { - if (feed && !showingExitModal) { + if (showingExitModal) { + return; + } + + if (feed) { Navigation.dismissModal(); return; } From e6cc9fdcc758f5a71e0470a440c8b74f2e8b957f Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 17 Nov 2025 07:26:14 +0000 Subject: [PATCH 16/24] resolve conflicts --- src/libs/Navigation/types.ts | 10 +++++----- src/pages/workspace/companyCards/utils.tsx | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 44fcc1e93dac..d915e6e60ab4 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -24,7 +24,7 @@ import type NAVIGATORS from '@src/NAVIGATORS'; import type {Route as ExpensifyRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type EXIT_SURVEY_REASON_FORM_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import type {ConnectionName, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type {CustomFieldType} from '@src/types/onyx/PolicyEmployee'; import type {FileObject} from '@src/types/utils/Attachment'; @@ -1175,7 +1175,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: { policyID: string; - feed: CompanyCardFeed; + feed: CompanyCardFeedWithDomainID; // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; @@ -1185,7 +1185,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { policyID: string; - feed: CompanyCardFeed; + feed: CompanyCardFeedWithDomainID; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: { policyID: string; @@ -1197,11 +1197,11 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_CONFIRMATION]: { policyID: string; - feed: CompanyCardFeed; + feed: CompanyCardFeedWithDomainID; }; [SCREENS.WORKSPACE.COMPANY_CARDS_PLAID_CONNECTION]: { policyID: string; - feed: CompanyCardFeed; + feed: CompanyCardFeedWithDomainID; }; [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: { policyID: string; diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index a8c8e70eaabe..969918720b53 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -10,7 +10,7 @@ import {findSelectedBankAccountWithDefaultSelect, findSelectedVendorWithDefaultS import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Card, CompanyCardFeed, Policy} from '@src/types/onyx'; +import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, Policy} from '@src/types/onyx'; import type {Account, PolicyConnectionName} from '@src/types/onyx/Policy'; type AssignCardRoute = @@ -386,7 +386,7 @@ function getExportMenuItem( } } -function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | undefined, isStartStep = false) { +function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCardFeed | CompanyCardFeedWithDomainID | undefined, isStartStep = false) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; const previousStepRef = useRef(currentStep); From 83822de92d28324dc86dabc237ae409a9a3a36e1 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:29:23 +0000 Subject: [PATCH 17/24] fix test --- .../companyCards/assignCard/AssignCardFeedPage.tsx | 9 --------- tests/ui/AssignCardFeedPage.tsx | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index d760ae8e9f56..093ab26e7540 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -14,20 +14,11 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; -import AssigneeStep from './AssigneeStep'; -import CardNameStep from './CardNameStep'; -import CardSelectionStep from './CardSelectionStep'; -import ConfirmationStep from './ConfirmationStep'; -import TransactionStartDateStep from './TransactionStartDateStep'; type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { - const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); - const currentStep = assignCard?.currentStep; - const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeedWithDomainID; - const backTo = route.params?.backTo; const policyID = policy?.id; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true}); diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 3d1fd570a8bd..2c4e688f4104 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -130,7 +130,7 @@ describe('AssignCardFeedPage', () => { // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, - feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, + feed: `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}#1234`, backTo: ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy.id, 1234), }); @@ -186,7 +186,7 @@ describe('AssignCardFeedPage', () => { // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, - feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, + feed: `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}#1234`, }); // Verify the page renders From a72c508ce9ad4d3d12c42c828a621cb26aad92bf Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Mon, 17 Nov 2025 17:16:50 +0000 Subject: [PATCH 18/24] use useMemoizedLazyExpensifyIcons - eslint --- .../Icon/chunks/illustrations.chunk.ts | 8 ++++++++ .../companyCards/addNew/CardTypeStep.tsx | 18 ++++++++++++------ .../companyCards/addNew/DetailsStep.tsx | 5 +++-- .../companyCards/assignCard/AssigneeStep.tsx | 7 ++++--- .../assignCard/CardSelectionStep.tsx | 5 +++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/components/Icon/chunks/illustrations.chunk.ts b/src/components/Icon/chunks/illustrations.chunk.ts index 866363886244..cd7bfeed4bee 100644 --- a/src/components/Icon/chunks/illustrations.chunk.ts +++ b/src/components/Icon/chunks/illustrations.chunk.ts @@ -1,6 +1,9 @@ // This file contains all the SVG imports for illustrations used in the app // Company Cards import type {SvgProps} from 'react-native-svg'; +import AmexCardCompanyCardDetail from '@assets/images/companyCards/card-amex.svg'; +import MasterCardCompanyCardDetail from '@assets/images/companyCards/card-mastercard.svg'; +import VisaCompanyCardDetail from '@assets/images/companyCards/card-visa.svg'; import CompanyCardsEmptyState from '@assets/images/companyCards/emptystate__card-pos.svg'; // Other assets import Computer from '@assets/images/computer.svg'; @@ -10,6 +13,7 @@ import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustra import LaptopWithSecondScreenSync from '@assets/images/laptop-with-second-screen-sync.svg'; import LaptopWithSecondScreenX from '@assets/images/laptop-with-second-screen-x.svg'; // Product Illustrations +import BrokenMagnifyingGlass from '@assets/images/product-illustrations/broken-magnifying-glass.svg'; import TeleScope from '@assets/images/product-illustrations/telescope.svg'; // Simple Illustrations - Core ones that are actually used import Accounting from '@assets/images/simple-illustrations/simple-illustration__accounting.svg'; @@ -37,6 +41,9 @@ import Workflows from '@assets/images/simple-illustrations/simple-illustration__ // Create the illustrations object with all imported illustrations const Illustrations = { // Company Cards + AmexCardCompanyCardDetail, + MasterCardCompanyCardDetail, + VisaCompanyCardDetail, CompanyCardsEmptyState, // Other assets @@ -49,6 +56,7 @@ const Illustrations = { ExpensifyCardIllustration, // Product Illustrations + BrokenMagnifyingGlass, TeleScope, Telescope: TeleScope, // Alias for consistency diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index 22528c532286..2d19cd187ec0 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -4,12 +4,12 @@ import type {StyleProp, ViewStyle} from 'react-native'; import FormHelpMessage from '@components/FormHelpMessage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; -import * as Illustrations from '@components/Icon/Illustrations'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionListWithSections'; import RadioListItem from '@components/SelectionListWithSections/RadioListItem'; import Text from '@components/Text'; +import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; @@ -24,6 +24,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CardFeedProvider} from '@src/types/onyx/CardFeeds'; +import type IconAsset from '@src/types/utils/IconAsset'; type AvailableCompanyCardTypes = { translate: LocaleContextProps['translate']; @@ -32,7 +33,11 @@ type AvailableCompanyCardTypes = { canUsePlaidCompanyCards?: boolean; }; -function getAvailableCompanyCardTypes({translate, typeSelected, styles, canUsePlaidCompanyCards}: AvailableCompanyCardTypes) { +type GetAvailableCompanyCardTypesParams = AvailableCompanyCardTypes & { + illustrations: Record<'MasterCardCompanyCardDetail' | 'VisaCompanyCardDetail' | 'AmexCardCompanyCardDetail', IconAsset>; +}; + +function getAvailableCompanyCardTypes({translate, typeSelected, styles, canUsePlaidCompanyCards, illustrations}: GetAvailableCompanyCardTypesParams) { const defaultCards = [ { value: CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, @@ -41,7 +46,7 @@ function getAvailableCompanyCardTypes({translate, typeSelected, styles, canUsePl isSelected: typeSelected === CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, leftElement: ( (); const [isError, setIsError] = useState(false); @@ -99,7 +105,7 @@ function CardTypeStep({route}: CardTypeStepProps) { const policyID = route.params?.policyID; useAddNewCardNavigation(policyID); - const data = getAvailableCompanyCardTypes({translate, typeSelected, styles: styles.mr3, canUsePlaidCompanyCards: isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS)}); + const data = getAvailableCompanyCardTypes({translate, typeSelected, styles: styles.mr3, canUsePlaidCompanyCards: isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS), illustrations}); const {bankName, selectedBank, feedType} = addNewCard?.data ?? {}; const isOtherBankSelected = selectedBank === CONST.COMPANY_CARDS.BANKS.OTHER; const isNewCardTypeSelected = typeSelected !== feedType; diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index ddd54f5e6715..1a67f79537b5 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -5,12 +5,12 @@ import InputWrapper from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import TextLink from '@components/TextLink'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useTheme from '@hooks/useTheme'; @@ -32,6 +32,7 @@ function DetailsStep({route}: DetailsStepProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); + const icons = useMemoizedLazyExpensifyIcons(['QuestionMark'] as const); const {inputCallbackRef} = useAutoFocusInput(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: false}); @@ -215,7 +216,7 @@ function DetailsStep({route}: DetailsStepProps) { {!!feedProvider && !isStripeFeedProvider && ( { if (!debouncedSearchTerm) { diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 4e49830680c9..71d0ae4c3a96 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -2,7 +2,6 @@ import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import Icon from '@components/Icon'; -import {BrokenMagnifyingGlass} from '@components/Icon/Illustrations'; import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import PlaidCardFeedIcon from '@components/PlaidCardFeedIcon'; @@ -13,6 +12,7 @@ import Text from '@components/Text'; import useBottomSafeSafeAreaPaddingStyle from '@hooks/useBottomSafeSafeAreaPaddingStyle'; import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; +import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; @@ -37,6 +37,7 @@ function CardSelectionStep({route}: CardSelectionStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const illustrations = useThemeIllustrations(); + const lazyIllustrations = useMemoizedLazyIllustrations(['BrokenMagnifyingGlass'] as const); const [searchText, setSearchText] = useState(''); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeedWithDomainID; @@ -131,7 +132,7 @@ function CardSelectionStep({route}: CardSelectionStepProps) { {!cardListOptions.length ? ( From 6145dd43f7dd981fcd5329af25493821736aa8a9 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:32:53 +0000 Subject: [PATCH 19/24] align with main --- .../Icon/chunks/illustrations.chunk.ts | 141 +++++++++++++- .../companyCards/addNew/AddNewCardPage.tsx | 14 ++ .../companyCards/assignCard/AssigneeStep.tsx | 183 ++++++++++++------ 3 files changed, 280 insertions(+), 58 deletions(-) diff --git a/src/components/Icon/chunks/illustrations.chunk.ts b/src/components/Icon/chunks/illustrations.chunk.ts index 5095e20900c7..3396d1cdb28c 100644 --- a/src/components/Icon/chunks/illustrations.chunk.ts +++ b/src/components/Icon/chunks/illustrations.chunk.ts @@ -5,39 +5,106 @@ import AmexCardCompanyCardDetail from '@assets/images/companyCards/card-amex.svg import MasterCardCompanyCardDetail from '@assets/images/companyCards/card-mastercard.svg'; import VisaCompanyCardDetail from '@assets/images/companyCards/card-visa.svg'; import CompanyCardsEmptyState from '@assets/images/companyCards/emptystate__card-pos.svg'; +import PendingBank from '@assets/images/companyCards/pending-bank.svg'; +import CompanyCardsPendingState from '@assets/images/companyCards/pendingstate_laptop-with-hourglass-and-cards.svg'; // Other assets import Computer from '@assets/images/computer.svg'; +// Educational Illustrations +import MultiScan from '@assets/images/educational-illustration__multi-scan.svg'; +// Expensify Casudo apt install antigravityrd +import EmptyCardState from '@assets/images/emptystate__expensifycard.svg'; import ExpensifyCardImage from '@assets/images/expensify-card.svg'; -// Expensify Card import ExpensifyCardIllustration from '@assets/images/expensifyCard/cardIllustration.svg'; +// Other Images +import Hand from '@assets/images/hand.svg'; +import LaptopWithSecondScreenAndHourglass from '@assets/images/laptop-with-second-screen-and-hourglass.svg'; import LaptopWithSecondScreenSync from '@assets/images/laptop-with-second-screen-sync.svg'; import LaptopWithSecondScreenX from '@assets/images/laptop-with-second-screen-x.svg'; // Product Illustrations +import Abracadabra from '@assets/images/product-illustrations/abracadabra.svg'; +import BrokenCompanyCardBankConnection from '@assets/images/product-illustrations/broken-humpty-dumpty.svg'; import BrokenMagnifyingGlass from '@assets/images/product-illustrations/broken-magnifying-glass.svg'; +import EmptyStateExpenses from '@assets/images/product-illustrations/emptystate__expenses.svg'; +import HoldExpense from '@assets/images/product-illustrations/emptystate__holdexpense.svg'; +import ReceiptFairy from '@assets/images/product-illustrations/emptystate__receiptfairy.svg'; +import FolderWithPapers from '@assets/images/product-illustrations/folder-with-papers.svg'; +import Hands from '@assets/images/product-illustrations/home-illustration-hands.svg'; +import CardReplacementSuccess from '@assets/images/product-illustrations/illustration__card-replacement-success.svg'; +import MagicCode from '@assets/images/product-illustrations/magic-code.svg'; +import ModalHoldOrReject from '@assets/images/product-illustrations/modal-hold-or-reject.svg'; +import MushroomTopHat from '@assets/images/product-illustrations/mushroom-top-hat.svg'; +import PaymentHands from '@assets/images/product-illustrations/payment-hands.svg'; +import ReceiptsStackedOnPin from '@assets/images/product-illustrations/receipts-stacked-on-pin.svg'; +import RocketBlue from '@assets/images/product-illustrations/rocket--blue.svg'; +import RocketDude from '@assets/images/product-illustrations/rocket-dude.svg'; +import SewerDino from '@assets/images/product-illustrations/sewer_dino.svg'; +import SmartScan from '@assets/images/product-illustrations/simple-illustration__smartscan.svg'; import TeleScope from '@assets/images/product-illustrations/telescope.svg'; +import ThreeLeggedLaptopWoman from '@assets/images/product-illustrations/three_legged_laptop_woman.svg'; import ToddBehindCloud from '@assets/images/product-illustrations/todd-behind-cloud.svg'; -// Simple Illustrations - Core ones that are actually used +import ToddInCar from '@assets/images/product-illustrations/todd-in-car.svg'; +import ToddWithPhones from '@assets/images/product-illustrations/todd-with-phones.svg'; +import ReceiptUpload from '@assets/images/receipt-upload.svg'; +import RunningTurtle from '@assets/images/running-turtle.svg'; +import Shutter from '@assets/images/shutter.svg'; +// Simple Illustrations - Bucket 3 (Common) +import Puzzle from '@assets/images/simple-illustrations/emptystate__puzzlepieces.svg'; +import Abacus from '@assets/images/simple-illustrations/simple-illustration__abacus.svg'; +// Simple Illustrations - Original core ones import Accounting from '@assets/images/simple-illustrations/simple-illustration__accounting.svg'; +import Alert from '@assets/images/simple-illustrations/simple-illustration__alert.svg'; +import Approval from '@assets/images/simple-illustrations/simple-illustration__approval.svg'; +import Binoculars from '@assets/images/simple-illustrations/simple-illustration__binoculars.svg'; import BlueShield from '@assets/images/simple-illustrations/simple-illustration__blueshield.svg'; import Building from '@assets/images/simple-illustrations/simple-illustration__building.svg'; +import Buildings from '@assets/images/simple-illustrations/simple-illustration__buildings.svg'; import CarIce from '@assets/images/simple-illustrations/simple-illustration__car-ice.svg'; +import Car from '@assets/images/simple-illustrations/simple-illustration__car.svg'; +import PinkCar from '@assets/images/simple-illustrations/simple-illustration__car_pink.svg'; +import ChatBubbles from '@assets/images/simple-illustrations/simple-illustration__chatbubbles.svg'; +import CheckmarkCircle from '@assets/images/simple-illustrations/simple-illustration__checkmarkcircle.svg'; import Coins from '@assets/images/simple-illustrations/simple-illustration__coins.svg'; +import CommentBubbles from '@assets/images/simple-illustrations/simple-illustration__commentbubbles.svg'; +import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustration__concierge-bubble.svg'; import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; +import CreditCardEyes from '@assets/images/simple-illustrations/simple-illustration__creditcardeyes.svg'; +import CreditCardsNewGreen from '@assets/images/simple-illustrations/simple-illustration__creditcards--green.svg'; +import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg'; +import EmptyShelves from '@assets/images/simple-illustrations/simple-illustration__empty-shelves.svg'; +import Encryption from '@assets/images/simple-illustrations/simple-illustration__encryption.svg'; +import EnvelopeReceipt from '@assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg'; +import Filters from '@assets/images/simple-illustrations/simple-illustration__filters.svg'; +import Flash from '@assets/images/simple-illustrations/simple-illustration__flash.svg'; import FolderOpen from '@assets/images/simple-illustrations/simple-illustration__folder-open.svg'; +import Gears from '@assets/images/simple-illustrations/simple-illustration__gears.svg'; import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg'; +import HeadSet from '@assets/images/simple-illustrations/simple-illustration__headset.svg'; +import Hourglass from '@assets/images/simple-illustrations/simple-illustration__hourglass.svg'; +import House from '@assets/images/simple-illustrations/simple-illustration__house.svg'; import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg'; +import Lightbulb from '@assets/images/simple-illustrations/simple-illustration__lightbulb.svg'; +import LockClosed from '@assets/images/simple-illustrations/simple-illustration__lockclosed.svg'; +import LockClosedOrange from '@assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg'; +import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg'; import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg'; import MagnifyingGlassMoney from '@assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg'; +import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg'; import MoneyReceipts from '@assets/images/simple-illustrations/simple-illustration__money-receipts.svg'; import MoneyWings from '@assets/images/simple-illustrations/simple-illustration__moneywings.svg'; import Pencil from '@assets/images/simple-illustrations/simple-illustration__pencil.svg'; import PerDiem from '@assets/images/simple-illustrations/simple-illustration__perdiem.svg'; +import RealtimeReport from '@assets/images/simple-illustrations/simple-illustration__realtimereports.svg'; import ReceiptWrangler from '@assets/images/simple-illustrations/simple-illustration__receipt-wrangler.svg'; import ReportReceipt from '@assets/images/simple-illustrations/simple-illustration__report-receipt.svg'; import Rules from '@assets/images/simple-illustrations/simple-illustration__rules.svg'; +import ShieldYellow from '@assets/images/simple-illustrations/simple-illustration__shield.svg'; +import Stopwatch from '@assets/images/simple-illustrations/simple-illustration__stopwatch.svg'; import Tag from '@assets/images/simple-illustrations/simple-illustration__tag.svg'; +import ThumbsDown from '@assets/images/simple-illustrations/simple-illustration__thumbsdown.svg'; import CompanyCard from '@assets/images/simple-illustrations/simple-illustration__twocards-horizontal.svg'; import Workflows from '@assets/images/simple-illustrations/simple-illustration__workflows.svg'; +import ExpensifyApprovedLogo from '@assets/images/subscription-details__approvedlogo.svg'; +import TurtleInShell from '@assets/images/turtle-in-shell.svg'; // Create the illustrations object with all imported illustrations const Illustrations = { @@ -46,10 +113,14 @@ const Illustrations = { MasterCardCompanyCardDetail, VisaCompanyCardDetail, CompanyCardsEmptyState, + CompanyCardsPendingState, + PendingBank, // Other assets Computer, + EmptyCardState, ExpensifyCardImage, + LaptopWithSecondScreenAndHourglass, LaptopWithSecondScreenSync, LaptopWithSecondScreenX, @@ -57,10 +128,41 @@ const Illustrations = { ExpensifyCardIllustration, // Product Illustrations + Abracadabra, + BrokenCompanyCardBankConnection, BrokenMagnifyingGlass, + EmptyStateExpenses, + HoldExpense, + ReceiptFairy, + FolderWithPapers, + Hands, + CardReplacementSuccess, + MagicCode, + ModalHoldOrReject, + MushroomTopHat, + PaymentHands, + ReceiptsStackedOnPin, + RocketBlue, + RocketDude, + SewerDino, + SmartScan, TeleScope, Telescope: TeleScope, // Alias for consistency + ThreeLeggedLaptopWoman, ToddBehindCloud, + ToddInCar, + ToddWithPhones, + + // Educational Illustrations + MultiScan, + + // Other Images + Hand, + ReceiptUpload, + RunningTurtle, + Shutter, + ExpensifyApprovedLogo, + TurtleInShell, // Simple Illustrations Accounting, @@ -74,18 +176,49 @@ const Illustrations = { MoneyReceipts, MoneyWings, PerDiem, + RealtimeReport, ReceiptWrangler, ReportReceipt, Rules, + Stopwatch, Tag, CompanyCard, + ThumbsDown, Workflows, CarIce, BlueShield, Pencil, Luggage, - // Legacy aliases for compatibility - Car: CompanyCard, // Fallback for Car illustration requests + Puzzle, + Abacus, + Alert, + Approval, + Binoculars, + Buildings, + Car, + PinkCar, + ChatBubbles, + CheckmarkCircle, + CommentBubbles, + ConciergeBubble, + CreditCardEyes, + CreditCardsNewGreen, + EmailAddress, + EmptyShelves, + Encryption, + EnvelopeReceipt, + Filters, + Flash, + Gears, + HeadSet, + Hourglass, + House, + Lightbulb, + LockClosed, + LockClosedOrange, + LockOpen, + Mailbox, + ShieldYellow, }; /** diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index f4eda3178ec6..8da2dd274e62 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -3,25 +3,39 @@ import React, {useEffect} from 'react'; import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; +import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; +import Navigation from '@navigation/Navigation'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAddNewCardFlow, clearAssignCardStepAndData, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import ROUTES from '@src/ROUTES'; type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; function AddNewCardPage({policy}: AddNewCardPageProps) { const policyID = policy?.id; const workspaceAccountID = useWorkspaceAccountID(policyID); + const {isBlockedToAddNewFeeds, isAllFeedsResultLoading} = useIsBlockedToAddFeed(policyID); + const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: false}); + useEffect(() => { + if (!policyID || !isBlockedToAddNewFeeds) { + return; + } + Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.companyCards.alias, ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)), { + forceReplace: true, + }); + }, [isBlockedToAddNewFeeds, policyID]); + useEffect(() => { return () => { clearAddNewCardFlow(); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 4e5236d30893..be60181ebb09 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -1,6 +1,8 @@ -import React, {useMemo, useState} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {Keyboard} from 'react-native'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import SelectionList from '@components/SelectionListWithSections'; import type {ListItem} from '@components/SelectionListWithSections/types'; @@ -13,13 +15,16 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import useSearchSelector from '@hooks/useSearchSelector'; import useThemeStyles from '@hooks/useThemeStyles'; +import {setDraftInviteAccountID} from '@libs/actions/Card'; +import {searchInServer} from '@libs/actions/Report'; import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; -import {isDeletedPolicyEmployee} from '@libs/PolicyUtils'; +import {getIneligibleInvitees, isDeletedPolicyEmployee} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import Navigation from '@navigation/Navigation'; import {useAssignCardNavigation} from '@pages/workspace/companyCards/utils'; @@ -27,6 +32,7 @@ import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; @@ -48,24 +54,44 @@ function AssigneeStep({route}: AssigneeStepProps) { const [list] = useCardsList(policy?.id, feed); const [cardFeeds] = useCardFeeds(policy?.id); const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); + const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); + const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); - const isEditing = assignCard?.isEditing; + const excludedUsers = useMemo(() => { + const ineligibleInvites = getIneligibleInvitees(policy?.employeeList); + return ineligibleInvites.reduce( + (acc, login) => { + acc[login] = true; + return acc; + }, + {} as Record, + ); + }, [policy?.employeeList]); - const [selectedMember, setSelectedMember] = useState(assignCard?.data?.email ?? ''); - const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); - const [shouldShowError, setShouldShowError] = useState(false); + const {searchTerm, setSearchTerm, debouncedSearchTerm, availableOptions, selectedOptionsForDisplay, areOptionsInitialized} = useSearchSelector({ + selectionMode: CONST.SEARCH_SELECTOR.SELECTION_MODE_MULTI, + searchContext: CONST.SEARCH_SELECTOR.SEARCH_CONTEXT_MEMBER_INVITE, + includeUserToInvite: true, + excludeLogins: excludedUsers, + includeRecentReports: true, + shouldInitialize: didScreenTransitionEnd, + }); - const selectMember = (assignee: ListItem) => { - Keyboard.dismiss(); - setSelectedMember(assignee.login ?? ''); - setShouldShowError(false); - }; + const isEditing = assignCard?.isEditing; useAssignCardNavigation(policyID, feed); - - const submit = () => { + + const submit = (assignee: ListItem) => { let nextStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; - if (selectedMember === assignCard?.data?.email) { + const personalDetail = getPersonalDetailByEmail(assignee?.login ?? ''); + const memberName = personalDetail?.firstName ? personalDetail.firstName : personalDetail?.login; + const data: Partial = { + email: assignee?.login ?? '', + cardName: getDefaultCardName(memberName), + }; + + Keyboard.dismiss(); + if (assignee?.login === assignCard?.data?.email) { setAssignCardStepAndData({ currentStep: isEditing ? CONST.COMPANY_CARD.STEP.CONFIRMATION : nextStep, isEditing: false, @@ -73,18 +99,18 @@ function AssigneeStep({route}: AssigneeStepProps) { return; } - if (!selectedMember) { - setShouldShowError(true); + if (!policy?.employeeList?.[assignee?.login ?? '']) { + setAssignCardStepAndData({ + currentStep: CONST.COMPANY_CARD.STEP.INVITE_NEW_MEMBER, + data: { + invitingMemberEmail: assignee?.login ?? '', + invitingMemberAccountID: assignee?.accountID ?? undefined, + }, + }); + setDraftInviteAccountID(assignee?.login ?? '', assignee?.accountID ?? undefined, policyID); return; } - const personalDetail = getPersonalDetailByEmail(selectedMember); - const memberName = personalDetail?.firstName ? personalDetail.firstName : personalDetail?.login; - const data: Partial = { - email: selectedMember, - cardName: getDefaultCardName(memberName), - }; - if (hasOnlyOneCardToAssign(filteredCardList)) { nextStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE; data.cardNumber = Object.keys(filteredCardList).at(0); @@ -109,9 +135,6 @@ function AssigneeStep({route}: AssigneeStepProps) { Navigation.dismissModal(); }; - const shouldShowSearchInput = policy?.employeeList && Object.keys(policy.employeeList).length >= MINIMUM_MEMBER_TO_SHOW_SEARCH; - const textInputLabel = shouldShowSearchInput ? translate('workspace.card.issueNewCard.findMember') : undefined; - const membersDetails = useMemo(() => { let membersList: ListItem[] = []; if (!policy?.employeeList) { @@ -130,7 +153,7 @@ function AssigneeStep({route}: AssigneeStepProps) { alternateText: email, login: email, accountID: personalDetail?.accountID, - isSelected: selectedMember === email, + isSelected: assignCard?.data?.email === email, icons: [ { source: personalDetail?.avatar ?? icons.FallbackAvatar, @@ -145,7 +168,7 @@ function AssigneeStep({route}: AssigneeStepProps) { membersList = sortAlphabetically(membersList, 'text', localeCompare); return membersList; - }, [isOffline, policy?.employeeList, selectedMember, formatPhoneNumber, localeCompare, icons.FallbackAvatar]); + }, [isOffline, policy?.employeeList, assignCard?.data?.email, formatPhoneNumber, localeCompare]); const sections = useMemo(() => { if (!debouncedSearchTerm) { @@ -157,23 +180,82 @@ function AssigneeStep({route}: AssigneeStepProps) { ]; } - const searchValue = getSearchValueForPhoneOrEmail(debouncedSearchTerm, countryCode).toLowerCase(); - const filteredOptions = tokenizedSearch(membersDetails, searchValue, (option) => [option.text ?? '', option.alternateText ?? '']); + const sectionsArr = []; + + if (!areOptionsInitialized) { + return []; + } - return [ - { + const searchValueForOptions = getSearchValueForPhoneOrEmail(debouncedSearchTerm, countryCode).toLowerCase(); + const filteredOptions = tokenizedSearch(membersDetails, searchValueForOptions, (option) => [option.text ?? '', option.alternateText ?? '']); + + sectionsArr.push({ + title: undefined, + data: filteredOptions, + shouldShow: true, + }); + + // Selected options section + if (selectedOptionsForDisplay.length > 0) { + sectionsArr.push({ title: undefined, - data: filteredOptions, - shouldShow: true, - }, - ]; - }, [membersDetails, debouncedSearchTerm, countryCode]); + data: selectedOptionsForDisplay, + }); + } - const headerMessage = useMemo(() => { - const searchValue = debouncedSearchTerm.trim().toLowerCase(); + // Recent reports section + if (availableOptions.recentReports.length > 0) { + sectionsArr.push({ + title: undefined, + data: availableOptions.recentReports, + }); + } + + // Contacts section + if (availableOptions.personalDetails.length > 0) { + sectionsArr.push({ + title: undefined, + data: availableOptions.personalDetails, + }); + } - return getHeaderMessage(sections[0].data.length !== 0, false, searchValue, countryCode, false); - }, [debouncedSearchTerm, sections, countryCode]); + // User to invite section + if (availableOptions.userToInvite) { + sectionsArr.push({ + title: undefined, + data: [availableOptions.userToInvite], + }); + } + + return sectionsArr; + }, [ + debouncedSearchTerm, + areOptionsInitialized, + countryCode, + membersDetails, + selectedOptionsForDisplay, + availableOptions.recentReports, + availableOptions.personalDetails, + availableOptions.userToInvite, + ]); + + useEffect(() => { + searchInServer(searchTerm); + }, [searchTerm]); + + const headerMessage = useMemo(() => { + const searchValue = searchTerm.trim().toLowerCase(); + if (!availableOptions.userToInvite && CONST.EXPENSIFY_EMAILS_OBJECT[searchValue]) { + return translate('messages.errorMessageInvalidEmail'); + } + return getHeaderMessage( + sections.some((section) => section.data.length > 0), + !!availableOptions.userToInvite, + searchValue, + countryCode, + false, + ); + }, [searchTerm, availableOptions.userToInvite, sections, countryCode, translate]); return ( setDidScreenTransitionEnd(true)} > {translate('workspace.companyCards.whoNeedsCardAssigned')} - } + showLoadingPlaceholder={!areOptionsInitialized} + isLoadingNewOptions={!!isSearchingForReports} /> ); From b008e3b7fd0e17e119c06d05ebab24d4e70b463b Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:41:42 +0000 Subject: [PATCH 20/24] add inviteNewMemberStep & eslint --- src/ROUTES.ts | 4 ++++ src/SCREENS.ts | 1 + .../AppNavigator/ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 +++ src/libs/Navigation/types.ts | 4 ++++ .../workspace/companyCards/addNew/AddNewCardPage.tsx | 1 - .../workspace/companyCards/addNew/SelectBankStep.tsx | 1 - .../workspace/companyCards/assignCard/AssigneeStep.tsx | 10 ++-------- src/pages/workspace/companyCards/utils.tsx | 2 ++ tests/ui/AssignCardFeedPage.tsx | 4 ++-- 10 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 477c0fbff344..a0bccbb071b1 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2171,6 +2171,10 @@ const ROUTES = { route: 'workspaces/:policyID/company-cards/:feed/assign-card/assignee', getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/assignee` as const, }, + WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER: { + route: 'workspaces/:policyID/company-cards/:feed/assign-card/invite-new-member', + getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/invite-new-member` as const, + }, WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT: { route: 'workspaces/:policyID/company-cards/:feed/assign-card/select', getRoute: (policyID: string, feed: string) => `workspaces/${policyID}/company-cards/${feed}/assign-card/select` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2fb371df4f19..c00d26c603ce 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -571,6 +571,7 @@ const SCREENS = { COMPANY_CARDS_ASSIGN_CARD: 'Workspace_CompanyCards_AssignCard', COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE: 'Workspace_CompanyCards_AssignCard_Assignee', + COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER: 'Workspace_CompanyCards_AssignCard_InviteNewMember', COMPANY_CARDS_ASSIGN_CARD_SELECT: 'Workspace_CompanyCards_AssignCard_Select', COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP: 'Workspace_CompanyCards_AssignCard_TransactionStartDateStep', COMPANY_CARDS_ASSIGN_CARD_NAME: 'Workspace_CompanyCards_AssignCard_CardNameStep', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 928c2c3dbc5e..81aa928fe127 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -746,6 +746,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/companyCards/assignCard/AssignCardFeedPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: () => require('../../../../pages/workspace/companyCards/assignCard/AssigneeStep').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER]: () => require('../../../../pages/workspace/companyCards/assignCard/InviteNewMemberStep').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: () => require('../../../../pages/workspace/companyCards/assignCard/CardSelectionStep').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_TRANSACTION_START_DATE_STEP]: () => require('../../../../pages/workspace/companyCards/assignCard/TransactionStartDateStep').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index d60012944ede..06edd72157c8 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -786,6 +786,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.route, }, + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER.route, + }, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_SELECT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 7999b1cb399e..d33e3b08137d 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1180,6 +1180,10 @@ type SettingsNavigatorParamList = { policyID: string; feed: CompanyCardFeed; }; + [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER]: { + policyID: string; + feed: CompanyCardFeed; + }; [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD_SELECT]: { policyID: string; feed: CompanyCardFeed; diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index d834529949d9..0bf0aa96a529 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -4,7 +4,6 @@ import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; -import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; diff --git a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx index ed8e02f96222..ab4f56944b50 100644 --- a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx @@ -1,4 +1,3 @@ -import {useRoute} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 80f02ea8f09f..47865c36581e 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -1,8 +1,5 @@ import React, {useEffect, useMemo, useState} from 'react'; import {Keyboard} from 'react-native'; -import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; -import type {OnyxEntry} from 'react-native-onyx'; -import * as Expensicons from '@components/Icon/Expensicons'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import SelectionList from '@components/SelectionListWithSections'; import type {ListItem} from '@components/SelectionListWithSections/types'; @@ -10,7 +7,6 @@ import UserListItem from '@components/SelectionListWithSections/UserListItem'; import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; -import useDebouncedState from '@hooks/useDebouncedState'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -22,7 +18,6 @@ import {searchInServer} from '@libs/actions/Report'; import {getDefaultCardName, getFilteredCardList, hasOnlyOneCardToAssign} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import {getHeaderMessage, getSearchValueForPhoneOrEmail, sortAlphabetically} from '@libs/OptionsListUtils'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {getIneligibleInvitees, isDeletedPolicyEmployee} from '@libs/PolicyUtils'; @@ -33,9 +28,8 @@ import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; -import { CompanyCardFeed } from '@src/types/onyx'; +import type {CompanyCardFeed} from '@src/types/onyx'; type AssigneeStepProps = PlatformStackScreenProps; @@ -167,7 +161,7 @@ function AssigneeStep({route}: AssigneeStepProps) { membersList = sortAlphabetically(membersList, 'text', localeCompare); return membersList; - }, [isOffline, policy?.employeeList, assignCard?.data?.email, formatPhoneNumber, localeCompare]); + }, [isOffline, policy?.employeeList, assignCard?.data?.email, formatPhoneNumber, localeCompare, icons.FallbackAvatar]); const sections = useMemo(() => { if (!debouncedSearchTerm) { diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index a8c8e70eaabe..e84b66d663c0 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -15,6 +15,7 @@ import type {Account, PolicyConnectionName} from '@src/types/onyx/Policy'; type AssignCardRoute = | ReturnType + | ReturnType | ReturnType | ReturnType | ReturnType @@ -406,6 +407,7 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard const stepRoutes: Record = { [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed), + [CONST.COMPANY_CARD.STEP.INVITE_NEW_MEMBER]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER.getRoute(policyID, feed), [CONST.COMPANY_CARD.STEP.BANK_CONNECTION]: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute( policyID, feed, diff --git a/tests/ui/AssignCardFeedPage.tsx b/tests/ui/AssignCardFeedPage.tsx index 0053697995ac..cb20679a91b7 100644 --- a/tests/ui/AssignCardFeedPage.tsx +++ b/tests/ui/AssignCardFeedPage.tsx @@ -150,7 +150,7 @@ describe('AssignCardFeedPage', () => { // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, - feed: `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}#1234`, + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, backTo: ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policy.id, 1234), }); @@ -206,7 +206,7 @@ describe('AssignCardFeedPage', () => { // Render the page const {unmount} = renderPage(SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD, { policyID: policy.id, - feed: `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}#1234`, + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, }); // Verify the page renders From 50038a2d8354e5d05a3bdea37517485911f081e5 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:47:09 +0000 Subject: [PATCH 21/24] prettier --- .../companyCards/addNew/AddNewCardPage.tsx | 4 ++-- .../assignCard/AssignCardFeedPage.tsx | 1 - .../companyCards/assignCard/AssigneeStep.tsx | 2 +- src/pages/workspace/companyCards/utils.tsx | 16 ++++++++-------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx index 0bf0aa96a529..24c09cd7bdae 100644 --- a/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx +++ b/src/pages/workspace/companyCards/addNew/AddNewCardPage.tsx @@ -7,16 +7,16 @@ import useIsBlockedToAddFeed from '@hooks/useIsBlockedToAddFeed'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import Navigation from '@navigation/Navigation'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import {useAddNewCardNavigation} from '@pages/workspace/companyCards/utils'; -import Navigation from '@navigation/Navigation'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import {clearAddNewCardFlow, clearAssignCardStepAndData, openPolicyAddCardFeedPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; type AddNewCardPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 72c6a4223825..1090094cfd3e 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -15,7 +15,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; - type AssignCardFeedPageProps = PlatformStackScreenProps & WithPolicyAndFullscreenLoadingProps; function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 47865c36581e..31f5ce412fed 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -28,8 +28,8 @@ import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; import type {CompanyCardFeed} from '@src/types/onyx'; +import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; type AssigneeStepProps = PlatformStackScreenProps; diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index e84b66d663c0..d3cdf7d404e7 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -395,16 +395,16 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === assignCard?.data?.email; useEffect(() => { - if (currentStep === previousStepRef.current && !isStartStep) { + if (!policyID || !currentStep || !feed) { return; } - previousStepRef.current = currentStep; - - if (!policyID || !currentStep || !feed) { + if (currentStep === previousStepRef.current && !isStartStep) { return; } + previousStepRef.current = currentStep; + const stepRoutes: Record = { [CONST.COMPANY_CARD.STEP.ASSIGNEE]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute(policyID, feed), [CONST.COMPANY_CARD.STEP.INVITE_NEW_MEMBER]: ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_INVITE_NEW_MEMBER.getRoute(policyID, feed), @@ -435,16 +435,16 @@ function useAddNewCardNavigation(policyID: string | undefined, isStartStep = fal const defaultStep = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) ? CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY : CONST.COMPANY_CARDS.STEP.SELECT_BANK; useEffect(() => { - if (currentStep === previousStepRef.current && !isStartStep) { + if (!policyID) { return; } - previousStepRef.current = currentStep; - - if (!policyID) { + if (currentStep === previousStepRef.current && !isStartStep) { return; } + previousStepRef.current = currentStep; + const stepRoutes: Record = { [CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_COUNTRY.getRoute(policyID), [CONST.COMPANY_CARDS.STEP.SELECT_BANK]: ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW_SELECT_BANK.getRoute(policyID), From a9ed735eb8a6dad21d49e0adf719efd324cde140 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:23:16 +0000 Subject: [PATCH 22/24] prettier & test --- src/pages/workspace/companyCards/utils.tsx | 4 ++-- tests/unit/CompanyCardsNavigationTest.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/utils.tsx b/src/pages/workspace/companyCards/utils.tsx index 4821a3cf62d1..8fbecd7b5406 100644 --- a/src/pages/workspace/companyCards/utils.tsx +++ b/src/pages/workspace/companyCards/utils.tsx @@ -422,7 +422,7 @@ function useAssignCardNavigation(policyID: string | undefined, feed: CompanyCard const targetRoute: AssignCardRoute = stepRoutes[currentStep]; if (targetRoute) { - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(targetRoute)); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(targetRoute)); } }, [currentStep, policyID, feed, isStartStep, shouldUseBackToParam]); } @@ -466,7 +466,7 @@ function useAddNewCardNavigation(policyID: string | undefined, isStartStep = fal const targetRoute: AddNewCardRoute = currentStep ? stepRoutes[currentStep] : stepRoutes[defaultStep]; if (targetRoute) { - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(targetRoute)); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(targetRoute)); } }, [currentStep, policyID, isStartStep, addNewCard?.data?.selectedBank, defaultStep]); } diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts index 03600d297e96..af4ed80acbbe 100644 --- a/tests/unit/CompanyCardsNavigationTest.ts +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -10,6 +10,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // Mock Navigation jest.mock('@libs/Navigation/Navigation', () => ({ navigate: jest.fn(), + setNavigationActionToMicrotaskQueue: jest.fn((callback) => callback()), })); // Mock usePermissions hook From 8c32a1c019634cf6257cc852f0bd24d8f6254356 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Wed, 10 Dec 2025 01:04:34 +0000 Subject: [PATCH 23/24] lint --- tests/unit/CompanyCardsNavigationTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/CompanyCardsNavigationTest.ts b/tests/unit/CompanyCardsNavigationTest.ts index af4ed80acbbe..53cd8a795d5b 100644 --- a/tests/unit/CompanyCardsNavigationTest.ts +++ b/tests/unit/CompanyCardsNavigationTest.ts @@ -10,7 +10,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // Mock Navigation jest.mock('@libs/Navigation/Navigation', () => ({ navigate: jest.fn(), - setNavigationActionToMicrotaskQueue: jest.fn((callback) => callback()), + setNavigationActionToMicrotaskQueue: jest.fn((callback: () => void) => callback()), })); // Mock usePermissions hook From f857a93ffb1d29db3754f0d18f4a1c69ff165687 Mon Sep 17 00:00:00 2001 From: "I.K." <54219858+M00rish@users.noreply.github.com> Date: Tue, 16 Dec 2025 04:48:29 +0000 Subject: [PATCH 24/24] confirmation fix --- .../workspace/companyCards/assignCard/ConfirmationStep.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 8d45d98ece16..d5bd976824de 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -86,7 +86,7 @@ function ConfirmationStep({route}: ConfirmationStepProps) { setAssignCardStepAndData({currentStep: institutionId ? CONST.COMPANY_CARD.STEP.PLAID_CONNECTION : CONST.COMPANY_CARD.STEP.BANK_CONNECTION}); return; } - assignWorkspaceCompanyCard(policyID, {...data, bankName}); + assignWorkspaceCompanyCard(policy, {...data, bankName}); Navigation.dismissModal(); };