diff --git a/src/CONST/index.ts b/src/CONST/index.ts index beb88f58ea03..107eb5c08ade 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2617,13 +2617,23 @@ const CONST = { AUTO_CREATE_ENTITIES: 'autoCreateEntities', APPROVAL_ACCOUNT: 'approvalAccount', CUSTOM_FORM_ID_OPTIONS: 'customFormIDOptions', - TOKEN_INPUT_STEP_NAMES: ['1', '2', '3', '4', '5'], - TOKEN_INPUT_STEP_KEYS: { - 0: 'installBundle', - 1: 'enableTokenAuthentication', - 2: 'enableSoapServices', - 3: 'createAccessToken', - 4: 'enterCredentials', + TOKEN_INPUT: { + STEP_INDEX_LIST: ['1', '2', '3', '4'], + PAGE_NAME: { + INSTALL: 'install', + AUTHENTICATION: 'authentication', + SOAP: 'soap', + ACCESS_TOKEN: 'access-token', + CREDENTIALS: 'credentials', + }, + CREDENTIALS_PAGE_INDEX: 4, + STEP_KEYS: { + install: 'installBundle', + authentication: 'enableTokenAuthentication', + soap: 'enableSoapServices', + 'access-token': 'createAccessToken', + credentials: 'enterCredentials', + }, }, IMPORT_CUSTOM_FIELDS: { CUSTOM_SEGMENTS: 'customSegments', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index afa59492cd28..2fe8c8e8ce7c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3368,8 +3368,13 @@ const ROUTES = { getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/existing-connections` as const, }, POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT: { - route: 'workspaces/:policyID/accounting/netsuite/token-input', - getRoute: (policyID: string) => `workspaces/${policyID}/accounting/netsuite/token-input` as const, + route: 'workspaces/:policyID/accounting/netsuite/token-input/:subPage', + getRoute: (policyID: string | undefined, subPage: string) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT route'); + } + return `workspaces/${policyID}/accounting/netsuite/token-input/${subPage}` as const; + }, }, POLICY_ACCOUNTING_NETSUITE_IMPORT: { route: 'workspaces/:policyID/accounting/netsuite/import', diff --git a/src/components/ConnectToNetSuiteFlow/index.tsx b/src/components/ConnectToNetSuiteFlow/index.tsx index 3b681d7e39e5..0cd41c8b0fd8 100644 --- a/src/components/ConnectToNetSuiteFlow/index.tsx +++ b/src/components/ConnectToNetSuiteFlow/index.tsx @@ -12,6 +12,7 @@ import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import {getInitialSubPageForNetsuiteTokenInput} from '@pages/workspace/accounting/netsuite/utils'; import type {ConnectToNetSuiteFlowProps} from './types'; function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { @@ -37,7 +38,7 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { icon: Expensicons.LinkCopy, text: translate('workspace.common.createNewConnection'), onSelected: () => { - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID)); + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID, getInitialSubPageForNetsuiteTokenInput(policy))); setIsReuseConnectionsPopoverOpen(false); }, }, @@ -53,7 +54,7 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { useEffect(() => { if (shouldGoToCredentialsPage || !hasPoliciesConnectedToNetSuite) { - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID)); + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID, getInitialSubPageForNetsuiteTokenInput(policy))); return; } setIsReuseConnectionsPopoverOpen(true); diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenInputPage.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenInputPage.tsx index 484dbf3a19c1..d07b29ebea61 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenInputPage.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenInputPage.tsx @@ -1,28 +1,31 @@ -import React, {useRef} from 'react'; -import type {ComponentType, ForwardedRef} from 'react'; +import React from 'react'; import {View} from 'react-native'; import ConnectionLayout from '@components/ConnectionLayout'; -import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; -import type {InteractiveStepSubHeaderHandle} from '@components/InteractiveStepSubHeader'; -import useSubStep from '@hooks/useSubStep'; +import InteractiveStepSubPageHeader from '@components/InteractiveStepSubPageHeader'; +import useSubPage from '@hooks/useSubPage'; import useThemeStyles from '@hooks/useThemeStyles'; import {isAuthenticationError} from '@libs/actions/connections'; import Navigation from '@libs/Navigation/Navigation'; -import type {SubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; +import type {CustomSubPageTokenInputProps} from '@pages/workspace/accounting/netsuite/types'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import NetSuiteTokenInputForm from './substeps/NetSuiteTokenInputForm'; -import NetSuiteTokenSetupContent from './substeps/NetSuiteTokenSetupContent'; +import NetSuiteTokenInputForm from './subPages/NetSuiteTokenInputForm'; +import NetSuiteTokenSetupContent from './subPages/NetSuiteTokenSetupContent'; -const staticContentSteps = Array>(4).fill(NetSuiteTokenSetupContent); -const tokenInputSteps: Array> = [...staticContentSteps, NetSuiteTokenInputForm]; +const pages = [ + {pageName: CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.INSTALL, component: NetSuiteTokenSetupContent}, + {pageName: CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.AUTHENTICATION, component: NetSuiteTokenSetupContent}, + {pageName: CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.SOAP, component: NetSuiteTokenSetupContent}, + {pageName: CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.ACCESS_TOKEN, component: NetSuiteTokenSetupContent}, + {pageName: CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.CREDENTIALS, component: NetSuiteTokenInputForm}, +]; -function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) { +function NetSuiteTokenInputPage({policy, route}: WithPolicyConnectionsProps) { const policyID = policy?.id; const styles = useThemeStyles(); - const ref: ForwardedRef = useRef(null); const hasAuthError = isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE); @@ -30,27 +33,18 @@ function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) { Navigation.dismissModal(); }; - const { - componentToRender: SubStep, - isEditing, - nextScreen, - prevScreen, - screenIndex, - moveTo, - } = useSubStep({bodyContent: tokenInputSteps, startFrom: hasAuthError ? 4 : 0, onFinished: submit}); + const {CurrentPage, nextPage, prevPage, pageIndex, moveTo, currentPageName} = useSubPage({ + pages, + onFinished: submit, + buildRoute: (pageName) => ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(route.params.policyID, pageName), + }); const handleBackButtonPress = () => { - if (screenIndex === 0) { + if (pageIndex === 0) { Navigation.goBack(); return; } - ref.current?.movePrevious(); - prevScreen(); - }; - - const handleNextScreen = () => { - ref.current?.moveNext(); - nextScreen(); + prevPage(); }; const shouldPageBeBlocked = !isEmptyObject(policy?.connections?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE]) && !hasAuthError; @@ -68,20 +62,20 @@ function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) { onBackButtonPress={handleBackButtonPress} shouldLoadForEmptyConnection={isEmptyObject(policy?.connections?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE])} shouldBeBlocked={shouldPageBeBlocked} - shouldUseScrollView={SubStep !== NetSuiteTokenInputForm} + shouldUseScrollView={CurrentPage !== NetSuiteTokenInputForm} > - - diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenInputForm.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx similarity index 95% rename from src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenInputForm.tsx rename to src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx index 4928fb2b4e17..b1f448c1bf0f 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenInputForm.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx @@ -13,12 +13,12 @@ import {connectPolicyToNetSuite} from '@libs/actions/connections/NetSuiteCommand import {isMobileSafari} from '@libs/Browser'; import {addErrorMessage} from '@libs/ErrorUtils'; import Parser from '@libs/Parser'; -import type {SubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; +import type {CustomSubPageTokenInputProps} from '@pages/workspace/accounting/netsuite/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/NetSuiteTokenInputForm'; -function NetSuiteTokenInputForm({onNext, policyID}: SubStepWithPolicy) { +function NetSuiteTokenInputForm({onNext, policyID}: CustomSubPageTokenInputProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenSetupContent.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenSetupContent.tsx similarity index 82% rename from src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenSetupContent.tsx rename to src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenSetupContent.tsx index 0301e634a582..15cb664ee310 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/substeps/NetSuiteTokenSetupContent.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenSetupContent.tsx @@ -7,16 +7,16 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Parser from '@libs/Parser'; -import type {SubStepWithPolicy} from '@pages/workspace/accounting/netsuite/types'; +import type {CustomSubPageTokenInputProps} from '@pages/workspace/accounting/netsuite/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; -function NetSuiteTokenSetupContent({onNext, screenIndex}: SubStepWithPolicy) { +function NetSuiteTokenSetupContent({onNext, currentPageName}: CustomSubPageTokenInputProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const stepKeys = CONST.NETSUITE_CONFIG.TOKEN_INPUT_STEP_KEYS; - const currentStepKey = stepKeys[(screenIndex ?? 0) as keyof typeof stepKeys]; + const stepKeys = CONST.NETSUITE_CONFIG.TOKEN_INPUT.STEP_KEYS; + const currentStepKey = stepKeys[currentPageName as keyof typeof stepKeys]; const titleKey = `workspace.netsuite.tokenInput.formSteps.${currentStepKey}.title` as TranslationPaths; const description = `workspace.netsuite.tokenInput.formSteps.${currentStepKey}.description` as TranslationPaths; diff --git a/src/pages/workspace/accounting/netsuite/types.ts b/src/pages/workspace/accounting/netsuite/types.ts index 910c5a67d9a0..61f9ef5c1041 100644 --- a/src/pages/workspace/accounting/netsuite/types.ts +++ b/src/pages/workspace/accounting/netsuite/types.ts @@ -3,6 +3,7 @@ import type {ValueOf} from 'type-fest'; import type {MenuItemProps} from '@components/MenuItem'; import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; import type {SelectorType} from '@components/SelectionScreen'; +import type {SubPageProps} from '@hooks/useSubPage/types'; import type {SubStepProps} from '@hooks/useSubStep/types'; import type {ToggleSettingOptionRowProps} from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import type CONST from '@src/CONST'; @@ -106,7 +107,7 @@ type CustomListSelectorType = SelectorType & { id: string; }; -type SubStepWithPolicy = SubStepProps & {policyID: string | undefined}; +type CustomSubPageTokenInputProps = SubPageProps & {policyID: string | undefined}; export type { MenuItem, @@ -118,5 +119,5 @@ export type { CustomFieldSubStepWithPolicy, CustomListSelectorType, ExtendedMenuItemWithSubscribedSettings, - SubStepWithPolicy, + CustomSubPageTokenInputProps, }; diff --git a/src/pages/workspace/accounting/netsuite/utils.ts b/src/pages/workspace/accounting/netsuite/utils.ts index 6ee461504053..1e47998a41f7 100644 --- a/src/pages/workspace/accounting/netsuite/utils.ts +++ b/src/pages/workspace/accounting/netsuite/utils.ts @@ -2,6 +2,9 @@ import type {ValueOf} from 'type-fest'; import {canUseProvincialTaxNetSuite, canUseTaxNetSuite} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import type {NetSuiteConnectionConfig, NetSuiteSubsidiary} from '@src/types/onyx/Policy'; +import {isAuthenticationError} from '@libs/actions/connections'; +import type Policy from '@src/types/onyx/Policy'; +import type {OnyxEntry} from 'react-native-onyx'; function shouldHideReimbursedReportsSection(config?: NetSuiteConnectionConfig) { return config?.reimbursableExpensesExportDestination === CONST.NETSUITE_EXPORT_DESTINATION.JOURNAL_ENTRY; @@ -73,6 +76,11 @@ function getImportCustomFieldsSettings(importField: ValueOf `${importField}_${index}`); } +function getInitialSubPageForNetsuiteTokenInput(policy: OnyxEntry) { + const hasAuthError = isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE); + return hasAuthError ? CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.CREDENTIALS : CONST.NETSUITE_CONFIG.TOKEN_INPUT.PAGE_NAME.INSTALL; +} + export { shouldHideReimbursedReportsSection, shouldHideReportsExportTo, @@ -89,4 +97,5 @@ export { shouldHideTaxPostingAccountSelect, shouldHideExportForeignCurrencyAmount, getImportCustomFieldsSettings, + getInitialSubPageForNetsuiteTokenInput, }; diff --git a/src/pages/workspace/accounting/utils.tsx b/src/pages/workspace/accounting/utils.tsx index a7bf68e6f21d..50f7d9569592 100644 --- a/src/pages/workspace/accounting/utils.tsx +++ b/src/pages/workspace/accounting/utils.tsx @@ -23,6 +23,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import { getImportCustomFieldsSettings, + getInitialSubPageForNetsuiteTokenInput, shouldHideCustomFormIDOptions, shouldHideExportForeignCurrencyAmount, shouldHideExportJournalsTo, @@ -221,7 +222,7 @@ function getAccountingIntegrationData( integrationAlias: CONST.UPGRADE_FEATURE_INTRO_MAPPING.netsuite.alias, backToAfterWorkspaceUpgradeRoute: integrationToDisconnect ? ROUTES.POLICY_ACCOUNTING.getRoute(policyID, connectionName, integrationToDisconnect, shouldDisconnectIntegrationBeforeConnecting) - : ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID), + : ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID, getInitialSubPageForNetsuiteTokenInput(policy)), }, pendingFields: {...netsuiteConfig?.pendingFields, ...policy?.connections?.netsuite?.config?.pendingFields, ...policy?.connections?.netsuite?.options?.config?.pendingFields}, errorFields: {...netsuiteConfig?.errorFields, ...policy?.connections?.netsuite?.config?.errorFields, ...policy?.connections?.netsuite?.options?.config?.errorFields},