diff --git a/src/components/AccountSwitcherSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx index 8712539d1799..b558b2fa3558 100644 --- a/src/components/AccountSwitcherSkeletonView/index.tsx +++ b/src/components/AccountSwitcherSkeletonView/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import {Circle, Rect} from 'react-native-svg'; import type {ValueOf} from 'type-fest'; import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader'; @@ -15,9 +16,15 @@ type AccountSwitcherSkeletonViewProps = { /** The size of the avatar */ avatarSize?: ValueOf; + + /** The width of the skeleton view */ + width?: number; + + /** Additional styles for the skeleton view */ + style?: StyleProp; }; -function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.AVATAR_SIZE.DEFAULT}: AccountSwitcherSkeletonViewProps) { +function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.AVATAR_SIZE.DEFAULT, width, style}: AccountSwitcherSkeletonViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -28,11 +35,12 @@ function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.A const rectXTranslation = startPositionX + avatarPlaceholderRadius + styles.gap3.gap; return ( - + ; + } + return ( @@ -32,8 +44,8 @@ function SearchFiltersSkeleton({shouldAnimate = true}: SearchFiltersSkeletonProp transform={[{translateX: index * 90}]} rx={14} ry={14} - width={84} - height={28} + width={width} + height={height} /> ))} @@ -50,8 +62,8 @@ function SearchFiltersSkeleton({shouldAnimate = true}: SearchFiltersSkeletonProp // eslint-disable-next-line react/no-array-index-key key={index} transform={[{translateX: 12 + index * 90}, {translateY: 10}]} - width={60} - height={8} + width={width * PILL_WIDTH_RATIO} + height={height * PILL_HEIGHT_RATIO} /> ))} diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts index 0fc2edb1f31e..7e51bd999a29 100644 --- a/src/hooks/useAssignCard.ts +++ b/src/hooks/useAssignCard.ts @@ -1,4 +1,4 @@ -import {useContext} from 'react'; +import {useContext, useRef} from 'react'; import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider'; import {importPlaidAccounts} from '@libs/actions/Plaid'; import { @@ -30,25 +30,27 @@ import useOnyx from './useOnyx'; import usePolicy from './usePolicy'; type UseAssignCardProps = { - /** The currently selected card feed (includes domain ID) */ - selectedFeed: CompanyCardFeedWithDomainID | undefined; + /** The currently selected card feed */ + feedName: CompanyCardFeedWithDomainID | undefined; + /** The ID of the workspace/policy */ policyID: string; + /** Callback to show/hide the offline modal */ setShouldShowOfflineModal: (shouldShow: boolean) => void; }; -function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseAssignCardProps) { +function useAssignCard({feedName, policyID, setShouldShowOfflineModal}: UseAssignCardProps) { const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: false}); const [cardFeeds] = useCardFeeds(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); - const currentFeedData = selectedFeed ? companyFeeds?.[selectedFeed] : ({} as CombinedCardFeed); + const currentFeedData = feedName ? companyFeeds?.[feedName] : ({} as CombinedCardFeed); const policy = usePolicy(policyID); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const companyCards = getCompanyFeeds(cardFeeds); - const selectedFeedData = selectedFeed && companyCards[selectedFeed]; + const selectedFeedData = feedName && companyCards[feedName]; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); const fetchCompanyCards = () => { @@ -57,10 +59,10 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const cardList = allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]; + const cardList = allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feedName}`]; const filteredFeedCards = filterInactiveCards(cardList); - const hasFeedError = selectedFeed ? !!cardFeeds?.[selectedFeed]?.errors : false; + const hasFeedError = feedName ? !!cardFeeds?.[feedName]?.errors : false; const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError; const isAllowedToIssueCompanyCard = useIsAllowedToIssueCompanyCard({policyID}); @@ -68,7 +70,7 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA const isAssigningCardDisabled = !currentFeedData || !!currentFeedData?.pending || isSelectedFeedConnectionBroken || !isAllowedToIssueCompanyCard; - const getInitialAssignCardStep = useInitialAssignCardStep({policyID, selectedFeed}); + const getInitialAssignCardStep = useInitialAssignCardStep({policyID, selectedFeed: feedName}); const assignCard = (cardID?: string) => { if (isAssigningCardDisabled) { @@ -80,11 +82,11 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA return; } - if (!selectedFeed || !cardID) { + if (!feedName || !cardID) { return; } - const isCommercialFeed = isCustomFeed(selectedFeed); + const isCommercialFeed = isCustomFeed(feedName); // If the feed is a direct feed (not a commercial feed) and the user is offline, // show the offline alert modal to inform them of the connectivity issue. @@ -110,11 +112,11 @@ function useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}: UseA switch (initialStep) { case CONST.COMPANY_CARD.STEP.PLAID_CONNECTION: case CONST.COMPANY_CARD.STEP.BANK_CONNECTION: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION.getRoute(policyID, selectedFeed)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION.getRoute(policyID, feedName)); break; case CONST.COMPANY_CARD.STEP.ASSIGNEE: default: - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute({policyID, feed: selectedFeed, cardID})); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_ASSIGNEE.getRoute({policyID, feed: feedName, cardID})); break; } }); @@ -145,6 +147,7 @@ function useInitialAssignCardStep({policyID, selectedFeed}: UseInitialAssignCard const bankName = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const isFeedExpired = isSelectedFeedExpired(feedData); const plaidAccessToken = feedData?.plaidAccessToken; + const hasImportedPlaidAccounts = useRef(false); const getInitialAssignCardStep = (cardID: string | undefined): {initialStep: AssignCardStep; cardToAssign: Partial} | undefined => { if (!selectedFeed) { @@ -158,9 +161,10 @@ function useInitialAssignCardStep({policyID, selectedFeed}: UseInitialAssignCard }; // Refetch plaid card list - if (!isFeedExpired && plaidAccessToken) { + if (!isFeedExpired && plaidAccessToken && !hasImportedPlaidAccounts.current) { const country = feedData?.country ?? ''; importPlaidAccounts('', selectedFeed, '', country, getDomainNameForPolicy(policyID), '', undefined, undefined, plaidAccessToken); + hasImportedPlaidAccounts.current = true; } if (isFeedExpired || !cardID) { diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx index 934d1cb63b6d..1a65b7a033c5 100644 --- a/src/hooks/useCardsList.tsx +++ b/src/hooks/useCardsList.tsx @@ -2,11 +2,11 @@ import type {ResultMetadata} from 'react-native-onyx'; import {filterInactiveCards} from '@libs/CardUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CardList, CompanyCardFeedWithDomainID} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx'; import useOnyx from './useOnyx'; /* Custom hook that retrieves a list of company cards for the given selected feed. */ -const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [CardList | undefined, ResultMetadata] => { +const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [WorkspaceCardsList | undefined, ResultMetadata] => { const [feed, domainOrWorkspaceAccountID] = selectedFeed?.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR) ?? []; const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feed}`, { selector: filterInactiveCards, diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts new file mode 100644 index 000000000000..55c89cb84ab3 --- /dev/null +++ b/src/hooks/useCompanyCards.ts @@ -0,0 +1,66 @@ +import type {OnyxCollection, ResultMetadata} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import {getCompanyCardFeed, getCompanyFeeds, getPlaidInstitutionId} from '@libs/CardUtils'; +import type CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {CardFeeds, CardList} from '@src/types/onyx'; +import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card'; +import type {CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds'; +import useCardFeeds from './useCardFeeds'; +import type {CombinedCardFeed, CombinedCardFeeds} from './useCardFeeds'; +import useCardsList from './useCardsList'; +import useOnyx from './useOnyx'; + +type CardFeedType = ValueOf; + +type UsCompanyCardsResult = Partial<{ + cardFeedType: CardFeedType; + bankName: CompanyCardFeed; + feedName: CompanyCardFeedWithDomainID; + cardList: AssignableCardsList; + assignedCards: CardList; + cardNames: string[]; + allCardFeeds: CombinedCardFeeds; + companyCardFeeds: CompanyFeeds; + selectedFeed: CombinedCardFeed; +}> & { + onyxMetadata: { + cardListMetadata: ResultMetadata; + allCardFeedsMetadata: ResultMetadata>; + }; +}; + +function useCompanyCards(policyID?: string): UsCompanyCardsResult { + const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); + const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID); + + const feedName = lastSelectedFeed; + const bankName = feedName ? getCompanyCardFeed(feedName) : undefined; + + const [cardsList, cardListMetadata] = useCardsList(feedName); + + const companyCardFeeds = getCompanyFeeds(allCardFeeds); + const selectedFeed = feedName && companyCardFeeds[feedName]; + const isPlaidCardFeed = !!getPlaidInstitutionId(feedName); + + let cardFeedType: CardFeedType = 'customFeed'; + if (isPlaidCardFeed) { + cardFeedType = 'directFeed'; + } + + const {cardList, ...assignedCards} = cardsList ?? {}; + const cardNames = cardFeedType === 'directFeed' ? (selectedFeed?.accountList ?? []) : Object.keys(cardList ?? {}); + + const onyxMetadata = { + cardListMetadata, + allCardFeedsMetadata, + }; + + if (!policyID) { + return {onyxMetadata}; + } + + return {allCardFeeds, feedName, companyCardFeeds, cardList, assignedCards, cardNames, selectedFeed, bankName, cardFeedType, onyxMetadata}; +} + +export default useCompanyCards; diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index de87574b7fdf..ab13eca190c3 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -91,7 +91,8 @@ function getAssignedCardSortKey(card: Card): number { } /** - * @param card + * Checks if the card is an Expensify card. + * @param card - The card to check. * @returns boolean */ function isExpensifyCard(card?: Card) { @@ -388,7 +389,11 @@ function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD /** * Verify if the feed is a custom feed. Those are also referred to as commercial feeds. */ -function isCustomFeed(feed: CompanyCardFeedWithNumber): boolean { +function isCustomFeed(feed: CompanyCardFeedWithNumber | undefined): boolean { + if (!feed) { + return false; + } + return [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX].some((value) => feed.startsWith(value)); } @@ -822,7 +827,10 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined } /** Extract feed from feed with domainID */ -function getCompanyCardFeed(feedWithDomainID: string): CompanyCardFeed { +function getCompanyCardFeed(feedWithDomainID: string | undefined): CompanyCardFeed { + if (!feedWithDomainID) { + return '' as CompanyCardFeed; + } const [feed] = feedWithDomainID.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx index c8eb0f10789d..059ea878a81f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx @@ -11,18 +11,18 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {hasIssuedExpensifyCard} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; -import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import colors from '@styles/theme/colors'; import {clearAddNewCardFlow} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Policy} from '@src/types/onyx'; import WorkspaceCompanyCardExpensifyCardPromotionBanner from './WorkspaceCompanyCardExpensifyCardPromotionBanner'; type WorkspaceCompanyCardPageEmptyStateProps = { + policy: Policy | undefined; shouldShowGBDisclaimer?: boolean; -} & WithPolicyAndFullscreenLoadingProps; +}; function WorkspaceCompanyCardPageEmptyState({policy, shouldShowGBDisclaimer}: WorkspaceCompanyCardPageEmptyStateProps) { const {translate} = useLocalize(); @@ -94,4 +94,4 @@ function WorkspaceCompanyCardPageEmptyState({policy, shouldShowGBDisclaimer}: Wo ); } -export default withPolicyAndFullscreenLoading(WorkspaceCompanyCardPageEmptyState); +export default WorkspaceCompanyCardPageEmptyState; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 7b3be9a233b8..6d21928381f1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -1,35 +1,26 @@ import React, {useEffect, useState} from 'react'; -import ActivityIndicator from '@components/ActivityIndicator'; -import CardFeedIcon from '@components/CardFeedIcon'; import DecisionModal from '@components/DecisionModal'; import useAssignCard from '@hooks/useAssignCard'; -import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; +import useCompanyCards from '@hooks/useCompanyCards'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import useOnyx from '@hooks/useOnyx'; -import usePermissions from '@hooks/usePermissions'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useThemeStyles from '@hooks/useThemeStyles'; import {openWorkspaceMembersPage} from '@libs/actions/Policy/Member'; -import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; +import {getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import {getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import variables from '@styles/variables'; import {openPolicyCompanyCardsFeed, openPolicyCompanyCardsPage} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState'; -import WorkspaceCompanyCardsFeedPendingPage from './WorkspaceCompanyCardsFeedPendingPage'; import WorkspaceCompanyCardsTable from './WorkspaceCompanyCardsTable'; -import WorkspaceCompanyCardsTableHeaderButtons from './WorkspaceCompanyCardsTableHeaderButtons'; type WorkspaceCompanyCardsPageProps = PlatformStackScreenProps; @@ -37,118 +28,61 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const policyID = route.params.policyID; const {translate} = useLocalize(); - const styles = useThemeStyles(); const memoizedIllustrations = useMemoizedLazyIllustrations(['CompanyCard']); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {isBetaEnabled} = usePermissions(); const policy = usePolicy(policyID); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; - const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); - const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); - const [cardFeeds] = useCardFeeds(policyID); - const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const companyFeeds = getCompanyFeeds(cardFeeds); - const selectedFeedData = selectedFeed && companyFeeds[selectedFeed]; - const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; + const {allCardFeeds, feedName, selectedFeed, bankName} = useCompanyCards(policyID); + const [, cardsListMetadata] = useCardsList(feedName); - const [cardsList, cardsListMetadata] = useCardsList(selectedFeed); - const {cardList, ...assignedCards} = cardsList ?? {}; - const hasNoAssignedCard = Object.keys(assignedCards).length === 0; - - const isNoFeed = !selectedFeedData; - const isFeedPending = !!selectedFeedData?.pending; - const isFeedAdded = !isFeedPending && !isNoFeed; + const isInitiallyLoadingFeeds = isEmptyObject(allCardFeeds); + const isNoFeed = !selectedFeed && !isInitiallyLoadingFeeds; + const isFeedPending = !!selectedFeed?.pending; + const isFeedAdded = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed; const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false); - const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); + const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed); const {isOffline} = useNetwork({ onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID), }); - const cardFeedIcon = ( - - ); - - const isLoading = !isOffline && (!cardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata))); - const isGB = countryByIp === CONST.COUNTRY.GB; - const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); - useEffect(() => { openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID); }, [policyID, domainOrWorkspaceAccountID]); + const isLoading = !isOffline && (!allCardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata))); useEffect(() => { - if (isLoading || !feed || isFeedPending) { + if (isLoading || !bankName || isFeedPending) { return; } const clientMemberEmails = Object.keys(getMemberAccountIDsForWorkspace(policy?.employeeList)); openWorkspaceMembersPage(policyID, clientMemberEmails); - openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, feed); - }, [feed, isLoading, policyID, isFeedPending, domainOrWorkspaceAccountID, policy?.employeeList]); + openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, bankName); + }, [bankName, isLoading, policyID, isFeedPending, domainOrWorkspaceAccountID, policy?.employeeList]); - const {assignCard, isAssigningCardDisabled} = useAssignCard({selectedFeed, policyID, setShouldShowOfflineModal}); + const {assignCard, isAssigningCardDisabled} = useAssignCard({feedName, policyID, setShouldShowOfflineModal}); return ( - {isLoading && ( - + - )} - {!isLoading && ( - - {isFeedPending && !!selectedFeed && ( - - )} - - {isNoFeed && ( - - )} - - {isFeedPending && } - - {isFeedAdded && !isFeedPending && ( - - )} - - )} + - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION.getRoute(policyID, selectedFeed))); + const openBankConnection = () => { + if (!feedName) { + return; + } + + Navigation.setNavigationActionToMicrotaskQueue(() => { + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION.getRoute(policyID ?? '', feedName)); + }); + }; const secondaryActions = [ { icon: icons.Gear, text: translate('common.settings'), - onSelected: () => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)), + onSelected: () => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID ?? '')), value: CONST.POLICY.SECONDARY_ACTIONS.SETTINGS, }, ]; @@ -104,32 +118,46 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, selectedFeed, should !shouldShowNarrowLayout && [styles.flexColumn, styles.pv2, styles.flexRow, styles.alignItemsCenter, styles.justifyContentBetween], ]} > - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))} - CardFeedIcon={CardFeedIcon} - feedName={formattedFeedName} - supportingText={supportingText} - shouldShowRBR={checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, domainOrWorkspaceAccountID), selectedFeed)} - /> + {isLoadingFeed ? ( + + ) : ( + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID ?? ''))} + CardFeedIcon={CardFeedIcon} + feedName={formattedFeedName} + supportingText={supportingText} + shouldShowRBR={checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, domainOrWorkspaceAccountID), feedName)} + /> + )} + - {shouldDisplayTableComponents && ( + {!isLoadingFeed && showTableControls && ( )} + - {shouldDisplayTableComponents && } - {}} - shouldUseOptionIcon - customText={translate('common.more')} - options={secondaryActions} - isSplitButton={false} - wrapperStyle={shouldShowNarrowLayout ? styles.flex1 : styles.flexGrow0} - /> + {!isLoadingFeed && ( + <> + {showTableControls && } + {}} + shouldUseOptionIcon + customText={translate('common.more')} + options={secondaryActions} + isSplitButton={false} + wrapperStyle={shouldShowNarrowLayout ? styles.flex1 : styles.flexGrow0} + /> + + )} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTableItem.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx similarity index 89% rename from src/pages/workspace/companyCards/WorkspaceCompanyCardsTableItem.tsx rename to src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx index d0f4dd9e23f2..59cd3ff7c130 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTableItem.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx @@ -17,7 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import {getDefaultAvatarURL} from '@libs/UserAvatarUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import type {Card, CompanyCardFeed, FailedCompanyCardAssignment, PersonalDetails} from '@src/types/onyx'; +import type {Card, FailedCompanyCardAssignment, PersonalDetails} from '@src/types/onyx'; type WorkspaceCompanyCardTableItemData = { /** Card number */ @@ -126,7 +126,7 @@ function WorkspaceCompanyCardTableItem({ > {({hovered}) => ( )} - - {isAssigned && ( - + + {isAssigned ? ( + {!shouldUseNarrowTableLayout && ( - )} - {!isAssigned && shouldUseNarrowTableLayout && ( + ) : (