From 4ed2347c31ed252c51605efad3dd163c23f03649 Mon Sep 17 00:00:00 2001 From: "huult (via MelvinBot)" Date: Tue, 16 Jun 2026 15:55:10 +0000 Subject: [PATCH 1/6] Migrate Travel workspace confirmation to dynamic route Migrate TRAVEL_WORKSPACE_CONFIRMATION from a static backTo-based route to a dynamic route using createDynamicRoute/useDynamicBackPath, per the migrate-to-dynamic-route skill. Co-authored-by: huult --- config/eslint/eslint.seatbelt.tsv | 4 ++-- src/ROUTES.ts | 9 ++++----- src/SCREENS.ts | 2 +- .../ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 5 +---- src/pages/Travel/DynamicTravelUpgrade.tsx | 5 +++-- ...amicWorkspaceConfirmationForTravelPage.tsx} | 18 +++++++----------- 8 files changed, 20 insertions(+), 27 deletions(-) rename src/pages/Travel/{WorkspaceConfirmationForTravelPage.tsx => DynamicWorkspaceConfirmationForTravelPage.tsx} (76%) diff --git a/config/eslint/eslint.seatbelt.tsv b/config/eslint/eslint.seatbelt.tsv index 3c950d420498..0b4f610d047d 100644 --- a/config/eslint/eslint.seatbelt.tsv +++ b/config/eslint/eslint.seatbelt.tsv @@ -68,7 +68,7 @@ "../../src/Expensify.tsx" "react-hooks/set-state-in-effect" 1 "../../src/GlobalModals.tsx" "no-restricted-syntax" 2 "../../src/ONYXKEYS.ts" "no-restricted-syntax" 2 -"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 128 +"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 127 "../../src/ROUTES.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/TIMEZONES.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/AccountingConnectionConfirmationModal.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 @@ -1121,8 +1121,8 @@ "../../src/pages/TransactionMerge/MergeTransactionsListContent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicTravelTerms.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicTravelUpgrade.tsx" "no-restricted-imports" 1 +"../../src/pages/Travel/DynamicWorkspaceConfirmationForTravelPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/Travel/TravelUpgrade.tsx" "react-hooks/set-state-in-effect" 1 -"../../src/pages/Travel/WorkspaceConfirmationForTravelPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/UnreportedExpenseListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/ValidateLoginPage/index.web.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/domain/Groups/PreferredWorkspaceToggle.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d37f9e39d1e1..0c3f3f8b0e48 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -809,6 +809,10 @@ const DYNAMIC_ROUTES = { path: 'travel-upgrade', entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT], }, + TRAVEL_WORKSPACE_CONFIRMATION: { + path: 'workspace-confirmation', + entryScreens: [SCREENS.TRAVEL.DYNAMIC_UPGRADE], + }, REPORT_CHANGE_APPROVER: { path: 'change-approver', entryScreens: [SCREENS.REPORT, SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT], @@ -3273,11 +3277,6 @@ const ROUTES = { return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${pnr}/${sequenceIndex}`, backTo); }, }, - TRAVEL_WORKSPACE_CONFIRMATION: { - route: 'travel/upgrade/workspace/confirmation', - - getRoute: (backTo?: string) => getUrlWithBackToParam(`travel/upgrade/workspace/confirmation`, backTo), - }, TRAVEL_WORKSPACE_ADDRESS: { route: 'travel/:domain/workspace-address', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index d1f17873afa7..f3f1ed4125d6 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -37,7 +37,7 @@ const SCREENS = { TRIP_DETAILS: 'Travel_TripDetails', DYNAMIC_DOMAIN_SELECTOR: 'Dynamic_Travel_DomainSelector', DYNAMIC_PUBLIC_DOMAIN_ERROR: 'Dynamic_Travel_PublicDomainError', - WORKSPACE_CONFIRMATION: 'Travel_WorkspaceConfirmation', + DYNAMIC_WORKSPACE_CONFIRMATION: 'Dynamic_Travel_WorkspaceConfirmation', WORKSPACE_ADDRESS: 'Travel_WorkspaceAddress', TRAVEL_DOT_LINK_WEB_VIEW: 'Travel_DotLinkWebView', VERIFY_ACCOUNT: 'Travel_VerifyAccount', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 7622435ba7ba..e0ced2641242 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -236,7 +236,7 @@ const TravelModalStackNavigator = createModalStackNavigator require('../../../../pages/Travel/DynamicDomainSelectorPage').default, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_PERMISSION_INFO]: () => require('../../../../pages/Travel/DynamicDomainPermissionInfoPage').default, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: () => require('../../../../pages/Travel/DynamicPublicDomainErrorPage').default, - [SCREENS.TRAVEL.WORKSPACE_CONFIRMATION]: () => require('../../../../pages/Travel/WorkspaceConfirmationForTravelPage').default, + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: () => require('../../../../pages/Travel/DynamicWorkspaceConfirmationForTravelPage').default, [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: () => require('../../../../pages/Travel/WorkspaceAddressForTravelPage').default, [SCREENS.TRAVEL.VERIFY_ACCOUNT]: () => require('../../../../pages/Travel/VerifyAccountPage').default, }); diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 6d75bb519d38..e804193a84b3 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1931,7 +1931,7 @@ const config: LinkingOptions['config'] = { [SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR]: DYNAMIC_ROUTES.TRAVEL_DOMAIN_SELECTOR.path, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_PERMISSION_INFO]: DYNAMIC_ROUTES.TRAVEL_DOMAIN_PERMISSION_INFO.path, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: DYNAMIC_ROUTES.TRAVEL_PUBLIC_DOMAIN_ERROR.path, - [SCREENS.TRAVEL.WORKSPACE_CONFIRMATION]: ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.route, + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: DYNAMIC_ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.path, [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: ROUTES.TRAVEL_WORKSPACE_ADDRESS.route, [SCREENS.TRAVEL.VERIFY_ACCOUNT]: ROUTES.TRAVEL_VERIFY_ACCOUNT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 27d4930de83f..75db78fd750b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2637,10 +2637,7 @@ type TravelNavigatorParamList = { domain: string; policyID?: string; }; - [SCREENS.TRAVEL.WORKSPACE_CONFIRMATION]: { - // 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.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: undefined; [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: { domain: 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 diff --git a/src/pages/Travel/DynamicTravelUpgrade.tsx b/src/pages/Travel/DynamicTravelUpgrade.tsx index 8969f7ff7f03..80bfde1f3a9b 100644 --- a/src/pages/Travel/DynamicTravelUpgrade.tsx +++ b/src/pages/Travel/DynamicTravelUpgrade.tsx @@ -8,13 +8,14 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getActivePolicies, isPaidGroupPolicy} from '@libs/PolicyUtils'; import UpgradeConfirmation from '@pages/workspace/upgrade/UpgradeConfirmation'; import UpgradeIntro from '@pages/workspace/upgrade/UpgradeIntro'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; function DynamicTravelUpgrade() { const styles = useThemeStyles(); @@ -29,7 +30,7 @@ function DynamicTravelUpgrade() { const isUpgraded = groupPaidPolicies.length > 0; const openWorkspaceConfirmation = () => { - Navigation.navigate(ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.getRoute(Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.path)); }; return ( diff --git a/src/pages/Travel/WorkspaceConfirmationForTravelPage.tsx b/src/pages/Travel/DynamicWorkspaceConfirmationForTravelPage.tsx similarity index 76% rename from src/pages/Travel/WorkspaceConfirmationForTravelPage.tsx rename to src/pages/Travel/DynamicWorkspaceConfirmationForTravelPage.tsx index e8a5acfd9c57..3575b8c2470c 100644 --- a/src/pages/Travel/WorkspaceConfirmationForTravelPage.tsx +++ b/src/pages/Travel/DynamicWorkspaceConfirmationForTravelPage.tsx @@ -1,4 +1,3 @@ -import type {StackScreenProps} from '@react-navigation/stack'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import React from 'react'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -6,20 +5,17 @@ import WorkspaceConfirmationForm from '@components/WorkspaceConfirmationForm'; import type {WorkspaceConfirmationSubmitFunctionParams} from '@components/WorkspaceConfirmationForm'; import useActivePolicy from '@hooks/useActivePolicy'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useOnyx from '@hooks/useOnyx'; import {createDraftWorkspace, createWorkspace} from '@libs/actions/Policy/Policy'; -import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; -import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; -type WorkspaceConfirmationForTravelPageProps = StackScreenProps; - -function WorkspaceConfirmationForTravelPage({route}: WorkspaceConfirmationForTravelPageProps) { +function DynamicWorkspaceConfirmationForTravelPage() { + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.path); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); @@ -29,7 +25,7 @@ function WorkspaceConfirmationForTravelPage({route}: WorkspaceConfirmationForTra const hasActiveAdminPolicies = useHasActiveAdminPolicies(); const goBack = () => { - Navigation.goBack(route.params?.backTo ?? createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_UPGRADE.path, ROUTES.TRAVEL_MY_TRIPS.route)); + Navigation.goBack(backPath); }; const onSubmit = (params: WorkspaceConfirmationSubmitFunctionParams) => { @@ -62,7 +58,7 @@ function WorkspaceConfirmationForTravelPage({route}: WorkspaceConfirmationForTra return ( Date: Wed, 17 Jun 2026 09:55:48 +0700 Subject: [PATCH 2/6] migrate TRAVEL_WORKSPACE_ADDRESS Co-authored-by: huult --- src/ROUTES.ts | 14 +++++++------- src/SCREENS.ts | 2 +- src/components/BookTravelButton.tsx | 4 ++-- .../AppNavigator/ModalStackNavigators/index.tsx | 4 ++-- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 4 +--- src/pages/Travel/DynamicDomainSelectorPage.tsx | 4 ++-- ...sx => DynamicWorkspaceAddressForTravelPage.tsx} | 14 ++++++++------ 8 files changed, 24 insertions(+), 24 deletions(-) rename src/pages/Travel/{WorkspaceAddressForTravelPage.tsx => DynamicWorkspaceAddressForTravelPage.tsx} (80%) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 0c3f3f8b0e48..7b24406ead8c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -462,7 +462,7 @@ const DYNAMIC_ROUTES = { SCREENS.WORKSPACE.DYNAMIC_WORKSPACE_OVERVIEW_ADDRESS, SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS, SCREENS.DOMAIN_CARD.DOMAIN_CARD_UPDATE_ADDRESS, - SCREENS.TRAVEL.WORKSPACE_ADDRESS, + SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS, SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT, ], getRoute: (country = '') => `country?country=${country}`, @@ -790,7 +790,7 @@ const DYNAMIC_ROUTES = { SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT, SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR, - SCREENS.TRAVEL.WORKSPACE_ADDRESS, + SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS, SCREENS.TRAVEL.VERIFY_ACCOUNT, ], getRoute: (domain: string, policyID?: string) => `terms/${domain}/accept${policyID ? `/${policyID}` : ''}`, @@ -799,6 +799,11 @@ const DYNAMIC_ROUTES = { path: 'domain-permission-info', entryScreens: [SCREENS.TRAVEL.DYNAMIC_TCS], }, + TRAVEL_WORKSPACE_ADDRESS: { + path: 'workspace-address/:domain/:policyID?', + entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT, SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR], + getRoute: (domain: string, policyID?: string) => `workspace-address/${domain}${policyID ? `/${policyID}` : ''}`, + }, TRAVEL_DOMAIN_SELECTOR: { path: 'domain-selector', entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT], @@ -3277,11 +3282,6 @@ const ROUTES = { return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${pnr}/${sequenceIndex}`, backTo); }, }, - TRAVEL_WORKSPACE_ADDRESS: { - route: 'travel/:domain/workspace-address', - - getRoute: (domain: string, policyID?: string, backTo?: string) => getUrlWithBackToParam(`travel/${domain}/workspace-address?${policyID ? `policyID=${policyID}` : ''}`, backTo), - }, TRAVEL_VERIFY_ACCOUNT: { route: `travel/${VERIFY_ACCOUNT}`, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f3f1ed4125d6..df0e13651e87 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -38,7 +38,7 @@ const SCREENS = { DYNAMIC_DOMAIN_SELECTOR: 'Dynamic_Travel_DomainSelector', DYNAMIC_PUBLIC_DOMAIN_ERROR: 'Dynamic_Travel_PublicDomainError', DYNAMIC_WORKSPACE_CONFIRMATION: 'Dynamic_Travel_WorkspaceConfirmation', - WORKSPACE_ADDRESS: 'Travel_WorkspaceAddress', + DYNAMIC_WORKSPACE_ADDRESS: 'Dynamic_Travel_WorkspaceAddress', TRAVEL_DOT_LINK_WEB_VIEW: 'Travel_DotLinkWebView', VERIFY_ACCOUNT: 'Travel_VerifyAccount', MISSING_PERSONAL_DETAILS_CONFIRM_MAGIC_CODE: 'Travel_MissingPersonalDetails_ConfirmMagicCode', diff --git a/src/components/BookTravelButton.tsx b/src/components/BookTravelButton.tsx index 6fc5044ffb1f..5daa36eb9a2a 100644 --- a/src/components/BookTravelButton.tsx +++ b/src/components/BookTravelButton.tsx @@ -194,7 +194,7 @@ function BookTravelButton({ if (!isUserValidated) { // Determine where to redirect after OTP validation const nextStep = isEmptyObject(policy?.address) - ? ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, activePolicyID, Navigation.getActiveRoute()) + ? createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, activePolicyID)) : createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, activePolicyID)); setTravelProvisioningNextStep(nextStep); Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, activePolicyID, Navigation.getActiveRoute())); @@ -202,7 +202,7 @@ function BookTravelButton({ } if (isEmptyObject(policy?.address)) { // Spotnana requires an address anytime an entity is created for a policy - Navigation.navigate(ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, activePolicyID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, activePolicyID))); } else { navigateToAcceptTerms(domain, !!isUserValidated, activePolicyID ?? undefined); } diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index e0ced2641242..1fe21da0b4c9 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -88,7 +88,7 @@ const OPTIONS_PER_SCREEN: Partial [SCREENS.TRAVEL.VERIFY_ACCOUNT]: { animationTypeForReplace: 'push', }, - [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: { + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: { animationTypeForReplace: 'push', }, [SCREENS.MULTIFACTOR_AUTHENTICATION.REVOKE]: { @@ -237,7 +237,7 @@ const TravelModalStackNavigator = createModalStackNavigator require('../../../../pages/Travel/DynamicDomainPermissionInfoPage').default, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: () => require('../../../../pages/Travel/DynamicPublicDomainErrorPage').default, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: () => require('../../../../pages/Travel/DynamicWorkspaceConfirmationForTravelPage').default, - [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: () => require('../../../../pages/Travel/WorkspaceAddressForTravelPage').default, + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: () => require('../../../../pages/Travel/DynamicWorkspaceAddressForTravelPage').default, [SCREENS.TRAVEL.VERIFY_ACCOUNT]: () => require('../../../../pages/Travel/VerifyAccountPage').default, }); diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index e804193a84b3..122460a95605 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1932,7 +1932,7 @@ const config: LinkingOptions['config'] = { [SCREENS.TRAVEL.DYNAMIC_DOMAIN_PERMISSION_INFO]: DYNAMIC_ROUTES.TRAVEL_DOMAIN_PERMISSION_INFO.path, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: DYNAMIC_ROUTES.TRAVEL_PUBLIC_DOMAIN_ERROR.path, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: DYNAMIC_ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.path, - [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: ROUTES.TRAVEL_WORKSPACE_ADDRESS.route, + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.path, [SCREENS.TRAVEL.VERIFY_ACCOUNT]: ROUTES.TRAVEL_VERIFY_ACCOUNT.route, }, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 75db78fd750b..cfa92af1eed1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2638,10 +2638,8 @@ type TravelNavigatorParamList = { policyID?: string; }; [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: undefined; - [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: { + [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: { domain: 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; policyID?: string; }; [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: { diff --git a/src/pages/Travel/DynamicDomainSelectorPage.tsx b/src/pages/Travel/DynamicDomainSelectorPage.tsx index 8f2bf62f6db1..d621f9c17ea6 100644 --- a/src/pages/Travel/DynamicDomainSelectorPage.tsx +++ b/src/pages/Travel/DynamicDomainSelectorPage.tsx @@ -65,7 +65,7 @@ function DynamicDomainSelectorPage({route}: DomainSelectorPageProps) { if (!isUserValidated) { // Determine where to redirect after OTP validation const nextStep = isEmptyObject(policy?.address) - ? ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, policyID, Navigation.getActiveRoute()) + ? createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, policyID)) : createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, policyID)); setTravelProvisioningNextStep(nextStep); Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, policyID)); @@ -73,7 +73,7 @@ function DynamicDomainSelectorPage({route}: DomainSelectorPageProps) { } if (isEmptyObject(policy?.address)) { // Spotnana requires an address anytime an entity is created for a policy - Navigation.navigate(ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, policyID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, policyID))); } else { cleanupTravelProvisioningSession(); Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, policyID))); diff --git a/src/pages/Travel/WorkspaceAddressForTravelPage.tsx b/src/pages/Travel/DynamicWorkspaceAddressForTravelPage.tsx similarity index 80% rename from src/pages/Travel/WorkspaceAddressForTravelPage.tsx rename to src/pages/Travel/DynamicWorkspaceAddressForTravelPage.tsx index ead02cf5a716..eccccb95828d 100644 --- a/src/pages/Travel/WorkspaceAddressForTravelPage.tsx +++ b/src/pages/Travel/DynamicWorkspaceAddressForTravelPage.tsx @@ -1,6 +1,7 @@ import {isUserValidatedSelector} from '@selectors/Account'; import React from 'react'; import type {FormOnyxValues} from '@components/Form/types'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; @@ -15,12 +16,14 @@ import {updateAddress} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type WorkspaceAddressForTravelPageProps = PlatformStackScreenProps; +type DynamicWorkspaceAddressForTravelPageProps = PlatformStackScreenProps; -function WorkspaceAddressForTravelPage({route}: WorkspaceAddressForTravelPageProps) { +function DynamicWorkspaceAddressForTravelPage({route}: DynamicWorkspaceAddressForTravelPageProps) { const {translate} = useLocalize(); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.path); const {policyID} = route.params; const policy = usePolicy(policyID); const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector}); @@ -33,8 +36,7 @@ function WorkspaceAddressForTravelPage({route}: WorkspaceAddressForTravelPagePro // Always validate OTP first before allowing address submission if (!isUserValidated) { // After OTP validation, redirect back to this address page - const currentRoute = ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(route.params.domain, policyID, route.params.backTo); - setTravelProvisioningNextStep(currentRoute); + setTravelProvisioningNextStep(Navigation.getActiveRoute() as Route); Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(route.params.domain, policyID)); return; } @@ -56,10 +58,10 @@ function WorkspaceAddressForTravelPage({route}: WorkspaceAddressForTravelPagePro isLoadingApp={false} updateAddress={updatePolicyAddress} title={translate('common.companyAddress')} - backTo={route.params.backTo} + backTo={backPath} /> ); } -export default WorkspaceAddressForTravelPage; +export default DynamicWorkspaceAddressForTravelPage; From c164b656dfc2ca6f1eaa49c73ad7c3ff851d8ac5 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:10:39 +0700 Subject: [PATCH 3/6] migrate TRAVEL_VERIFY_ACCOUNT Co-authored-by: huult --- src/ROUTES.ts | 12 ++++++------ src/SCREENS.ts | 2 +- src/components/BookTravelButton.tsx | 6 +++--- .../AppNavigator/ModalStackNavigators/index.tsx | 4 ++-- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 6 ++---- src/pages/Travel/DynamicDomainSelectorPage.tsx | 4 ++-- ...ountPage.tsx => DynamicVerifyAccountPage.tsx} | 16 +++++++++------- .../DynamicWorkspaceAddressForTravelPage.tsx | 4 ++-- 9 files changed, 28 insertions(+), 28 deletions(-) rename src/pages/Travel/{VerifyAccountPage.tsx => DynamicVerifyAccountPage.tsx} (77%) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7b24406ead8c..86c574e24644 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -791,7 +791,7 @@ const DYNAMIC_ROUTES = { SCREENS.SEARCH.ROOT, SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR, SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS, - SCREENS.TRAVEL.VERIFY_ACCOUNT, + SCREENS.TRAVEL.DYNAMIC_VERIFY_ACCOUNT, ], getRoute: (domain: string, policyID?: string) => `terms/${domain}/accept${policyID ? `/${policyID}` : ''}`, }, @@ -804,6 +804,11 @@ const DYNAMIC_ROUTES = { entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT, SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR], getRoute: (domain: string, policyID?: string) => `workspace-address/${domain}${policyID ? `/${policyID}` : ''}`, }, + TRAVEL_VERIFY_ACCOUNT: { + path: 'verify-account/:domain/:policyID?', + entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT, SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR, SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS], + getRoute: (domain: string, policyID?: string) => `verify-account/${domain}${policyID ? `/${policyID}` : ''}`, + }, TRAVEL_DOMAIN_SELECTOR: { path: 'domain-selector', entryScreens: [SCREENS.TRAVEL.MY_TRIPS, SCREENS.WORKSPACE.TRAVEL, SCREENS.SEARCH.ROOT], @@ -3282,11 +3287,6 @@ const ROUTES = { return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${pnr}/${sequenceIndex}`, backTo); }, }, - TRAVEL_VERIFY_ACCOUNT: { - route: `travel/${VERIFY_ACCOUNT}`, - - getRoute: (domain?: string, policyID?: string, backTo?: string) => getUrlWithBackToParam(getUrlWithParams(`travel/${VERIFY_ACCOUNT}`, {domain, policyID}), backTo), - }, ONBOARDING_ROOT: { route: 'onboarding', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index df0e13651e87..394e26fe3f15 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -40,7 +40,7 @@ const SCREENS = { DYNAMIC_WORKSPACE_CONFIRMATION: 'Dynamic_Travel_WorkspaceConfirmation', DYNAMIC_WORKSPACE_ADDRESS: 'Dynamic_Travel_WorkspaceAddress', TRAVEL_DOT_LINK_WEB_VIEW: 'Travel_DotLinkWebView', - VERIFY_ACCOUNT: 'Travel_VerifyAccount', + DYNAMIC_VERIFY_ACCOUNT: 'Dynamic_Travel_VerifyAccount', MISSING_PERSONAL_DETAILS_CONFIRM_MAGIC_CODE: 'Travel_MissingPersonalDetails_ConfirmMagicCode', }, SEARCH: { diff --git a/src/components/BookTravelButton.tsx b/src/components/BookTravelButton.tsx index 5daa36eb9a2a..61f3f13725a2 100644 --- a/src/components/BookTravelButton.tsx +++ b/src/components/BookTravelButton.tsx @@ -53,7 +53,7 @@ const navigateToAcceptTerms = (domain: string, isUserValidated?: boolean, policy Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, policyID))); return; } - Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, policyID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, policyID))); }; const hasPolicyIDInActiveRoute = () => getSearchParamFromPath(Navigation.getActiveRoute(), CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID) !== null; @@ -164,7 +164,7 @@ function BookTravelButton({ navigateToAcceptTerms(CONST.TRAVEL.DEFAULT_DOMAIN, undefined, activePolicyID ?? undefined); } else if (!isBetaEnabled(CONST.BETAS.IS_TRAVEL_VERIFIED)) { if (!isUserValidated) { - Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(undefined, activePolicyID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(CONST.TRAVEL.DEFAULT_DOMAIN, activePolicyID))); return; } if (shouldShowVerifyAccountModal) { @@ -197,7 +197,7 @@ function BookTravelButton({ ? createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, activePolicyID)) : createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, activePolicyID)); setTravelProvisioningNextStep(nextStep); - Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, activePolicyID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, activePolicyID))); return; } if (isEmptyObject(policy?.address)) { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 1fe21da0b4c9..df9edddbe7f8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -85,7 +85,7 @@ const OPTIONS_PER_SCREEN: Partial [SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: { animation: Animations.NONE, }, - [SCREENS.TRAVEL.VERIFY_ACCOUNT]: { + [SCREENS.TRAVEL.DYNAMIC_VERIFY_ACCOUNT]: { animationTypeForReplace: 'push', }, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: { @@ -238,7 +238,7 @@ const TravelModalStackNavigator = createModalStackNavigator require('../../../../pages/Travel/DynamicPublicDomainErrorPage').default, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: () => require('../../../../pages/Travel/DynamicWorkspaceConfirmationForTravelPage').default, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: () => require('../../../../pages/Travel/DynamicWorkspaceAddressForTravelPage').default, - [SCREENS.TRAVEL.VERIFY_ACCOUNT]: () => require('../../../../pages/Travel/VerifyAccountPage').default, + [SCREENS.TRAVEL.DYNAMIC_VERIFY_ACCOUNT]: () => require('../../../../pages/Travel/DynamicVerifyAccountPage').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 122460a95605..615f4a2a45ee 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1933,7 +1933,7 @@ const config: LinkingOptions['config'] = { [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: DYNAMIC_ROUTES.TRAVEL_PUBLIC_DOMAIN_ERROR.path, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_CONFIRMATION]: DYNAMIC_ROUTES.TRAVEL_WORKSPACE_CONFIRMATION.path, [SCREENS.TRAVEL.DYNAMIC_WORKSPACE_ADDRESS]: DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.path, - [SCREENS.TRAVEL.VERIFY_ACCOUNT]: ROUTES.TRAVEL_VERIFY_ACCOUNT.route, + [SCREENS.TRAVEL.DYNAMIC_VERIFY_ACCOUNT]: DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.path, }, }, [SCREENS.RIGHT_MODAL.SEARCH_COLUMNS]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index cfa92af1eed1..68eb78c6238d 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2649,11 +2649,9 @@ type TravelNavigatorParamList = { [SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR]: { policyID?: string; }; - [SCREENS.TRAVEL.VERIFY_ACCOUNT]: { - domain?: string; + [SCREENS.TRAVEL.DYNAMIC_VERIFY_ACCOUNT]: { + domain: string; 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; }; }; diff --git a/src/pages/Travel/DynamicDomainSelectorPage.tsx b/src/pages/Travel/DynamicDomainSelectorPage.tsx index d621f9c17ea6..7fd52deb552b 100644 --- a/src/pages/Travel/DynamicDomainSelectorPage.tsx +++ b/src/pages/Travel/DynamicDomainSelectorPage.tsx @@ -22,7 +22,7 @@ import {getAdminsPrivateEmailDomains, getMostFrequentEmailDomain} from '@libs/Po import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -68,7 +68,7 @@ function DynamicDomainSelectorPage({route}: DomainSelectorPageProps) { ? createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_WORKSPACE_ADDRESS.getRoute(domain, policyID)) : createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TCS.getRoute(domain, policyID)); setTravelProvisioningNextStep(nextStep); - Navigation.navigate(ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, policyID)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.getRoute(domain, policyID))); return; } if (isEmptyObject(policy?.address)) { diff --git a/src/pages/Travel/VerifyAccountPage.tsx b/src/pages/Travel/DynamicVerifyAccountPage.tsx similarity index 77% rename from src/pages/Travel/VerifyAccountPage.tsx rename to src/pages/Travel/DynamicVerifyAccountPage.tsx index 8d39b594decf..c19eb61896f8 100644 --- a/src/pages/Travel/VerifyAccountPage.tsx +++ b/src/pages/Travel/DynamicVerifyAccountPage.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect} from 'react'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import {requestTravelAccess, setTravelProvisioningNextStep} from '@libs/actions/Travel'; @@ -12,10 +13,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type VerifyAccountPageProps = StackScreenProps; +type DynamicVerifyAccountPageProps = StackScreenProps; -function VerifyAccountPage({route}: VerifyAccountPageProps) { - const {domain, backTo, policyID} = route.params; +function DynamicVerifyAccountPage({route}: DynamicVerifyAccountPageProps) { + const {domain, policyID} = route.params; + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.TRAVEL_VERIFY_ACCOUNT.path); const [travelProvisioning] = useOnyx(ONYXKEYS.TRAVEL_PROVISIONING); const {isBetaEnabled} = usePermissions(); @@ -36,12 +38,12 @@ function VerifyAccountPage({route}: VerifyAccountPageProps) { }, []); const handleClose = useCallback(() => { - Navigation.goBack(backTo); - }, [backTo]); + Navigation.goBack(backPath); + }, [backPath]); return ( Date: Wed, 17 Jun 2026 03:45:07 +0000 Subject: [PATCH 4/6] Update eslint seatbelt for migrated TRAVEL_WORKSPACE_ADDRESS and TRAVEL_VERIFY_ACCOUNT routes Decrement ROUTES.ts getUrlWithBackToParam count (removed two static routes) and grandfather the no-unsafe-type-assertion in DynamicWorkspaceAddressForTravelPage. Co-authored-by: huult --- config/eslint/eslint.seatbelt.tsv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/eslint/eslint.seatbelt.tsv b/config/eslint/eslint.seatbelt.tsv index 0b4f610d047d..84475056f668 100644 --- a/config/eslint/eslint.seatbelt.tsv +++ b/config/eslint/eslint.seatbelt.tsv @@ -68,7 +68,7 @@ "../../src/Expensify.tsx" "react-hooks/set-state-in-effect" 1 "../../src/GlobalModals.tsx" "no-restricted-syntax" 2 "../../src/ONYXKEYS.ts" "no-restricted-syntax" 2 -"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 127 +"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 125 "../../src/ROUTES.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/TIMEZONES.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/AccountingConnectionConfirmationModal.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1 @@ -1121,6 +1121,7 @@ "../../src/pages/TransactionMerge/MergeTransactionsListContent.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicTravelTerms.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicTravelUpgrade.tsx" "no-restricted-imports" 1 +"../../src/pages/Travel/DynamicWorkspaceAddressForTravelPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/pages/Travel/DynamicWorkspaceConfirmationForTravelPage.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 "../../src/pages/Travel/TravelUpgrade.tsx" "react-hooks/set-state-in-effect" 1 "../../src/pages/UnreportedExpenseListItem.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1 From 5353b204194b2df5b154479d3ff26397315881e5 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:14:57 +0700 Subject: [PATCH 5/6] migrate TRAVEL_TRIP_SUMMARY and TRAVEL_TRIP_DETAILS Co-authored-by: huult --- src/ROUTES.ts | 30 +++++++------------ src/SCREENS.ts | 4 +-- .../ReportActionItem/MoneyRequestView.tsx | 14 +++++++-- .../ReportActionItem/TripDetailsView.tsx | 8 +++-- .../ModalStackNavigators/index.tsx | 4 +-- src/libs/Navigation/linkingConfig/config.ts | 9 ++---- src/libs/Navigation/types.ts | 10 ++----- ...ilsPage.tsx => DynamicTripDetailsPage.tsx} | 13 +++++--- ...aryPage.tsx => DynamicTripSummaryPage.tsx} | 15 ++++++---- .../UpcomingTravelItem.tsx | 10 +++++-- 10 files changed, 63 insertions(+), 54 deletions(-) rename src/pages/Travel/{TripDetailsPage.tsx => DynamicTripDetailsPage.tsx} (93%) rename src/pages/Travel/{TripSummaryPage.tsx => DynamicTripSummaryPage.tsx} (80%) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 86c574e24644..0400cf68755e 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -823,6 +823,16 @@ const DYNAMIC_ROUTES = { path: 'workspace-confirmation', entryScreens: [SCREENS.TRAVEL.DYNAMIC_UPGRADE], }, + TRAVEL_TRIP_SUMMARY: { + path: 'trip/:transactionID', + entryScreens: [SCREENS.REPORT], + getRoute: (transactionID: string) => `trip/${transactionID}` as const, + }, + TRAVEL_TRIP_DETAILS: { + path: 'trip/:transactionID/:pnr/:sequenceIndex', + entryScreens: [SCREENS.REPORT], + getRoute: (transactionID: string, pnr: string, sequenceIndex: number | string) => `trip/${transactionID}/${pnr}/${sequenceIndex}` as const, + }, REPORT_CHANGE_APPROVER: { path: 'change-approver', entryScreens: [SCREENS.REPORT, SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT], @@ -3267,26 +3277,6 @@ const ROUTES = { }, }, TRACK_TRAINING_MODAL: 'track-training', - TRAVEL_TRIP_SUMMARY: { - route: 'r/:reportID/trip/:transactionID', - getRoute: (reportID: string | undefined, transactionID: string | undefined, backTo?: string) => { - if (!reportID || !transactionID) { - Log.warn('Invalid reportID or transactionID is used to build the TRAVEL_TRIP_SUMMARY route'); - } - - return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo); - }, - }, - TRAVEL_TRIP_DETAILS: { - route: 'r/:reportID/trip/:transactionID/:pnr/:sequenceIndex', - getRoute: (reportID: string | undefined, transactionID: string | undefined, pnr: string | undefined, sequenceIndex: number, backTo?: string) => { - if (!reportID || !transactionID || !pnr) { - Log.warn('Invalid reportID, transactionID or pnr is used to build the TRAVEL_TRIP_DETAILS route'); - } - - return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${pnr}/${sequenceIndex}`, backTo); - }, - }, ONBOARDING_ROOT: { route: 'onboarding', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 394e26fe3f15..b4a8f3c1b6e8 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -33,8 +33,8 @@ const SCREENS = { DYNAMIC_TCS: 'Dynamic_Travel_TCS', DYNAMIC_UPGRADE: 'Dynamic_Travel_Upgrade', DYNAMIC_DOMAIN_PERMISSION_INFO: 'Dynamic_Travel_DomainPermissionInfo', - TRIP_SUMMARY: 'Travel_TripSummary', - TRIP_DETAILS: 'Travel_TripDetails', + DYNAMIC_TRIP_SUMMARY: 'Dynamic_Travel_TripSummary', + DYNAMIC_TRIP_DETAILS: 'Dynamic_Travel_TripDetails', DYNAMIC_DOMAIN_SELECTOR: 'Dynamic_Travel_DomainSelector', DYNAMIC_PUBLIC_DOMAIN_ERROR: 'Dynamic_Travel_PublicDomainError', DYNAMIC_WORKSPACE_CONFIRMATION: 'Dynamic_Travel_WorkspaceConfirmation', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index bc785fb5e4d5..8ac7a70fbe57 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -57,6 +57,7 @@ import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getRateFromMerchant} from '@libs/MergeTransactionUtils'; import {isBillableEnabledOnPolicy, isSingleTransactionReport} from '@libs/MoneyRequestReportUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import {hasEnabledOptions} from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; import { @@ -132,7 +133,7 @@ import AnimatedEmptyStateBackground from '@pages/inbox/report/AnimatedEmptyState import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -1433,10 +1434,17 @@ function MoneyRequestView({ icon={icons.Suitcase} onPress={() => { const reservations = transaction?.receipt?.reservationList?.length ?? 0; + const reportID = transactionThreadReport?.reportID; if (reservations > 1) { - Navigation.navigate(ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(transactionThreadReport?.reportID, transaction.transactionID, getReportRHPActiveRoute())); + if (!reportID) { + return; + } + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(transaction.transactionID), ROUTES.REPORT_WITH_ID.getRoute(reportID))); + } + if (!reportID) { + return; } - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(transactionThreadReport?.reportID, transaction.transactionID, '0', 0, getReportRHPActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TRIP_DETAILS.getRoute(transaction.transactionID, '0', 0), ROUTES.REPORT_WITH_ID.getRoute(reportID))); }} /> )} diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 235a37d7f52d..da9d4d06d012 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -17,13 +17,14 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import StringUtils from '@libs/StringUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {ReservationData} from '@src/libs/TripReservationUtils'; import {formatCancelledDescription, formatTransitLocationLabel, getPNRReservationDataFromTripReport, getTripReservationCode, getTripReservationIcon} from '@src/libs/TripReservationUtils'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; import type Transaction from '@src/types/onyx/Transaction'; @@ -166,7 +167,10 @@ function ReservationView({reservation, transactionID, tripRoomReportID, sequence secondaryIconFill={theme.icon} onPress={() => Navigation.navigate( - ROUTES.TRAVEL_TRIP_DETAILS.getRoute(tripRoomReportID, transactionID, String(reservation.reservationID), sequenceIndex, Navigation.getReportRHPActiveRoute()), + createDynamicRoute( + DYNAMIC_ROUTES.TRAVEL_TRIP_DETAILS.getRoute(transactionID, String(reservation.reservationID), sequenceIndex), + ROUTES.REPORT_WITH_ID.getRoute(tripRoomReportID), + ), ) } /> diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index df9edddbe7f8..294a1724e1e0 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -231,8 +231,8 @@ const TravelModalStackNavigator = createModalStackNavigator require('../../../../pages/Travel/TravelDotLinkWebview').default, [SCREENS.TRAVEL.DYNAMIC_TCS]: () => require('../../../../pages/Travel/DynamicTravelTerms').default, [SCREENS.TRAVEL.DYNAMIC_UPGRADE]: () => require('../../../../pages/Travel/DynamicTravelUpgrade').default, - [SCREENS.TRAVEL.TRIP_SUMMARY]: () => require('../../../../pages/Travel/TripSummaryPage').default, - [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, + [SCREENS.TRAVEL.DYNAMIC_TRIP_SUMMARY]: () => require('../../../../pages/Travel/DynamicTripSummaryPage').default, + [SCREENS.TRAVEL.DYNAMIC_TRIP_DETAILS]: () => require('../../../../pages/Travel/DynamicTripDetailsPage').default, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR]: () => require('../../../../pages/Travel/DynamicDomainSelectorPage').default, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_PERMISSION_INFO]: () => require('../../../../pages/Travel/DynamicDomainPermissionInfoPage').default, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: () => require('../../../../pages/Travel/DynamicPublicDomainErrorPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 615f4a2a45ee..82679e346088 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1921,13 +1921,8 @@ const config: LinkingOptions['config'] = { [SCREENS.TRAVEL.TRAVEL_DOT_LINK_WEB_VIEW]: ROUTES.TRAVEL_DOT_LINK_WEB_VIEW.route, [SCREENS.TRAVEL.DYNAMIC_UPGRADE]: DYNAMIC_ROUTES.TRAVEL_UPGRADE.path, [SCREENS.TRAVEL.DYNAMIC_TCS]: DYNAMIC_ROUTES.TRAVEL_TCS.path, - [SCREENS.TRAVEL.TRIP_SUMMARY]: ROUTES.TRAVEL_TRIP_SUMMARY.route, - [SCREENS.TRAVEL.TRIP_DETAILS]: { - path: ROUTES.TRAVEL_TRIP_DETAILS.route, - parse: { - reservationIndex: (reservationIndex: string) => parseInt(reservationIndex, 10), - }, - }, + [SCREENS.TRAVEL.DYNAMIC_TRIP_SUMMARY]: DYNAMIC_ROUTES.TRAVEL_TRIP_SUMMARY.path, + [SCREENS.TRAVEL.DYNAMIC_TRIP_DETAILS]: DYNAMIC_ROUTES.TRAVEL_TRIP_DETAILS.path, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_SELECTOR]: DYNAMIC_ROUTES.TRAVEL_DOMAIN_SELECTOR.path, [SCREENS.TRAVEL.DYNAMIC_DOMAIN_PERMISSION_INFO]: DYNAMIC_ROUTES.TRAVEL_DOMAIN_PERMISSION_INFO.path, [SCREENS.TRAVEL.DYNAMIC_PUBLIC_DOMAIN_ERROR]: DYNAMIC_ROUTES.TRAVEL_PUBLIC_DOMAIN_ERROR.path, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 68eb78c6238d..8f287de53998 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2615,19 +2615,15 @@ type TravelNavigatorParamList = { isTestAccount?: string; redirectUrl?: string; }; - [SCREENS.TRAVEL.TRIP_SUMMARY]: { + [SCREENS.TRAVEL.DYNAMIC_TRIP_SUMMARY]: { reportID: string; transactionID: 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?: string; }; - [SCREENS.TRAVEL.TRIP_DETAILS]: { + [SCREENS.TRAVEL.DYNAMIC_TRIP_DETAILS]: { reportID: string; transactionID: string; - sequenceIndex: number; + sequenceIndex: string; pnr: 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?: string; }; [SCREENS.TRAVEL.DYNAMIC_TCS]: { domain?: string; diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/DynamicTripDetailsPage.tsx similarity index 93% rename from src/pages/Travel/TripDetailsPage.tsx rename to src/pages/Travel/DynamicTripDetailsPage.tsx index af373c201276..f987b947eb5d 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/DynamicTripDetailsPage.tsx @@ -6,6 +6,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -15,6 +16,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import Navigation from '@libs/Navigation/Navigation'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import {getTripIDFromTransactionParentReportID} from '@libs/ReportUtils'; import {formatCancelledDescription, getReservationDetailsFromSequence, getReservationsFromTripReport} from '@libs/TripReservationUtils'; @@ -22,6 +24,7 @@ import {openTravelDotLink} from '@userActions/Link'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; @@ -34,9 +37,9 @@ function pickTravelerPersonalDetails(personalDetails: OnyxEntry personalDetail?.login === reservation?.travelerPersonalInfo?.email); } -type TripDetailsPageProps = StackScreenProps; +type DynamicTripDetailsPageProps = StackScreenProps; -function TripDetailsPage({route}: TripDetailsPageProps) { +function DynamicTripDetailsPage({route}: DynamicTripDetailsPageProps) { const icons = useMemoizedLazyExpensifyIcons([ 'NewWindow', 'Plane', @@ -58,6 +61,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const {isBetaEnabled} = usePermissions(); const isBlockedFromSpotnanaTravel = isBetaEnabled(CONST.BETAS.PREVENT_SPOTNANA_TRAVEL); const {isOffline} = useNetwork(); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.TRAVEL_TRIP_DETAILS.path); const [isModifyTripLoading, setIsModifyTripLoading] = useState(false); const [isTripSupportLoading, setIsTripSupportLoading] = useState(false); @@ -83,7 +87,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { includeSafeAreaPaddingBottom shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID="TripDetailsPage" + testID="DynamicTripDetailsPage" shouldShowOfflineIndicatorInWideScreen > Navigation.goBack(backPath)} /> {!!reservation && reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( @@ -164,4 +169,4 @@ function TripDetailsPage({route}: TripDetailsPageProps) { ); } -export default TripDetailsPage; +export default DynamicTripDetailsPage; diff --git a/src/pages/Travel/TripSummaryPage.tsx b/src/pages/Travel/DynamicTripSummaryPage.tsx similarity index 80% rename from src/pages/Travel/TripSummaryPage.tsx rename to src/pages/Travel/DynamicTripSummaryPage.tsx index 3f3ae47cffdc..e5904a56ab92 100644 --- a/src/pages/Travel/TripSummaryPage.tsx +++ b/src/pages/Travel/DynamicTripSummaryPage.tsx @@ -6,19 +6,23 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {ReservationView} from '@components/ReportActionItem/TripDetailsView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import Navigation from '@libs/Navigation/Navigation'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import CONFIG from '@src/CONFIG'; import * as TripReservationUtils from '@src/libs/TripReservationUtils'; import ONYXKEYS from '@src/ONYXKEYS'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type TripSummaryPageProps = StackScreenProps; +type DynamicTripSummaryPageProps = StackScreenProps; -function TripSummaryPage({route}: TripSummaryPageProps) { +function DynamicTripSummaryPage({route}: DynamicTripSummaryPageProps) { const {translate} = useLocalize(); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.TRAVEL_TRIP_SUMMARY.path); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(route.params.transactionID)}`); @@ -29,7 +33,7 @@ function TripSummaryPage({route}: TripSummaryPageProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID="TripSummaryPage" + testID="DynamicTripSummaryPage" shouldShowOfflineIndicatorInWideScreen > Navigation.goBack(backPath)} /> {reservationsData.map(({reservation, transactionID, sequenceIndex, isCancelled}) => { @@ -60,4 +65,4 @@ function TripSummaryPage({route}: TripSummaryPageProps) { ); } -export default TripSummaryPage; +export default DynamicTripSummaryPage; diff --git a/src/pages/home/UpcomingTravelSection/UpcomingTravelItem.tsx b/src/pages/home/UpcomingTravelSection/UpcomingTravelItem.tsx index a676f90674a5..f96009470c83 100644 --- a/src/pages/home/UpcomingTravelSection/UpcomingTravelItem.tsx +++ b/src/pages/home/UpcomingTravelSection/UpcomingTravelItem.tsx @@ -10,11 +10,12 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {formatCancelledDescription, getTripReservationIcon} from '@libs/TripReservationUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Reservation} from '@src/types/onyx/Transaction'; import type {UpcomingReservation} from './useUpcomingTravelReservations'; @@ -101,7 +102,12 @@ function UpcomingTravelItem({reservation: upcomingReservation}: UpcomingTravelIt const subtitle = typeId ? `${relativeTime} ${CONST.DOT_SEPARATOR} ${typeId}` : relativeTime; const handlePress = () => { - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, reservation.reservationID, sequenceIndex)); + if (!reportID || !transactionID || !reservation.reservationID) { + return; + } + Navigation.navigate( + createDynamicRoute(DYNAMIC_ROUTES.TRAVEL_TRIP_DETAILS.getRoute(transactionID, String(reservation.reservationID), sequenceIndex), ROUTES.REPORT_WITH_ID.getRoute(reportID)), + ); }; return ( From 0546cdc367aca93ff2d96c43f23e056020a21082 Mon Sep 17 00:00:00 2001 From: "huult (via MelvinBot)" Date: Wed, 17 Jun 2026 04:26:57 +0000 Subject: [PATCH 6/6] Heal eslint seatbelt for migrated TRAVEL_TRIP_SUMMARY and TRAVEL_TRIP_DETAILS routes Co-authored-by: huult --- config/eslint/eslint.seatbelt.tsv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/eslint/eslint.seatbelt.tsv b/config/eslint/eslint.seatbelt.tsv index 84475056f668..8b0b7dbe9490 100644 --- a/config/eslint/eslint.seatbelt.tsv +++ b/config/eslint/eslint.seatbelt.tsv @@ -68,7 +68,7 @@ "../../src/Expensify.tsx" "react-hooks/set-state-in-effect" 1 "../../src/GlobalModals.tsx" "no-restricted-syntax" 2 "../../src/ONYXKEYS.ts" "no-restricted-syntax" 2 -"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 125 +"../../src/ROUTES.ts" "@typescript-eslint/no-deprecated/getUrlWithBackToParam" 123 "../../src/ROUTES.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 "../../src/TIMEZONES.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 "../../src/components/AccountingConnectionConfirmationModal.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1