From 8c3091c168c202ec0b15c4176b0ac371d0883870 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 15 Jan 2025 12:21:17 +0100 Subject: [PATCH 01/16] Make isLoading be always boolean to prevent extra calls of the useEffect --- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 635ff33dbda6..94e745eb863e 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -61,12 +61,12 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { }, [policyID, workspaceAccountID]); const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && (!cardFeeds || (cardFeeds.isLoading && !cardsList)); + const isLoading = !isOffline && (!cardFeeds || (!!cardFeeds.isLoading && !cardsList)); useFocusEffect(fetchCompanyCards); useEffect(() => { - if (!!isLoading || !selectedFeed || isPending) { + if (isLoading || !selectedFeed || isPending) { return; } From 40daa37158e9b817721846d36c00c400ac6fa93a Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 15 Jan 2025 16:40:19 +0100 Subject: [PATCH 02/16] Use cards lastScrapeResult to check if connection is broken --- src/libs/PolicyUtils.ts | 5 -- src/libs/actions/CompanyCards.ts | 52 ++++++++++++------- src/pages/workspace/WorkspaceInitialPage.tsx | 8 +-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 41 ++++++++------- ...WorkspaceCompanyCardsListHeaderButtons.tsx | 10 ++-- .../WorkspaceCompanyCardsPage.tsx | 4 +- src/types/onyx/Card.ts | 3 ++ src/types/onyx/CardFeeds.ts | 6 --- 8 files changed, 74 insertions(+), 55 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 4b35152c7677..acdfb849be0d 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1146,10 +1146,6 @@ function getWorkflowApprovalsUnavailable(policy: OnyxEntry) { return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL || !!policy?.errorFields?.approvalMode; } -function hasPolicyFeedsError(feeds: Record, feedToSkip?: string): boolean { - return Object.entries(feeds).filter(([feedName, feedData]) => feedName !== feedToSkip && !!feedData.errors).length > 0; -} - function getAllPoliciesLength() { return Object.keys(allPolicies ?? {}).length; } @@ -1233,7 +1229,6 @@ export { goBackFromInvalidPolicy, hasAccountingConnections, shouldShowSyncError, - hasPolicyFeedsError, shouldShowCustomUnitsError, shouldShowEmployeeListError, hasIntegrationAutoSync, diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index d0e19398c257..8089fea985fd 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import type {OnyxUpdate} from 'react-native-onyx'; +import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import * as API from '@libs/API'; import type { AssignCompanyCardParams, @@ -18,10 +18,11 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Card} from '@src/types/onyx'; +import type {Card, WorkspaceCardsList} from '@src/types/onyx'; import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard'; import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed} from '@src/types/onyx/CardFeeds'; import type {OnyxData} from '@src/types/onyx/Request'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AddNewCompanyCardFlowData = { /** Step to be set in Onyx */ @@ -374,8 +375,6 @@ function unassignWorkspaceCompanyCard(workspaceAccountID: number, bankName: stri function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, bankName: CompanyCardFeed) { const authToken = NetworkStore.getAuthToken(); - const optimisticFeedUpdates = {[bankName]: {errors: null}}; - const failureFeedUpdates = {[bankName]: {errors: {error: CONST.COMPANY_CARDS.CONNECTION_ERROR}}}; const optimisticData: OnyxUpdate[] = [ { @@ -408,13 +407,6 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, - value: { - settings: {companyCards: optimisticFeedUpdates}, - }, - }, ]; const finallyData: OnyxUpdate[] = [ @@ -475,13 +467,6 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, - value: { - settings: {companyCards: failureFeedUpdates}, - }, - }, ]; const parameters = { @@ -711,6 +696,35 @@ function openPolicyCompanyCardsFeed(policyID: string, feed: CompanyCardFeed) { API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters); } +function flatAllCardsList(allCardsList: OnyxCollection): Record | undefined { + if (!allCardsList) { + return; + } + + let cards: Record = {}; + + Object.values(allCardsList).forEach((allCards) => { + const {cardList, ...feedCards} = allCards ?? {}; + cards = {...cards, ...feedCards}; + }); + + return cards; +} + +function checkIfFeedConnectionIsBroken(feedCards: Record | undefined, feedToExclude?: string): boolean { + if (!feedCards || isEmptyObject(feedCards)) { + return false; + } + + return !!Object.values(feedCards).find((card) => { + if (card.bank === feedToExclude) { + return false; + } + + return card.lastScrapeResult !== 200; + }); +} + export { setWorkspaceCompanyCardFeedName, deleteWorkspaceCompanyCardFeed, @@ -728,4 +742,6 @@ export { clearAddNewCardFlow, setAssignCardStepAndData, clearAssignCardStepAndData, + checkIfFeedConnectionIsBroken, + flatAllCardsList, }; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 2a9b77551c0f..287c9b5aaab8 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -21,7 +21,6 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {isConnectionInProgress} from '@libs/actions/connections'; -import * as CardUtils from '@libs/CardUtils'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; @@ -30,6 +29,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar, getIcons, getPolicyExpenseChat, getReportName, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; import type {FullScreenNavigatorParamList} from '@navigation/types'; import * as App from '@userActions/App'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; import * as Policy from '@userActions/Policy/Policy'; import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; @@ -89,7 +89,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policy?.id); const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && !isEmptyObject(policy.errors)); - const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); + const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`); const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`); @@ -212,14 +212,14 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac } if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]) { - const hasPolicyFeedsError = PolicyUtils.hasPolicyFeedsError(CardUtils.getCompanyFeeds(cardFeeds)); + const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards)); protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.companyCards', icon: Expensicons.CreditCard, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.COMPANY_CARDS, - brickRoadIndicator: hasPolicyFeedsError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: hasBrokenFeedConnection ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }); } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 29d0dfb0c6af..0d60c133ee17 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -19,6 +19,7 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import variables from '@styles/variables'; import * as Card from '@userActions/Card'; import * as CompanyCards from '@userActions/CompanyCards'; +import {checkIfFeedConnectionIsBroken} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -39,28 +40,32 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const {translate} = useLocalize(); const styles = useThemeStyles(); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); + const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const selectedFeed = CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); - const feeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((feed) => ({ - value: feed, - text: CardUtils.getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), - keyForList: feed, - isSelected: feed === selectedFeed, - isDisabled: companyFeeds[feed]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - pendingAction: companyFeeds[feed]?.pendingAction, - brickRoadIndicator: companyFeeds[feed]?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - canShowSeveralIndicators: !!companyFeeds[feed]?.errors, - leftElement: ( - - ), - })); + const feeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((feed) => { + const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feed}`]); + return { + value: feed, + text: CardUtils.getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), + keyForList: feed, + isSelected: feed === selectedFeed, + isDisabled: companyFeeds[feed]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: companyFeeds[feed]?.pendingAction, + brickRoadIndicator: isFeedConnectionBroken ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + canShowSeveralIndicators: isFeedConnectionBroken, + leftElement: ( + + ), + }; + }); const goBack = () => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 0f8893c5bce2..c990e018a541 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -16,6 +16,8 @@ import * as CardUtils from '@libs/CardUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; @@ -41,15 +43,17 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); + const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; const formattedFeedName = CardUtils.getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); const isCustomFeed = CardUtils.isCustomFeed(selectedFeed); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; + const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); return ( @@ -70,7 +74,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS {formattedFeedName} - {PolicyUtils.hasPolicyFeedsError(companyFeeds, selectedFeed) && ( + {checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards), selectedFeed) && ( { CompanyCards.openPolicyCompanyCardsPage(policyID, workspaceAccountID); @@ -149,7 +151,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { cardsList={cardsList} policyID={policyID} handleAssignCard={handleAssignCard} - isDisabledAssignCardButton={!selectedFeedData || !!selectedFeedData?.errors} + isDisabledAssignCardButton={!selectedFeedData || isFeedConnectionBroken} /> )} diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index 7d3d252dd86b..032cc1df9402 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -55,6 +55,9 @@ type Card = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Last updated time */ lastScrape?: string; + /** Last update result */ + lastScrapeResult?: number; + /** Card related error messages */ errors?: OnyxCommon.Errors; diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 9ea014c5007c..0f1f42220318 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -34,9 +34,6 @@ type CustomCardFeedData = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Indicates the day when the statement period for this card ends */ statementPeriodEndDay?: string; - - /** Broken connection errors */ - errors?: OnyxCommon.Errors; }>; /** Direct card feed data */ @@ -55,9 +52,6 @@ type DirectCardFeedData = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Whether any actions are pending */ pending?: boolean; - - /** Broken connection errors */ - errors?: OnyxCommon.Errors; }>; /** Card feed data */ From fc70fd93ff658ebc4cedb59f6647ea4c46fdf26b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 15 Jan 2025 17:05:25 +0100 Subject: [PATCH 03/16] Lint fixes pt1 --- src/libs/PolicyUtils.ts | 1 - ...WorkspaceCompanyCardsListHeaderButtons.tsx | 16 +++++----- .../WorkspaceCompanyCardsPage.tsx | 31 +++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index acdfb849be0d..1e4c834fe64f 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -9,7 +9,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NetSuiteCustomFieldForm'; import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate} from '@src/types/onyx'; -import type {CardFeedData} from '@src/types/onyx/CardFeeds'; import type {ErrorFields, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon'; import type { ConnectionLastSync, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index c990e018a541..9ba46746a327 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -12,8 +12,8 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CardUtils from '@libs/CardUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils'; +import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; @@ -41,13 +41,13 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const {translate} = useLocalize(); const theme = useTheme(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const formattedFeedName = CardUtils.getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); - const isCustomFeed = CardUtils.isCustomFeed(selectedFeed); - const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); + const formattedFeedName = getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); + const isSelectedFeedCustom = isCustomFeed(selectedFeed); + const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); @@ -64,7 +64,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS accessibilityLabel={formattedFeedName ?? ''} > )} - {translate(isCustomFeed ? 'workspace.companyCards.customFeed' : 'workspace.companyCards.directFeed')} + {translate(isSelectedFeedCustom ? 'workspace.companyCards.customFeed' : 'workspace.companyCards.directFeed')} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 3a6d83f2e46b..470031f2d3c8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -8,16 +8,15 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CardUtils from '@libs/CardUtils'; +import {getCompanyFeeds, getFilteredCardList, getSelectedFeed, hasOnlyOneCardToAssign, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; +import {getWorkspaceAccountID, isDeletedPolicyEmployee} from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import * as CompanyCards from '@userActions/CompanyCards'; -import {checkIfFeedConnectionIsBroken} from '@userActions/CompanyCards'; +import {checkIfFeedConnectionIsBroken, openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage, setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -36,10 +35,10 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const styles = useThemeStyles(); const theme = useTheme(); const policyID = route.params.policyID; - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const workspaceAccountID = getWorkspaceAccountID(policyID); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); - const selectedFeed = CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds); + const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); const {cardList, ...cards} = cardsList ?? {}; @@ -48,18 +47,18 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate}); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); - const filteredCardList = CardUtils.getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); + const filteredCardList = getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); - const companyCards = CardUtils.getCompanyFeeds(cardFeeds); + const companyCards = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed && companyCards[selectedFeed]; const isNoFeed = !selectedFeedData; const isPending = !!selectedFeedData?.pending; const isFeedAdded = !isPending && !isNoFeed; - const isFeedExpired = CardUtils.isSelectedFeedExpired(selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); + const isFeedExpired = isSelectedFeedExpired(selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(cards); const fetchCompanyCards = useCallback(() => { - CompanyCards.openPolicyCompanyCardsPage(policyID, workspaceAccountID); + openPolicyCompanyCardsPage(policyID, workspaceAccountID); }, [policyID, workspaceAccountID]); const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); @@ -72,7 +71,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { return; } - CompanyCards.openPolicyCompanyCardsFeed(policyID, selectedFeed); + openPolicyCompanyCardsFeed(policyID, selectedFeed); }, [selectedFeed, isLoading, policyID, isPending]); const handleAssignCard = () => { @@ -88,17 +87,17 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; - const employeeList = Object.values(policy?.employeeList ?? {}).filter((employee) => !PolicyUtils.isDeletedPolicyEmployee(employee, isOffline)); + const employeeList = Object.values(policy?.employeeList ?? {}).filter((employee) => !isDeletedPolicyEmployee(employee, isOffline)); if (employeeList.length === 1) { const userEmail = Object.keys(policy?.employeeList ?? {}).at(0) ?? ''; data.email = userEmail; - const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(userEmail); + const personalDetails = getPersonalDetailByEmail(userEmail); const memberName = personalDetails?.firstName ? personalDetails.firstName : personalDetails?.login; data.cardName = `${memberName}'s card`; currentStep = CONST.COMPANY_CARD.STEP.CARD; - if (CardUtils.hasOnlyOneCardToAssign(filteredCardList)) { + if (hasOnlyOneCardToAssign(filteredCardList)) { currentStep = CONST.COMPANY_CARD.STEP.TRANSACTION_START_DATE; data.cardNumber = Object.keys(filteredCardList).at(0); data.encryptedCardNumber = Object.values(filteredCardList).at(0); @@ -109,7 +108,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { currentStep = CONST.COMPANY_CARD.STEP.BANK_CONNECTION; } - CompanyCards.setAssignCardStepAndData({data, currentStep}); + setAssignCardStepAndData({data, currentStep}); Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD.getRoute(policyID, selectedFeed))); }; From 67cd5257e7f079b73b3b59431ae453e07816884f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 11:46:33 +0100 Subject: [PATCH 04/16] Import updates to fix lint errors --- src/pages/workspace/WorkspaceInitialPage.tsx | 63 +++++++++++-------- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 23 ++++--- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 287c9b5aaab8..3e9c9d5b4bce 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -21,17 +21,29 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {isConnectionInProgress} from '@libs/actions/connections'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import { + shouldShowPolicy as checkIfShouldShowPolicy, + getWorkspaceAccountID, + goBackFromInvalidPolicy, + hasPolicyCategoriesError, + isPaidGroupPolicy, + isPendingDeletePolicy, + isPolicyAdmin, + isPolicyFeatureEnabled, + shouldShowEmployeeListError, + shouldShowSyncError, + shouldShowTaxRateError, +} from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar, getIcons, getPolicyExpenseChat, getReportName, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; import type {FullScreenNavigatorParamList} from '@navigation/types'; -import * as App from '@userActions/App'; +import {confirmReadyToOpenApp} from '@userActions/App'; import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; -import * as Policy from '@userActions/Policy/Policy'; -import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; +import {clearErrors, openPolicyInitialPage, removeWorkspace, updateGeneralSettings} from '@userActions/Policy/Policy'; +import {navigateToBankAccountRoute} from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -76,17 +88,17 @@ type PolicyFeatureStates = Record; function dismissError(policyID: string, pendingAction: PendingAction | undefined) { if (!policyID || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - PolicyUtils.goBackFromInvalidPolicy(); - Policy.removeWorkspace(policyID); + goBackFromInvalidPolicy(); + removeWorkspace(policyID); } else { - Policy.clearErrors(policyID); + clearErrors(policyID); } } function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: WorkspaceInitialPageProps) { const styles = useThemeStyles(); const policy = policyDraft?.id ? policyDraft : policyProp; - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policy?.id); + const workspaceAccountID = getWorkspaceAccountID(policy?.id); const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && !isEmptyObject(policy.errors)); const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`); @@ -97,7 +109,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params?.policyID}`); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const {login, accountID} = useCurrentUserPersonalDetails(); - const hasSyncError = PolicyUtils.shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy)); + const hasSyncError = shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy)); const waitForNavigate = useWaitForNavigation(); const {singleExecution, isExecuting} = useSingleExecution(); const activeRoute = useNavigationState(getTopmostRouteName); @@ -131,7 +143,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac if (policyDraft?.id) { return; } - Policy.openPolicyInitialPage(route.params.policyID); + openPolicyInitialPage(route.params.policyID); }, [policyDraft?.id, route.params.policyID]); useNetwork({onReconnect: fetchPolicyData}); @@ -153,20 +165,19 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac /** Call update workspace currency and hide the modal */ const confirmCurrencyChangeAndHideModal = useCallback(() => { - Policy.updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); + updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); setIsCurrencyModalOpen(false); - ReimbursementAccount.navigateToBankAccountRoute(policyID); + navigateToBankAccountRoute(policyID); }, [policyID, policyName]); - const hasMembersError = PolicyUtils.shouldShowEmployeeListError(policy); - const hasPolicyCategoryError = PolicyUtils.hasPolicyCategoriesError(policyCategories); + const hasMembersError = shouldShowEmployeeListError(policy); + const hasPolicyCategoryError = hasPolicyCategoriesError(policyCategories); const hasGeneralSettingsError = !isEmptyObject(policy?.errorFields?.name ?? {}) || !isEmptyObject(policy?.errorFields?.avatarURL ?? {}) || !isEmptyObject(policy?.errorFields?.ouputCurrency ?? {}) || !isEmptyObject(policy?.errorFields?.address ?? {}); - const shouldShowProtectedItems = PolicyUtils.isPolicyAdmin(policy, login); - const isPaidGroupPolicy = PolicyUtils.isPaidGroupPolicy(policy); + const shouldShowProtectedItems = isPolicyAdmin(policy, login); const [featureStates, setFeatureStates] = useState(policyFeatureStates); const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = []; @@ -177,7 +188,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac setFeatureStates((currentFeatureStates) => { const newFeatureStates = {} as PolicyFeatureStates; (Object.keys(policy?.pendingFields ?? {}) as PolicyFeatureName[]).forEach((key) => { - const isFeatureEnabled = PolicyUtils.isPolicyFeatureEnabled(policy, key); + const isFeatureEnabled = isPolicyFeatureEnabled(policy, key); newFeatureStates[key] = prevPendingFields?.[key] !== policy?.pendingFields?.[key] || isOffline || !policy?.pendingFields?.[key] ? isFeatureEnabled : currentFeatureStates[key]; }); @@ -189,7 +200,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac }, [policy, isOffline, policyFeatureStates, prevPendingFields]); useEffect(() => { - App.confirmReadyToOpenApp(); + confirmReadyToOpenApp(); }, []); if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_DISTANCE_RATES_ENABLED]) { @@ -259,7 +270,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac icon: Expensicons.InvoiceGeneric, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_INVOICES.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.INVOICES, - badgeText: CurrencyUtils.convertToDisplayString(policy?.invoice?.bankAccount?.stripeConnectAccountBalance ?? 0, currencyCode), + badgeText: convertToDisplayString(policy?.invoice?.bankAccount?.stripeConnectAccountBalance ?? 0, currencyCode), }); } @@ -288,7 +299,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac icon: Expensicons.Coins, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_TAXES.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.TAXES, - brickRoadIndicator: PolicyUtils.shouldShowTaxRateError(policy) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: shouldShowTaxRateError(policy) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }); } @@ -333,24 +344,24 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac brickRoadIndicator: hasMembersError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, routeName: SCREENS.WORKSPACE.MEMBERS, }, - ...(isPaidGroupPolicy && shouldShowProtectedItems ? protectedCollectPolicyMenuItems : []), + ...(isPaidGroupPolicy(policy) && shouldShowProtectedItems ? protectedCollectPolicyMenuItems : []), ]; const prevPolicy = usePrevious(policy); const prevProtectedMenuItems = usePrevious(protectedCollectPolicyMenuItems); const enabledItem = protectedCollectPolicyMenuItems.find((curItem) => !prevProtectedMenuItems.some((prevItem) => curItem.routeName === prevItem.routeName)); - const shouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(policy, isOffline, currentUserLogin), [policy, isOffline, currentUserLogin]); - const prevShouldShowPolicy = useMemo(() => PolicyUtils.shouldShowPolicy(prevPolicy, isOffline, currentUserLogin), [prevPolicy, isOffline, currentUserLogin]); + const shouldShowPolicy = useMemo(() => checkIfShouldShowPolicy(policy, isOffline, currentUserLogin), [policy, isOffline, currentUserLogin]); + const prevShouldShowPolicy = useMemo(() => checkIfShouldShowPolicy(prevPolicy, isOffline, currentUserLogin), [prevPolicy, isOffline, currentUserLogin]); // We check shouldShowPolicy and prevShouldShowPolicy to prevent the NotFound view from showing right after we delete the workspace // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = isEmptyObject(policy) || (!shouldShowPolicy && !prevShouldShowPolicy); useEffect(() => { - if (isEmptyObject(prevPolicy) || PolicyUtils.isPendingDeletePolicy(prevPolicy) || !PolicyUtils.isPendingDeletePolicy(policy)) { + if (isEmptyObject(prevPolicy) || isPendingDeletePolicy(prevPolicy) || !isPendingDeletePolicy(policy)) { return; } - PolicyUtils.goBackFromInvalidPolicy(); + goBackFromInvalidPolicy(); }, [policy, prevPolicy]); // We are checking if the user can access the route. diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 0d60c133ee17..980d8e08fb77 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -10,16 +10,15 @@ import RadioListItem from '@components/SelectionList/RadioListItem'; import type {ListItem} from '@components/SelectionList/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CardUtils from '@libs/CardUtils'; +import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, getSelectedFeed} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import variables from '@styles/variables'; -import * as Card from '@userActions/Card'; -import * as CompanyCards from '@userActions/CompanyCards'; -import {checkIfFeedConnectionIsBroken} from '@userActions/CompanyCards'; +import {updateSelectedFeed} from '@userActions/Card'; +import {checkIfFeedConnectionIsBroken, clearAddNewCardFlow} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -35,21 +34,21 @@ type WorkspaceCompanyCardFeedSelectorPageProps = PlatformStackScreenProps { const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feed}`]); return { value: feed, - text: CardUtils.getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), + text: getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), keyForList: feed, isSelected: feed === selectedFeed, isDisabled: companyFeeds[feed]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, @@ -58,7 +57,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS canShowSeveralIndicators: isFeedConnectionBroken, leftElement: ( Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); const selectFeed = (feed: CardFeedListItem) => { - Card.updateSelectedFeed(feed.value, policyID); + updateSelectedFeed(feed.value, policyID); goBack(); }; @@ -100,7 +99,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS title={translate('workspace.companyCards.addCards')} icon={Expensicons.Plus} onPress={() => { - CompanyCards.clearAddNewCardFlow(); + clearAddNewCardFlow(); Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ADD_NEW.getRoute(policyID)); }} /> From eef9e26dc0522119814e603d48ca33141c80d609 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 13:02:15 +0100 Subject: [PATCH 05/16] Change the broken connection message display and add WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route --- src/ROUTES.ts | 9 ++++++ src/SCREENS.ts | 1 + src/components/DotIndicatorMessage.tsx | 22 ------------- .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 ++ src/libs/Navigation/types.ts | 4 +++ ...WorkspaceCompanyCardsListHeaderButtons.tsx | 32 ++++++++++++++----- 8 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2be9368a10b3..761071f4e213 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1247,6 +1247,15 @@ const ROUTES = { return `settings/workspaces/${policyID}/company-cards` as const; }, }, + WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: { + route: 'settings/workspaces/:policyID/company-cards/bank-connection', + getRoute: (policyID: string | undefined, backTo: string) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route'); + } + return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/bank-connection`, backTo); + }, + }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { route: 'settings/workspaces/:policyID/company-cards/add-card-feed', getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards/add-card-feed` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2359324c9b90..afa7231eacf1 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -451,6 +451,7 @@ const SCREENS = { COMPANY_CARDS: 'Workspace_CompanyCards', COMPANY_CARDS_ASSIGN_CARD: 'Workspace_CompanyCards_AssignCard', COMPANY_CARDS_SELECT_FEED: 'Workspace_CompanyCards_Select_Feed', + COMPANY_CARDS_BANK_CONNECTION: 'Workspace_CompanyCards_BankConnection', COMPANY_CARDS_ADD_NEW: 'Workspace_CompanyCards_New', COMPANY_CARDS_TYPE: 'Workspace_CompanyCards_Type', COMPANY_CARDS_INSTRUCTIONS: 'Workspace_CompanyCards_Instructions', diff --git a/src/components/DotIndicatorMessage.tsx b/src/components/DotIndicatorMessage.tsx index d831fca562c3..a103675f1b30 100644 --- a/src/components/DotIndicatorMessage.tsx +++ b/src/components/DotIndicatorMessage.tsx @@ -8,7 +8,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isReceiptError} from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; import * as Localize from '@libs/Localize'; -import CONST from '@src/CONST'; import type {ReceiptError} from '@src/types/onyx/Transaction'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -76,27 +75,6 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles}: DotIndica ); } - if (message === CONST.COMPANY_CARDS.CONNECTION_ERROR) { - return ( - - {Localize.translateLocal('workspace.companyCards.brokenConnectionErrorFirstPart')} - { - // TODO: re-navigate the user to the bank’s website to re-authenticate https://github.com/Expensify/App/issues/50448 - }} - > - {Localize.translateLocal('workspace.companyCards.brokenConnectionErrorLink')} - - - {Localize.translateLocal('workspace.companyCards.brokenConnectionErrorSecondPart')} - - ); - } - return ( require('../../../../pages/workspace/invoices/WorkspaceInvoicingDetailsWebsite').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: () => require('../../../../pages/workspace/companyCards/assignCard/AssignCardFeedPage').default, [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default, + [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: () => require('../../../../pages/workspace/companyCards/addNew/BankConnection').default, [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: () => require('../../../../pages/workspace/companyCards/addNew/AddNewCardPage').default, [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage').default, [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index 2c3b060e0835..7865993d08e9 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -213,6 +213,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.COMPANY_CARDS_NAME, SCREENS.WORKSPACE.COMPANY_CARDS_DETAILS, SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED, + SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION, SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS, SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME, SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index bdd6e58db21c..96b323ef9c05 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -603,6 +603,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: { path: ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.route, }, + [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { + path: ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.route, + }, [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { path: ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 40290552b048..173f5cf16d6a 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -821,6 +821,10 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: { policyID: string; }; + [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { + policyID: string; + backTo: Routes; + }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { policyID: string; bank: CompanyCardFeed; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 9ba46746a327..c55c267359bf 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -5,11 +5,12 @@ import Button from '@components/Button'; import CaretWrapper from '@components/CaretWrapper'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {PressableWithFeedback} from '@components/Pressable'; import Text from '@components/Text'; +import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils'; @@ -17,7 +18,6 @@ import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; @@ -40,6 +40,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); + const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); @@ -52,11 +53,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); return ( - + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))} @@ -104,7 +101,26 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS /> - + {isSelectedFeedConnectionBroken && ( + + + + {translate('workspace.companyCards.brokenConnectionErrorFirstPart')} + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, Navigation.getActiveRoute()))} + > + {translate('workspace.companyCards.brokenConnectionErrorLink')} + + {translate('workspace.companyCards.brokenConnectionErrorSecondPart')} + + + )} + ); } From 291bbb2ef5fea60b982e08c095425ea664842fcf Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 15:04:29 +0100 Subject: [PATCH 06/16] Update BankConnection page to support its direct open --- src/ROUTES.ts | 7 ++++--- src/libs/Navigation/types.ts | 2 ++ src/libs/actions/CompanyCards.ts | 7 ++++++- .../getCompanyCardBankConnection/index.tsx | 9 ++++++--- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 7 ++++--- .../addNew/BankConnection/index.native.tsx | 15 ++++++++++++--- .../companyCards/addNew/BankConnection/index.tsx | 15 ++++++++++++--- src/types/onyx/CardFeeds.ts | 4 ++++ 8 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 761071f4e213..3ef914d8a589 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -5,6 +5,7 @@ import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import Log from './libs/Log'; import type {ExitReason} from './types/form/ExitSurveyReasonForm'; +import type {CompanyCardFeedConnection} from './types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; @@ -1248,12 +1249,12 @@ const ROUTES = { }, }, WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: { - route: 'settings/workspaces/:policyID/company-cards/bank-connection', - getRoute: (policyID: string | undefined, backTo: string) => { + route: 'settings/workspaces/:policyID/company-cards/:bankConnection/bank-connection', + getRoute: (policyID: string | undefined, bankConnection: CompanyCardFeedConnection, backTo: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/bank-connection`, backTo); + return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankConnection as string}/bank-connection`, backTo); }, }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 173f5cf16d6a..2c5f6b45206b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -21,6 +21,7 @@ import type {Route as ExpensifyRoute, HybridAppRoute, Route as Routes} from '@sr 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 {CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from '@src/types/onyx/Policy'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -823,6 +824,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { policyID: string; + bankConnection: CompanyCardFeedConnection; backTo: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 8089fea985fd..1ab95240d943 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -20,7 +20,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Card, WorkspaceCardsList} from '@src/types/onyx'; import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard'; -import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed} from '@src/types/onyx/CardFeeds'; +import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed, CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -725,6 +725,10 @@ function checkIfFeedConnectionIsBroken(feedCards: Record | undefin }); } +function getBankConnectionFromFeed(feed: CompanyCardFeed): CompanyCardFeedConnection | undefined { + return Object.values(CONST.COMPANY_CARDS.BANK_CONNECTIONS).find((connection) => feed.includes(connection)); +} + export { setWorkspaceCompanyCardFeedName, deleteWorkspaceCompanyCardFeed, @@ -743,5 +747,6 @@ export { setAssignCardStepAndData, clearAssignCardStepAndData, checkIfFeedConnectionIsBroken, + getBankConnectionFromFeed, flatAllCardsList, }; diff --git a/src/libs/actions/getCompanyCardBankConnection/index.tsx b/src/libs/actions/getCompanyCardBankConnection/index.tsx index fb6dd9943972..9db969c15d8b 100644 --- a/src/libs/actions/getCompanyCardBankConnection/index.tsx +++ b/src/libs/actions/getCompanyCardBankConnection/index.tsx @@ -2,6 +2,7 @@ import {getApiRoot} from '@libs/ApiUtils'; import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; +import type {CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; type CompanyCardBankConnection = { authToken: string; @@ -11,24 +12,26 @@ type CompanyCardBankConnection = { isNewDot: string; }; -export default function getCompanyCardBankConnection(policyID?: string, bankName?: string, scrapeMinDate?: string) { +export default function getCompanyCardBankConnection(policyID?: string, bankName?: string, bankConnectionFromRoute?: CompanyCardFeedConnection) { const bankConnection = Object.keys(CONST.COMPANY_CARDS.BANKS).find((key) => CONST.COMPANY_CARDS.BANKS[key as keyof typeof CONST.COMPANY_CARDS.BANKS] === bankName); + const bank = bankConnectionFromRoute ?? CONST.COMPANY_CARDS.BANK_CONNECTIONS[bankConnection as keyof typeof CONST.COMPANY_CARDS.BANK_CONNECTIONS]; if (!bankName || !bankConnection || !policyID) { return null; } + const authToken = NetworkStore.getAuthToken(); const params: CompanyCardBankConnection = { authToken: authToken ?? '', isNewDot: 'true', domainName: PolicyUtils.getDomainNameForPolicy(policyID), isCorporate: 'true', - scrapeMinDate: scrapeMinDate ?? '', + scrapeMinDate: '', }; const commandURL = getApiRoot({ shouldSkipWebProxy: true, command: '', }); - const bank = CONST.COMPANY_CARDS.BANK_CONNECTIONS[bankConnection as keyof typeof CONST.COMPANY_CARDS.BANK_CONNECTIONS]; + return `${commandURL}partners/banks/${bank}/oauth_callback.php?${new URLSearchParams(params).toString()}`; } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index c55c267359bf..4db63db16858 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -17,7 +17,7 @@ import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustom import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; -import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList, getBankConnectionFromFeed} from '@userActions/CompanyCards'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; @@ -50,6 +50,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const isSelectedFeedCustom = isCustomFeed(selectedFeed); const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; + const bankConnection = getBankConnectionFromFeed(selectedFeed); const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); return ( @@ -101,7 +102,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS /> - {isSelectedFeedConnectionBroken && ( + {isSelectedFeedConnectionBroken && !!bankConnection && ( {translate('workspace.companyCards.brokenConnectionErrorFirstPart')} Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, bankConnection, Navigation.getActiveRoute()))} > {translate('workspace.companyCards.brokenConnectionErrorLink')} diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx index 76bf9b2bd967..38471c547fe9 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx @@ -17,17 +17,21 @@ import {checkIfNewFeedConnected} from '@libs/CardUtils'; import getUAForWebView from '@libs/getUAForWebView'; import Navigation from '@libs/Navigation/Navigation'; import {getWorkspaceAccountID} from '@libs/PolicyUtils'; +import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@navigation/types'; import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import getCompanyCardBankConnection from '@userActions/getCompanyCardBankConnection'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; type BankConnectionStepProps = { policyID?: string; + route?: PlatformStackRouteProp; }; -function BankConnection({policyID}: BankConnectionStepProps) { +function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStepProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); @@ -35,8 +39,10 @@ function BankConnection({policyID}: BankConnectionStepProps) { const [session] = useOnyx(ONYXKEYS.SESSION); const authToken = session?.authToken ?? null; const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const {bankConnection, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; + const policyID = policyIDFromProps ?? policyIDFromRoute; const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; - const url = getCompanyCardBankConnection(policyID, bankName); + const url = getCompanyCardBankConnection(policyID, bankName, bankConnection); const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const [isConnectionCompleted, setConnectionCompleted] = useState(false); @@ -46,6 +52,9 @@ function BankConnection({policyID}: BankConnectionStepProps) { const renderLoading = () => ; const handleBackButtonPress = () => { + if (backTo) { + Navigation.goBack(backTo); + } if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); return; @@ -85,7 +94,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { shouldEnableMaxHeight > diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index e7e71d1c3785..770c5c47f9df 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -13,31 +13,37 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; +import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@navigation/types'; import * as Card from '@userActions/Card'; import * as CompanyCards from '@userActions/CompanyCards'; import getCompanyCardBankConnection from '@userActions/getCompanyCardBankConnection'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import openBankConnection from './openBankConnection'; let customWindow: Window | null = null; type BankConnectionStepProps = { policyID?: string; + route?: PlatformStackRouteProp; }; -function BankConnection({policyID}: BankConnectionStepProps) { +function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStepProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const {bankConnection, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; + const policyID = policyIDFromProps ?? policyIDFromRoute; const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); const {isNewFeedConnected, newFeed} = useMemo(() => CardUtils.checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); - const url = getCompanyCardBankConnection(policyID, bankName); + const url = getCompanyCardBankConnection(policyID, bankName, bankConnection); const onOpenBankConnectionFlow = useCallback(() => { if (!url) { @@ -48,6 +54,9 @@ function BankConnection({policyID}: BankConnectionStepProps) { const handleBackButtonPress = () => { customWindow?.close(); + if (backTo) { + Navigation.goBack(backTo); + } if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); return; @@ -84,7 +93,7 @@ function BankConnection({policyID}: BankConnectionStepProps) { return ( ; + export default CardFeeds; export type { AddNewCardFeedStep, @@ -129,4 +132,5 @@ export type { CardFeedProvider, CompanyFeeds, CompanyCardNicknames, + CompanyCardFeedConnection, }; From 76f8777518d823cf7cc935615fb001d66729e541 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 15:09:18 +0100 Subject: [PATCH 07/16] Fix lint errors --- src/components/DotIndicatorMessage.tsx | 8 ++++---- .../addNew/BankConnection/index.tsx | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/DotIndicatorMessage.tsx b/src/components/DotIndicatorMessage.tsx index a103675f1b30..c998c38e96ca 100644 --- a/src/components/DotIndicatorMessage.tsx +++ b/src/components/DotIndicatorMessage.tsx @@ -7,7 +7,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {isReceiptError} from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; -import * as Localize from '@libs/Localize'; +import {translateLocal} from '@libs/Localize'; import type {ReceiptError} from '@src/types/onyx/Transaction'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -60,17 +60,17 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles}: DotIndica key={index} style={styles.offlineFeedback.text} > - {Localize.translateLocal('iou.error.receiptFailureMessage')} + {translateLocal('iou.error.receiptFailureMessage')} { fileDownload(message.source, message.filename); }} > - {Localize.translateLocal('iou.error.saveFileMessage')} + {translateLocal('iou.error.saveFileMessage')} - {Localize.translateLocal('iou.error.loseFileMessage')} + {translateLocal('iou.error.loseFileMessage')} ); } diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index 770c5c47f9df..a77387d40ccd 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -10,13 +10,13 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CardUtils from '@libs/CardUtils'; +import {checkIfNewFeedConnected} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; -import * as Card from '@userActions/Card'; -import * as CompanyCards from '@userActions/CompanyCards'; +import {updateSelectedFeed} from '@userActions/Card'; +import {setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import getCompanyCardBankConnection from '@userActions/getCompanyCardBankConnection'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -38,10 +38,10 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const {bankConnection, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); - const {isNewFeedConnected, newFeed} = useMemo(() => CardUtils.checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); + const {isNewFeedConnected, newFeed} = useMemo(() => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); const url = getCompanyCardBankConnection(policyID, bankName, bankConnection); @@ -58,14 +58,14 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep Navigation.goBack(backTo); } if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); return; } if (bankName === CONST.COMPANY_CARDS.BANKS.AMEX) { - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.AMEX_CUSTOM_FEED}); return; } - CompanyCards.setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); + setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_FEED_TYPE}); }; const CustomSubtitle = ( @@ -82,7 +82,7 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep if (isNewFeedConnected) { customWindow?.close(); if (newFeed) { - Card.updateSelectedFeed(newFeed, policyID); + updateSelectedFeed(newFeed, policyID); } Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); return; From 07bfde88c053cd6d07c9f70eacd4479f50781c6d Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 15:16:49 +0100 Subject: [PATCH 08/16] Minor lint fix --- src/libs/actions/CompanyCards.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index e664fe81de65..3676cef8d8bb 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -1,4 +1,4 @@ -import type {OnyxEntry, OnyxUpdate, OnyxCollection} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { From 7749530df103dd7bf24a118fccb350cd4c594b3b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 16:16:56 +0100 Subject: [PATCH 09/16] Fix bank connection --- src/ROUTES.ts | 8 ++++---- src/libs/Navigation/types.ts | 4 ++-- src/libs/actions/CompanyCards.ts | 10 ++++++---- .../actions/getCompanyCardBankConnection/index.tsx | 8 +++----- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 8 ++++---- .../addNew/BankConnection/index.native.tsx | 6 +++--- .../companyCards/addNew/BankConnection/index.tsx | 6 +++--- src/types/onyx/CardFeeds.ts | 6 +++--- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 3ef914d8a589..66dafe6e4ce8 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -5,7 +5,7 @@ import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import Log from './libs/Log'; import type {ExitReason} from './types/form/ExitSurveyReasonForm'; -import type {CompanyCardFeedConnection} from './types/onyx/CardFeeds'; +import type {CompanyCardBankName} from './types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; @@ -1249,12 +1249,12 @@ const ROUTES = { }, }, WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: { - route: 'settings/workspaces/:policyID/company-cards/:bankConnection/bank-connection', - getRoute: (policyID: string | undefined, bankConnection: CompanyCardFeedConnection, backTo: string) => { + route: 'settings/workspaces/:policyID/company-cards/:bankName/bank-connection', + getRoute: (policyID: string | undefined, bankName: CompanyCardBankName, backTo: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankConnection as string}/bank-connection`, backTo); + return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankName as string}/bank-connection`, backTo); }, }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2c5f6b45206b..af8b90acac20 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -21,7 +21,7 @@ import type {Route as ExpensifyRoute, HybridAppRoute, Route as Routes} from '@sr 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 {CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; +import type {CompanyCardBankName} from '@src/types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from '@src/types/onyx/Policy'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -824,7 +824,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { policyID: string; - bankConnection: CompanyCardFeedConnection; + bankName: CompanyCardBankName; backTo: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 3676cef8d8bb..c5ebc5112b5a 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -20,7 +20,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Card, CardFeeds, WorkspaceCardsList} from '@src/types/onyx'; import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard'; -import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed, CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; +import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardBankName, CompanyCardFeed} from '@src/types/onyx/CardFeeds'; import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -754,8 +754,10 @@ function checkIfFeedConnectionIsBroken(feedCards: Record | undefin }); } -function getBankConnectionFromFeed(feed: CompanyCardFeed): CompanyCardFeedConnection | undefined { - return Object.values(CONST.COMPANY_CARDS.BANK_CONNECTIONS).find((connection) => feed.includes(connection)); +function getBankNameByFeed(feed: CompanyCardFeed): CompanyCardBankName | undefined { + const bankConnectionEntry = Object.entries(CONST.COMPANY_CARDS.BANK_CONNECTIONS).find(([, connection]) => feed.includes(connection)); + const bankNameKey = bankConnectionEntry?.at(0); + return bankNameKey ? CONST.COMPANY_CARDS.BANKS[bankNameKey as keyof typeof CONST.COMPANY_CARDS.BANKS] : undefined; } export { @@ -776,6 +778,6 @@ export { setAssignCardStepAndData, clearAssignCardStepAndData, checkIfFeedConnectionIsBroken, - getBankConnectionFromFeed, + getBankNameByFeed, flatAllCardsList, }; diff --git a/src/libs/actions/getCompanyCardBankConnection/index.tsx b/src/libs/actions/getCompanyCardBankConnection/index.tsx index 9db969c15d8b..8cf8e579d798 100644 --- a/src/libs/actions/getCompanyCardBankConnection/index.tsx +++ b/src/libs/actions/getCompanyCardBankConnection/index.tsx @@ -2,7 +2,7 @@ import {getApiRoot} from '@libs/ApiUtils'; import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; -import type {CompanyCardFeedConnection} from '@src/types/onyx/CardFeeds'; +import type {CompanyCardBankName} from '@src/types/onyx/CardFeeds'; type CompanyCardBankConnection = { authToken: string; @@ -12,14 +12,12 @@ type CompanyCardBankConnection = { isNewDot: string; }; -export default function getCompanyCardBankConnection(policyID?: string, bankName?: string, bankConnectionFromRoute?: CompanyCardFeedConnection) { +export default function getCompanyCardBankConnection(policyID?: string, bankName?: CompanyCardBankName) { const bankConnection = Object.keys(CONST.COMPANY_CARDS.BANKS).find((key) => CONST.COMPANY_CARDS.BANKS[key as keyof typeof CONST.COMPANY_CARDS.BANKS] === bankName); - const bank = bankConnectionFromRoute ?? CONST.COMPANY_CARDS.BANK_CONNECTIONS[bankConnection as keyof typeof CONST.COMPANY_CARDS.BANK_CONNECTIONS]; if (!bankName || !bankConnection || !policyID) { return null; } - const authToken = NetworkStore.getAuthToken(); const params: CompanyCardBankConnection = { authToken: authToken ?? '', @@ -32,6 +30,6 @@ export default function getCompanyCardBankConnection(policyID?: string, bankName shouldSkipWebProxy: true, command: '', }); - + const bank = CONST.COMPANY_CARDS.BANK_CONNECTIONS[bankConnection as keyof typeof CONST.COMPANY_CARDS.BANK_CONNECTIONS]; return `${commandURL}partners/banks/${bank}/oauth_callback.php?${new URLSearchParams(params).toString()}`; } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 4db63db16858..dd6c7cc85d35 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -17,7 +17,7 @@ import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustom import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; -import {checkIfFeedConnectionIsBroken, flatAllCardsList, getBankConnectionFromFeed} from '@userActions/CompanyCards'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList, getBankNameByFeed} from '@userActions/CompanyCards'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; @@ -50,7 +50,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const isSelectedFeedCustom = isCustomFeed(selectedFeed); const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; - const bankConnection = getBankConnectionFromFeed(selectedFeed); + const bankName = getBankNameByFeed(selectedFeed); const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); return ( @@ -102,7 +102,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS /> - {isSelectedFeedConnectionBroken && !!bankConnection && ( + {isSelectedFeedConnectionBroken && !!bankName && ( {translate('workspace.companyCards.brokenConnectionErrorFirstPart')} Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, bankConnection, Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BANK_CONNECTION.getRoute(policyID, bankName, Navigation.getActiveRoute()))} > {translate('workspace.companyCards.brokenConnectionErrorLink')} diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx index 38471c547fe9..d471d23b78fa 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx @@ -39,10 +39,10 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const [session] = useOnyx(ONYXKEYS.SESSION); const authToken = session?.authToken ?? null; const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); - const {bankConnection, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; + const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; - const url = getCompanyCardBankConnection(policyID, bankName, bankConnection); + const bankName: ValueOf | undefined = bankNameFromRoute ?? addNewCard?.data?.selectedBank; + const url = getCompanyCardBankConnection(policyID, bankName); const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const [isConnectionCompleted, setConnectionCompleted] = useState(false); diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index a77387d40ccd..69e5155e92c9 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -35,15 +35,15 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const styles = useThemeStyles(); const {translate} = useLocalize(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); - const {bankConnection, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; + const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName: ValueOf | undefined = addNewCard?.data?.selectedBank; + const bankName: ValueOf | undefined = bankNameFromRoute ?? addNewCard?.data?.selectedBank; const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); const {isNewFeedConnected, newFeed} = useMemo(() => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}), [cardFeeds, prevFeedsData]); - const url = getCompanyCardBankConnection(policyID, bankName, bankConnection); + const url = getCompanyCardBankConnection(policyID, bankName); const onOpenBankConnectionFlow = useCallback(() => { if (!url) { diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 806fc93069f8..4527b13f046a 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -120,8 +120,8 @@ type AddNewCompanyCardFeed = { isEditing: boolean; }; -/** Card feed connection */ -type CompanyCardFeedConnection = ValueOf; +/** Card feed name */ +type CompanyCardBankName = ValueOf; export default CardFeeds; export type { @@ -135,6 +135,6 @@ export type { CardFeedProvider, CompanyFeeds, CompanyCardNicknames, - CompanyCardFeedConnection, + CompanyCardBankName, CompanyCardFeedWithNumber, }; From fc686e02f9a5b3e0d0739bb0e6663beaadb7417c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 16:29:53 +0100 Subject: [PATCH 10/16] Reuse existing getBankName method --- src/ROUTES.ts | 5 ++--- src/libs/Navigation/types.ts | 3 +-- src/libs/actions/CompanyCards.ts | 9 +-------- src/libs/actions/getCompanyCardBankConnection/index.tsx | 3 +-- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 6 +++--- .../companyCards/addNew/BankConnection/index.native.tsx | 2 +- .../companyCards/addNew/BankConnection/index.tsx | 2 +- src/types/onyx/CardFeeds.ts | 4 ---- 8 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 66dafe6e4ce8..000f67113de9 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -5,7 +5,6 @@ import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import Log from './libs/Log'; import type {ExitReason} from './types/form/ExitSurveyReasonForm'; -import type {CompanyCardBankName} from './types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; @@ -1250,11 +1249,11 @@ const ROUTES = { }, WORKSPACE_COMPANY_CARDS_BANK_CONNECTION: { route: 'settings/workspaces/:policyID/company-cards/:bankName/bank-connection', - getRoute: (policyID: string | undefined, bankName: CompanyCardBankName, backTo: string) => { + getRoute: (policyID: string | undefined, bankName: string, backTo: string) => { if (!policyID) { Log.warn('Invalid policyID is used to build the WORKSPACE_COMPANY_CARDS_BANK_CONNECTION route'); } - return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankName as string}/bank-connection`, backTo); + return getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bankName}/bank-connection`, backTo); }, }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index af8b90acac20..ee681c3ff9d1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -21,7 +21,6 @@ import type {Route as ExpensifyRoute, HybridAppRoute, Route as Routes} from '@sr 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 {CompanyCardBankName} from '@src/types/onyx/CardFeeds'; import type {ConnectionName, SageIntacctMappingName} from '@src/types/onyx/Policy'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -824,7 +823,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { policyID: string; - bankName: CompanyCardBankName; + bankName: string; backTo: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index c5ebc5112b5a..68e2d9899bc5 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -20,7 +20,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Card, CardFeeds, WorkspaceCardsList} from '@src/types/onyx'; import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard'; -import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardBankName, CompanyCardFeed} from '@src/types/onyx/CardFeeds'; +import type {AddNewCardFeedData, AddNewCardFeedStep, CompanyCardFeed} from '@src/types/onyx/CardFeeds'; import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -754,12 +754,6 @@ function checkIfFeedConnectionIsBroken(feedCards: Record | undefin }); } -function getBankNameByFeed(feed: CompanyCardFeed): CompanyCardBankName | undefined { - const bankConnectionEntry = Object.entries(CONST.COMPANY_CARDS.BANK_CONNECTIONS).find(([, connection]) => feed.includes(connection)); - const bankNameKey = bankConnectionEntry?.at(0); - return bankNameKey ? CONST.COMPANY_CARDS.BANKS[bankNameKey as keyof typeof CONST.COMPANY_CARDS.BANKS] : undefined; -} - export { setWorkspaceCompanyCardFeedName, deleteWorkspaceCompanyCardFeed, @@ -778,6 +772,5 @@ export { setAssignCardStepAndData, clearAssignCardStepAndData, checkIfFeedConnectionIsBroken, - getBankNameByFeed, flatAllCardsList, }; diff --git a/src/libs/actions/getCompanyCardBankConnection/index.tsx b/src/libs/actions/getCompanyCardBankConnection/index.tsx index 8cf8e579d798..a136a3783136 100644 --- a/src/libs/actions/getCompanyCardBankConnection/index.tsx +++ b/src/libs/actions/getCompanyCardBankConnection/index.tsx @@ -2,7 +2,6 @@ import {getApiRoot} from '@libs/ApiUtils'; import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; -import type {CompanyCardBankName} from '@src/types/onyx/CardFeeds'; type CompanyCardBankConnection = { authToken: string; @@ -12,7 +11,7 @@ type CompanyCardBankConnection = { isNewDot: string; }; -export default function getCompanyCardBankConnection(policyID?: string, bankName?: CompanyCardBankName) { +export default function getCompanyCardBankConnection(policyID?: string, bankName?: string) { const bankConnection = Object.keys(CONST.COMPANY_CARDS.BANKS).find((key) => CONST.COMPANY_CARDS.BANKS[key as keyof typeof CONST.COMPANY_CARDS.BANKS] === bankName); if (!bankName || !bankConnection || !policyID) { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index dd6c7cc85d35..3319fdea61fa 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -13,11 +13,11 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils'; +import {getBankName, getCardFeedIcon, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils'; import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; -import {checkIfFeedConnectionIsBroken, flatAllCardsList, getBankNameByFeed} from '@userActions/CompanyCards'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList} from '@userActions/CompanyCards'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; @@ -50,7 +50,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const isSelectedFeedCustom = isCustomFeed(selectedFeed); const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; - const bankName = getBankNameByFeed(selectedFeed); + const bankName = getBankName(selectedFeed); const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`]); return ( diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx index d471d23b78fa..5e2b2f36428d 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx @@ -41,7 +41,7 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName: ValueOf | undefined = bankNameFromRoute ?? addNewCard?.data?.selectedBank; + const bankName = bankNameFromRoute ?? addNewCard?.data?.selectedBank; const url = getCompanyCardBankConnection(policyID, bankName); const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index 69e5155e92c9..71d76a74df67 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -37,7 +37,7 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName: ValueOf | undefined = bankNameFromRoute ?? addNewCard?.data?.selectedBank; + const bankName = bankNameFromRoute ?? addNewCard?.data?.selectedBank; const workspaceAccountID = getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 4527b13f046a..4886e35ebfce 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -120,9 +120,6 @@ type AddNewCompanyCardFeed = { isEditing: boolean; }; -/** Card feed name */ -type CompanyCardBankName = ValueOf; - export default CardFeeds; export type { AddNewCardFeedStep, @@ -135,6 +132,5 @@ export type { CardFeedProvider, CompanyFeeds, CompanyCardNicknames, - CompanyCardBankName, CompanyCardFeedWithNumber, }; From 40fc52cfc2e35d6a4cc27de27bbd138582e21618 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 16:32:27 +0100 Subject: [PATCH 11/16] Lint fix --- .../companyCards/addNew/BankConnection/index.native.tsx | 1 - src/pages/workspace/companyCards/addNew/BankConnection/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx index 5e2b2f36428d..3a0f2f506676 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx @@ -3,7 +3,6 @@ import {ActivityIndicator} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {WebViewNavigation} from 'react-native-webview'; import {WebView} from 'react-native-webview'; -import type {ValueOf} from 'type-fest'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index 71d76a74df67..328e3bc3ff77 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -1,6 +1,5 @@ import React, {useCallback, useEffect, useMemo} from 'react'; import {useOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; import BlockingView from '@components/BlockingViews/BlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; From 16b2397a0a38b543494230d07d269a400d058e51 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 16 Jan 2025 17:09:03 +0100 Subject: [PATCH 12/16] Fix all cards processing --- src/libs/actions/CompanyCards.ts | 10 ++++++---- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 68e2d9899bc5..333721693708 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -725,16 +725,18 @@ function openPolicyCompanyCardsFeed(policyID: string, feed: CompanyCardFeed) { API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters); } -function flatAllCardsList(allCardsList: OnyxCollection): Record | undefined { +function flatAllCardsList(allCardsList: OnyxCollection, workspaceAccountID: number): Record | undefined { if (!allCardsList) { return; } let cards: Record = {}; - Object.values(allCardsList).forEach((allCards) => { - const {cardList, ...feedCards} = allCards ?? {}; - cards = {...cards, ...feedCards}; + Object.entries(allCardsList).forEach(([key, allCards]) => { + if (key.includes(workspaceAccountID.toString()) && !key.includes(CONST.EXPENSIFY_CARD.BANK)) { + const {cardList, ...feedCards} = allCards ?? {}; + cards = {...cards, ...feedCards}; + } }); return cards; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 3e9c9d5b4bce..163fa60cb1fd 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -223,7 +223,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac } if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]) { - const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards)); + const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, workspaceAccountID)); protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.companyCards', diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 3319fdea61fa..899225567dbc 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -72,7 +72,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS {formattedFeedName} - {checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards), selectedFeed) && ( + {checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, workspaceAccountID), selectedFeed) && ( Date: Thu, 16 Jan 2025 17:45:18 +0100 Subject: [PATCH 13/16] Lint fix --- src/libs/actions/CompanyCards.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 333721693708..9ae1066faafa 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -733,10 +733,12 @@ function flatAllCardsList(allCardsList: OnyxCollection, work let cards: Record = {}; Object.entries(allCardsList).forEach(([key, allCards]) => { - if (key.includes(workspaceAccountID.toString()) && !key.includes(CONST.EXPENSIFY_CARD.BANK)) { - const {cardList, ...feedCards} = allCards ?? {}; - cards = {...cards, ...feedCards}; + if (!key.includes(workspaceAccountID.toString()) || key.includes(CONST.EXPENSIFY_CARD.BANK)) { + return; } + + const {cardList, ...feedCards} = allCards ?? {}; + cards = {...cards, ...feedCards}; }); return cards; From befa0c39c07d81080c35830873905f0159b3d9bd Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 27 Jan 2025 09:14:01 +0100 Subject: [PATCH 14/16] Code improvements --- src/libs/actions/CompanyCards.ts | 22 +++++-------------- .../addNew/BankConnection/index.native.tsx | 1 + .../addNew/BankConnection/index.tsx | 1 + 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 9ae1066faafa..8ca1e2ab3852 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -730,18 +730,14 @@ function flatAllCardsList(allCardsList: OnyxCollection, work return; } - let cards: Record = {}; - - Object.entries(allCardsList).forEach(([key, allCards]) => { + return Object.entries(allCardsList).reduce((acc, [key, allCards]) => { if (!key.includes(workspaceAccountID.toString()) || key.includes(CONST.EXPENSIFY_CARD.BANK)) { - return; + return acc; } - const {cardList, ...feedCards} = allCards ?? {}; - cards = {...cards, ...feedCards}; - }); - - return cards; + Object.assign(acc, feedCards); + return acc; + }, {}); } function checkIfFeedConnectionIsBroken(feedCards: Record | undefined, feedToExclude?: string): boolean { @@ -749,13 +745,7 @@ function checkIfFeedConnectionIsBroken(feedCards: Record | undefin return false; } - return !!Object.values(feedCards).find((card) => { - if (card.bank === feedToExclude) { - return false; - } - - return card.lastScrapeResult !== 200; - }); + return Object.values(feedCards).some((card) => card.bank !== feedToExclude && card.lastScrapeResult !== 200); } export { diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx index 3a0f2f506676..903abeb0d22f 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.native.tsx @@ -53,6 +53,7 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep const handleBackButtonPress = () => { if (backTo) { Navigation.goBack(backTo); + return; } if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); diff --git a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx index 25c4e495cd04..fb7731589e84 100644 --- a/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/addNew/BankConnection/index.tsx @@ -56,6 +56,7 @@ function BankConnection({policyID: policyIDFromProps, route}: BankConnectionStep customWindow?.close(); if (backTo) { Navigation.goBack(backTo); + return; } if (bankName === CONST.COMPANY_CARDS.BANKS.BREX) { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); From 3081c3cb5985db133966f5829ca420ecbf6a4772 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 27 Jan 2025 15:31:18 +0100 Subject: [PATCH 15/16] Align dot indicator Co-authored-by: Getabalew <75031127+getusha@users.noreply.github.com> --- .../companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index c593fdda6603..559029db3848 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -103,7 +103,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS {isSelectedFeedConnectionBroken && !!bankName && ( - + Date: Mon, 27 Jan 2025 16:21:09 +0100 Subject: [PATCH 16/16] Add functions description --- src/libs/actions/CompanyCards.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 8ca1e2ab3852..f6e6dbb3729d 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -725,6 +725,12 @@ function openPolicyCompanyCardsFeed(policyID: string, feed: CompanyCardFeed) { API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters); } +/** + * Takes the list of cards divided by workspaces and feeds and returns the flattened non-Expensify cards related to the provided workspace + * + * @param allCardsList the list where cards split by workspaces and feeds and stored under `card_${workspaceAccountID}_${feedName}` keys + * @param workspaceAccountID the workspace account id we want to get cards for + */ function flatAllCardsList(allCardsList: OnyxCollection, workspaceAccountID: number): Record | undefined { if (!allCardsList) { return; @@ -740,6 +746,12 @@ function flatAllCardsList(allCardsList: OnyxCollection, work }, {}); } +/** + * Check if any feed card has a broken connection + * + * @param feedCards the list of the cards, related to one or several feeds + * @param [feedToExclude] the feed to ignore during the check, it's useful for checking broken connection error only in the feeds other than the selected one + */ function checkIfFeedConnectionIsBroken(feedCards: Record | undefined, feedToExclude?: string): boolean { if (!feedCards || isEmptyObject(feedCards)) { return false;