From d18f99b66235cee8266d0ac9d0c1ec691b8898c1 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Jan 2025 13:38:12 +0200 Subject: [PATCH 01/28] Pass policy to Travel.bookATrip --- src/libs/actions/Travel.ts | 17 +++++++---------- src/pages/Search/EmptySearchView.tsx | 10 +++++++++- src/pages/Travel/ManageTrips.tsx | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index 1886885587c4..8605ce924c33 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -14,7 +14,7 @@ import {getAdminsPrivateEmailDomains, getPolicy} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {TravelSettings} from '@src/types/onyx'; +import type {Policy, TravelSettings} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {buildTravelDotURL, openTravelDotLink} from './Link'; @@ -114,22 +114,19 @@ function provisionDomain(domain: string) { Navigation.navigate(ROUTES.TRAVEL_TCS.getRoute(domain)); } -function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessage: Dispatch>, ctaErrorMessage = ''): void { - if (!activePolicyID) { - return; - } +function bookATrip(policy: Policy, translate: LocaleContextProps['translate'], setCtaErrorMessage: Dispatch>, ctaErrorMessage = ''): void { if (Str.isSMSLogin(primaryLogin)) { setCtaErrorMessage(translate('travel.phoneError')); return; } - const policy = getPolicy(activePolicyID); - if (isEmptyObject(policy?.address)) { - Navigation.navigate(ROUTES.WORKSPACE_PROFILE_ADDRESS.getRoute(activePolicyID, Navigation.getActiveRoute())); + + if (isEmptyObject(policy.address)) { + Navigation.navigate(ROUTES.WORKSPACE_PROFILE_ADDRESS.getRoute(policy.id, Navigation.getActiveRoute())); return; } - const isPolicyProvisioned = policy?.travelSettings?.spotnanaCompanyID ?? policy?.travelSettings?.associatedTravelDomainAccountID; - if (policy?.travelSettings?.hasAcceptedTerms ?? (travelSettings?.hasAcceptedTerms && isPolicyProvisioned)) { + const isPolicyProvisioned = policy.travelSettings?.spotnanaCompanyID ?? policy.travelSettings?.associatedTravelDomainAccountID; + if (policy.travelSettings?.hasAcceptedTerms ?? (travelSettings?.hasAcceptedTerms && isPolicyProvisioned)) { openTravelDotLink(activePolicyID) ?.then(() => { if (!NativeModules.HybridAppModule || !isSingleNewDotEntry) { diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index c27edc2e70e2..17b57aeec855 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -60,6 +60,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { const shouldRedirectToExpensifyClassic = useMemo(() => { return areAllGroupPoliciesExpenseChatDisabled((allPolicies as OnyxCollection) ?? {}); }, [allPolicies]); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [ctaErrorMessage, setCtaErrorMessage] = useState(''); @@ -133,7 +134,13 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { buttons: [ { buttonText: translate('search.searchResults.emptyTripResults.buttonText'), - buttonAction: () => bookATrip(translate, setCtaErrorMessage, ctaErrorMessage), + buttonAction: () => { + const activePolicy = ((allPolicies as OnyxCollection) ?? {})[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`]; + if (!activePolicy) { + return; + } + bookATrip(activePolicy, translate, setCtaErrorMessage, ctaErrorMessage); + }, success: true, }, ], @@ -245,6 +252,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { viewTourTaskReport, canModifyTheTask, canActionTheTask, + activePolicyID, ]); return ( diff --git a/src/pages/Travel/ManageTrips.tsx b/src/pages/Travel/ManageTrips.tsx index ac427d1d56c9..bbfd2223b48f 100644 --- a/src/pages/Travel/ManageTrips.tsx +++ b/src/pages/Travel/ManageTrips.tsx @@ -55,7 +55,7 @@ function ManageTrips() { ctaText={translate('travel.bookTravel')} ctaAccessibilityLabel={translate('travel.bookTravel')} onCtaPress={() => { - bookATrip(translate, setCtaErrorMessage, ctaErrorMessage); + bookATrip(policy, translate, setCtaErrorMessage, ctaErrorMessage); }} ctaErrorMessage={ctaErrorMessage} illustration={LottieAnimations.TripsEmptyState} From 9ae5e5599c4ec35fe6c31cd46b381111e6106c32 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Jan 2025 13:47:44 +0200 Subject: [PATCH 02/28] Remove activePolicyID from Travel.ts --- src/libs/actions/Travel.ts | 12 ++---------- src/pages/Search/EmptySearchView.tsx | 3 ++- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index 8605ce924c33..cf0f617a0617 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -10,7 +10,7 @@ import {WRITE_COMMANDS} from '@libs/API/types'; import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import {getAdminsPrivateEmailDomains, getPolicy} from '@libs/PolicyUtils'; +import {getAdminsPrivateEmailDomains} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -26,14 +26,6 @@ Onyx.connect({ }, }); -let activePolicyID: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, - callback: (val) => { - activePolicyID = val; - }, -}); - let primaryLogin: string; Onyx.connect({ key: ONYXKEYS.ACCOUNT, @@ -127,7 +119,7 @@ function bookATrip(policy: Policy, translate: LocaleContextProps['translate'], s const isPolicyProvisioned = policy.travelSettings?.spotnanaCompanyID ?? policy.travelSettings?.associatedTravelDomainAccountID; if (policy.travelSettings?.hasAcceptedTerms ?? (travelSettings?.hasAcceptedTerms && isPolicyProvisioned)) { - openTravelDotLink(activePolicyID) + openTravelDotLink(policy.id) ?.then(() => { if (!NativeModules.HybridAppModule || !isSingleNewDotEntry) { return; diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index 17b57aeec855..6040ae0a63d6 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -135,7 +135,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { { buttonText: translate('search.searchResults.emptyTripResults.buttonText'), buttonAction: () => { - const activePolicy = ((allPolicies as OnyxCollection) ?? {})[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`]; + const activePolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`]; if (!activePolicy) { return; } @@ -252,6 +252,7 @@ function EmptySearchView({type, hasResults}: EmptySearchViewProps) { viewTourTaskReport, canModifyTheTask, canActionTheTask, + allPolicies, activePolicyID, ]); From 1a32608deabb24e640ad162118466e31d04b2295 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Jan 2025 15:11:13 +0200 Subject: [PATCH 03/28] Remove primaryLogin from Travel.ts --- src/libs/actions/Travel.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index cf0f617a0617..8a5a51daa621 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -11,6 +11,7 @@ import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import {getAdminsPrivateEmailDomains} from '@libs/PolicyUtils'; +import {getContactMethod} from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -26,14 +27,6 @@ Onyx.connect({ }, }); -let primaryLogin: string; -Onyx.connect({ - key: ONYXKEYS.ACCOUNT, - callback: (val) => { - primaryLogin = val?.primaryLogin ?? ''; - }, -}); - let isSingleNewDotEntry: boolean | undefined; Onyx.connect({ key: ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, @@ -107,7 +100,7 @@ function provisionDomain(domain: string) { } function bookATrip(policy: Policy, translate: LocaleContextProps['translate'], setCtaErrorMessage: Dispatch>, ctaErrorMessage = ''): void { - if (Str.isSMSLogin(primaryLogin)) { + if (Str.isSMSLogin(getContactMethod())) { setCtaErrorMessage(translate('travel.phoneError')); return; } From bc569f509e83c3ceeeeae7aeb9bbd08a4c22118f Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Jan 2025 23:52:37 +0200 Subject: [PATCH 04/28] Add footer to the FeatureList component --- src/components/FeatureList.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/FeatureList.tsx b/src/components/FeatureList.tsx index 70c56ad5d963..1964faddb4a1 100644 --- a/src/components/FeatureList.tsx +++ b/src/components/FeatureList.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import {View} from 'react-native'; import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -65,6 +65,9 @@ type FeatureListProps = { /** Padding for content on large screens */ contentPaddingOnLargeScreens?: {padding: number}; + + /** Custom content to display in the footer */ + footer?: ReactNode; }; function FeatureList({ @@ -84,6 +87,7 @@ function FeatureList({ illustrationContainerStyle, titleStyles, contentPaddingOnLargeScreens, + footer, }: FeatureListProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -146,6 +150,7 @@ function FeatureList({ success large /> + {!!footer && <>{footer}} ); From d16741c8de67cbed7947efb056fc8ac533959b61 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 29 Jan 2025 00:05:12 +0200 Subject: [PATCH 05/28] Move Travel specific components out of FeatureList --- src/components/BookTravelButton.tsx | 37 +++++++++++++++++++++++++++ src/components/FeatureList.tsx | 39 ++++++++--------------------- src/pages/Travel/ManageTrips.tsx | 17 ++++++++++--- 3 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 src/components/BookTravelButton.tsx diff --git a/src/components/BookTravelButton.tsx b/src/components/BookTravelButton.tsx new file mode 100644 index 000000000000..3362f23b615e --- /dev/null +++ b/src/components/BookTravelButton.tsx @@ -0,0 +1,37 @@ +import React, {useState} from 'react'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Button from './Button'; +import DotIndicatorMessage from './DotIndicatorMessage'; + +type BookTravelButtonProps = { + text: string; +}; + +function BookTravelButton({text}: BookTravelButtonProps) { + const [errorMessage, setErrorMessage] = useState(''); + const styles = useThemeStyles(); + + return ( + <> + {!!errorMessage && ( + + )} +