From 0abbf92675394583186b862689b40a06e41ccc89 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 4 Nov 2025 16:47:12 +0100 Subject: [PATCH 01/23] Update useCardFeeds hook to support domain/workspace feed with the same name within one workspace --- src/ONYXKEYS.ts | 2 +- src/hooks/useCardFeeds.tsx | 58 +- src/hooks/useCardsList.tsx | 12 +- src/hooks/useUpdateFeedBrokenConnection.ts | 10 +- src/libs/CardFeedUtils.ts | 4 +- src/libs/CardUtils.ts | 59 +- src/libs/actions/Card.ts | 4 +- src/libs/actions/CompanyCards.ts | 34 +- src/pages/workspace/WorkspaceInitialPage.tsx | 4 +- .../workspace/WorkspaceMoreFeaturesPage.tsx | 4 +- ...kspaceCompanyCardAccountSelectCardPage.tsx | 14 +- .../WorkspaceCompanyCardDetailsPage.tsx | 25 +- .../WorkspaceCompanyCardEditCardNamePage.tsx | 12 +- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 32 +- ...spaceCompanyCardStatementCloseDatePage.tsx | 8 +- ...WorkspaceCompanyCardsErrorConfirmation.tsx | 12 +- ...WorkspaceCompanyCardsListHeaderButtons.tsx | 21 +- .../WorkspaceCompanyCardsPage.tsx | 23 +- ...kspaceCompanyCardsSettingsFeedNamePage.tsx | 8 +- .../WorkspaceCompanyCardsSettingsPage.tsx | 14 +- .../addNew/CardInstructionsStep.tsx | 4 +- .../addNew/DirectStatementCloseDatePage.tsx | 8 +- .../assignCard/AssignCardFeedPage.tsx | 4 +- .../companyCards/assignCard/AssigneeStep.tsx | 4 +- .../assignCard/CardSelectionStep.tsx | 10 +- .../assignCard/ConfirmationStep.tsx | 10 +- .../assignCard/InviteNewMemberStep.tsx | 6 +- .../downgrade/WorkspaceDowngradePage.tsx | 4 +- .../members/WorkspaceMemberDetailsPage.tsx | 12 +- .../members/WorkspaceMemberNewCardPage.tsx | 49 +- src/types/onyx/AssignCard.ts | 3 +- src/types/onyx/CardFeeds.ts | 7 +- src/types/onyx/index.ts | 3 +- tests/unit/CardUtilsTest.ts | 1840 ++++++++--------- 34 files changed, 1198 insertions(+), 1126 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c3c1c5ef94f3..e8488250b57c 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -1090,7 +1090,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; - [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CompanyCardFeed; + [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CombinedFeedKey; [ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED]: OnyxTypes.FundID; [ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST]: OnyxTypes.CardOnWaitlist; [ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD]: OnyxTypes.IssueNewCard; diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx index a926c0a9db18..7ea2b85967a3 100644 --- a/src/hooks/useCardFeeds.tsx +++ b/src/hooks/useCardFeeds.tsx @@ -1,11 +1,24 @@ import {useMemo} from 'react'; import type {OnyxCollection, ResultMetadata} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CardFeeds, CompanyCardFeed} from '@src/types/onyx'; +import type {CardFeeds, CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; +import type {CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds'; import useOnyx from './useOnyx'; import useWorkspaceAccountID from './useWorkspaceAccountID'; +type CombinedCardFeed = CustomCardFeedData & + Partial & { + /** Custom feed name, originally coming from settings.companyCardNicknames */ + customFeedName?: string; + + /** Feed name */ + feed: CompanyCardFeed; + }; + +type CombinedCardFeeds = Record; + /** + * UPDATE * This is a custom hook that combines workspace and domain card feeds for a given policy. * * This hook: @@ -20,7 +33,7 @@ import useWorkspaceAccountID from './useWorkspaceAccountID'; * 2. The result metadata from the Onyx collection fetch. * 3. Card feeds specific to the given policyID (or `undefined` if unavailable). */ -const useCardFeeds = (policyID: string | undefined): [CardFeeds | undefined, ResultMetadata>, CardFeeds | undefined] => { +const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata>, CardFeeds | undefined, boolean] => { const workspaceAccountID = useWorkspaceAccountID(policyID); const [allFeeds, allFeedsResult] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true}); const defaultFeed = allFeeds?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]; @@ -30,18 +43,9 @@ const useCardFeeds = (policyID: string | undefined): [CardFeeds | undefined, Res return undefined; } - const {companyCards = {}, companyCardNicknames = {}, oAuthAccountDetails = {}} = defaultFeed?.settings ?? {}; + const result: CombinedCardFeeds = {}; - const result: CardFeeds & {settings: Required} = { - settings: { - companyCards: {...companyCards}, - companyCardNicknames: {...companyCardNicknames}, - oAuthAccountDetails: {...oAuthAccountDetails}, - }, - isLoading: defaultFeed?.isLoading, - }; - - return Object.entries(allFeeds).reduce}>((acc, [onyxKey, feed]) => { + return Object.entries(allFeeds).reduce((acc, [onyxKey, feed]) => { if (!feed?.settings?.companyCards) { return acc; } @@ -49,29 +53,29 @@ const useCardFeeds = (policyID: string | undefined): [CardFeeds | undefined, Res Object.entries(feed.settings.companyCards).forEach(([key, feedSettings]) => { const feedName = key as CompanyCardFeed; const feedOAuthAccountDetails = feed.settings.oAuthAccountDetails?.[feedName]; - const feedCompanyCardNicknames = feed.settings.companyCardNicknames?.[feedName]; + const feedCompanyCardNickname = feed.settings.companyCardNicknames?.[feedName]; + const domainID = onyxKey.split('_').at(-1); + const combinedFeedKey: CombinedFeedKey = `${feedName}#${domainID}`; - if (feedSettings.preferredPolicy !== policyID || acc.settings.companyCards[feedName]) { + if (feedSettings.preferredPolicy !== policyID) { return; } - const domainID = onyxKey.split('_').at(-1); - - acc.settings.companyCards[feedName] = {...feedSettings, domainID: domainID ? Number(domainID) : undefined}; - - if (feedOAuthAccountDetails) { - acc.settings.oAuthAccountDetails[feedName] = feedOAuthAccountDetails; - } - if (feedCompanyCardNicknames) { - acc.settings.companyCardNicknames[feedName] = feedCompanyCardNicknames; - } + acc[combinedFeedKey] = { + ...feedSettings, + ...feedOAuthAccountDetails, + customFeedName: feedCompanyCardNickname, + domainID: Number(domainID), + feed: feedName, + }; }); return acc; }, result); - }, [allFeeds, defaultFeed?.isLoading, defaultFeed?.settings, policyID]); + }, [allFeeds, policyID]); - return [workspaceFeeds, allFeedsResult, defaultFeed]; + return [workspaceFeeds, allFeedsResult, defaultFeed, !!defaultFeed?.isLoading]; }; export default useCardFeeds; +export type {CombinedCardFeeds, CombinedFeedKey, CombinedCardFeed}; diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx index 769144a1a02b..879712f9034f 100644 --- a/src/hooks/useCardsList.tsx +++ b/src/hooks/useCardsList.tsx @@ -1,18 +1,20 @@ import type {ResultMetadata} from 'react-native-onyx'; -import {filterInactiveCards, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; +import {filterInactiveCards, getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CardList, CompanyCardFeed} from '@src/types/onyx'; +import type {CardList, CombinedFeedKey} from '@src/types/onyx'; import useCardFeeds from './useCardFeeds'; import useOnyx from './useOnyx'; import useWorkspaceAccountID from './useWorkspaceAccountID'; /* Custom hook that retrieves a list of company cards for the given policy and selected feed. */ -const useCardsList = (policyID: string | undefined, selectedFeed: CompanyCardFeed | undefined): [CardList | undefined, ResultMetadata] => { +const useCardsList = (policyID: string | undefined, selectedFeed: CombinedFeedKey | undefined): [CardList | undefined, ResultMetadata] => { const workspaceAccountID = useWorkspaceAccountID(policyID); const [cardFeeds] = useCardFeeds(policyID); - const companyCards = getCompanyFeeds(cardFeeds); + const companyCards = getCombinedCompanyFeeds(cardFeeds); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyCards[selectedFeed] : undefined); - const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`, { + // TODO: can be improved + const [feed] = selectedFeed?.split('#') ?? []; + const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feed}`, { selector: filterInactiveCards, canBeMissing: true, }); diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index 1c4daab40368..d984affc81e5 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -1,17 +1,17 @@ import {useCallback} from 'react'; -import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeedName} from '@libs/CardUtils'; import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; import useCardFeeds from './useCardFeeds'; import useCardsList from './useCardsList'; import usePolicy from './usePolicy'; -export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CompanyCardFeed}) { +export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CombinedFeedKey}) { const [cardsList] = useCardsList(policyID, feed); const policy = usePolicy(policyID); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const {cardList, ...cards} = cardsList ?? {}; const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const domainOrWorkspaceAccountID = feed ? getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[feed]) : CONST.DEFAULT_NUMBER_ID; @@ -23,7 +23,7 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI if (!brokenCardId || !feed) { return; } - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, feed, brokenCard?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getOriginalFeedName(feed), brokenCard?.lastScrapeResult); }, [brokenCard?.lastScrapeResult, brokenCardId, domainOrWorkspaceAccountID, feed]); return {updateBrokenConnection, isFeedConnectionBroken}; diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts index 9d73ff28b11a..70ea4a37fb30 100644 --- a/src/libs/CardFeedUtils.ts +++ b/src/libs/CardFeedUtils.ts @@ -436,7 +436,7 @@ function getCardFeedsForDisplay(allCardFeeds: OnyxCollection, allCard id, feed, fundID, - name: getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames, false) ?? feed, + name: getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames?.[feed], false) ?? feed, }; }); }); @@ -487,7 +487,7 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection id, feed, fundID, - name: getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames, false) ?? feed, + name: getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames?.[feed], false) ?? feed, }); }); }); diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 5abc29ee9a54..21ec36f378ee 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -4,6 +4,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import ExpensifyCardImage from '@assets/images/expensify-card.svg'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; +import type {CombinedCardFeed, CombinedCardFeeds} from '@hooks/useCardFeeds'; import type IllustrationsType from '@styles/theme/illustrations/types'; import * as Illustrations from '@src/components/Icon/Illustrations'; import CONST from '@src/CONST'; @@ -11,7 +12,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, CurrencyList, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {FilteredCardList} from '@src/types/onyx/Card'; -import type {CardFeedData, CompanyCardFeedWithNumber, CompanyCardNicknames, CompanyFeeds, DirectCardFeedData} from '@src/types/onyx/CardFeeds'; +import type {CardFeedData, CombinedFeedKey, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -358,6 +359,21 @@ function getCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemoved ); } +function getCombinedCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false, shouldFilterOutPendingFeeds = false): CombinedCardFeeds { + return Object.fromEntries( + Object.entries(cardFeeds ?? {}).filter(([, value]) => { + if (shouldFilterOutRemovedFeeds && value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + return false; + } + if (shouldFilterOutPendingFeeds && value.pending) { + return false; + } + // FIX HERE + return !value.feed.includes(CONST.EXPENSIFY_CARD.BANK); + }), + ); +} + function getBankName(feedType: CompanyCardFeed): string { const feedNamesMapping = { [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: 'Visa', @@ -406,13 +422,11 @@ const getBankCardDetailsImage = (bank: ValueOf return iconMap[bank]; }; -function getCustomOrFormattedFeedName(feed?: CompanyCardFeed, companyCardNicknames?: CompanyCardNicknames, shouldAddCardsSuffix = true): string | undefined { +function getCustomOrFormattedFeedName(feed?: CompanyCardFeed, customFeedName?: string, shouldAddCardsSuffix = true): string | undefined { if (!feed) { return; } - const customFeedName = companyCardNicknames?.[feed]; - if (customFeedName && typeof customFeedName !== 'string') { return ''; } @@ -513,21 +527,24 @@ function getCorrectStepForPlaidSelectedBank(selectedBank: ValueOf, cardFeeds: OnyxEntry): CompanyCardFeed | undefined { - const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CompanyCardFeed | undefined; - return lastSelectedFeed ?? defaultFeed; +function getSelectedFeed(lastSelectedFeed: OnyxEntry, cardFeeds: OnyxEntry): CombinedFeedKey | undefined { + const defaultFeed = Object.keys(getCombinedCompanyFeeds(cardFeeds, true)).at(0) as CombinedFeedKey | undefined; + if (!lastSelectedFeed?.includes('#')) { + return defaultFeed; + } + return lastSelectedFeed; } -function isSelectedFeedExpired(directFeed: DirectCardFeedData | undefined): boolean { - if (!directFeed || !directFeed.expiration) { +function isSelectedFeedExpired(cardFeed: CombinedCardFeed | undefined): boolean { + if (!cardFeed || !cardFeed.expiration) { return false; } - return isBefore(fromUnixTime(directFeed.expiration), new Date()); + return isBefore(fromUnixTime(cardFeed.expiration), new Date()); } /** Returns list of cards which can be assigned */ -function getFilteredCardList(list: WorkspaceCardsList | undefined, directFeed: DirectCardFeedData | undefined, workspaceCardFeeds: OnyxCollection) { +function getFilteredCardList(list: WorkspaceCardsList | undefined, accountList: string[] | undefined, workspaceCardFeeds: OnyxCollection) { const {cardList: customFeedCardsToAssign, ...cards} = list ?? {}; const assignedCards = Object.values(cards).map((card) => card.cardName); @@ -546,8 +563,8 @@ function getFilteredCardList(list: WorkspaceCardsList | undefined, directFeed: D }); }); - if (directFeed) { - const unassignedDirectFeedCards = directFeed.accountList.filter((cardNumber) => !assignedCards.includes(cardNumber) && !allWorkspaceAssignedCards.has(cardNumber)); + if (accountList?.length) { + const unassignedDirectFeedCards = accountList.filter((cardNumber) => !assignedCards.includes(cardNumber) && !allWorkspaceAssignedCards.has(cardNumber)); return Object.fromEntries(unassignedDirectFeedCards.map((cardNumber) => [cardNumber, cardNumber])); } @@ -583,11 +600,11 @@ function filterInactiveCards(cards: CardList | undefined): CardList { function getAllCardsForWorkspace( workspaceAccountID: number, allCardList: OnyxCollection, - cardFeeds?: CardFeeds, + cardFeeds?: CombinedCardFeeds, expensifyCardSettings?: OnyxCollection, ): CardList { const cards = {}; - const companyCardsDomainFeeds = Object.entries(cardFeeds?.settings?.companyCards ?? {}).map(([feedName, feedData]) => ({domainID: feedData.domainID, feedName})); + const companyCardsDomainFeeds = Object.entries(cardFeeds ?? {}).map(([feedName, feedData]) => ({domainID: feedData.domainID, feedName})); const expensifyCardsDomainIDs = Object.keys(expensifyCardSettings ?? {}) .map((key) => key.split('_').at(-1)) .filter((id): id is string => !!id); @@ -610,9 +627,9 @@ function isSmartLimitEnabled(cards: CardList) { const CUSTOM_FEEDS = [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]; -function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry): CompanyCardFeedWithNumber { +function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry): CompanyCardFeedWithNumber { if (CUSTOM_FEEDS.some((feed) => feed === feedKey)) { - const filteredFeeds = Object.keys(cardFeeds?.settings?.companyCards ?? {}).filter((str) => str.includes(feedKey)); + const filteredFeeds = Object.keys(cardFeeds ?? {}).filter((str) => str.includes(feedKey)); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); @@ -719,6 +736,12 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined return Object.values(feedCards).find((card) => !isEmptyObject(card) && card.bank !== feedToExclude && card.lastScrapeResult !== 200); } +/** Extract original feed name */ +function getOriginalFeedName(feedName: CombinedFeedKey): CompanyCardFeed { + const [feed] = feedName.split('#'); + return feed as CompanyCardFeed; +} + export { getAssignedCardSortKey, isExpensifyCard, @@ -772,5 +795,7 @@ export { getPlaidInstitutionId, getFeedConnectionBrokenCard, getCorrectStepForPlaidSelectedBank, + getCombinedCompanyFeeds, + getOriginalFeedName, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index 1e3456a8d606..5c472eb00fe1 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -22,7 +22,7 @@ import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Card, CompanyCardFeed} from '@src/types/onyx'; +import type {Card, CombinedFeedKey} from '@src/types/onyx'; import type {CardLimitType, ExpensifyCardDetails, IssueNewCardData, IssueNewCardStep} from '@src/types/onyx/Card'; import type {ConnectionName} from '@src/types/onyx/Policy'; @@ -941,7 +941,7 @@ function toggleContinuousReconciliation(workspaceAccountID: number, shouldUseCon }); } -function updateSelectedFeed(feed: CompanyCardFeed, policyID: string | undefined) { +function updateSelectedFeed(feed: CombinedFeedKey, policyID: string | undefined) { if (!policyID) { return; } diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index cd681507361a..bd552bcdbabe 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -1,5 +1,6 @@ import type {NullishDeep, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import type {CombinedCardFeeds} from '@hooks/useCardFeeds'; import * as API from '@libs/API'; import type { AssignCompanyCardParams, @@ -20,9 +21,18 @@ 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, CardFeeds} from '@src/types/onyx'; +import type {Card} from '@src/types/onyx'; import type {AssignCard, AssignCardData} from '@src/types/onyx/AssignCard'; -import type {AddNewCardFeedData, AddNewCardFeedStep, CardFeedData, CardFeedDetails, CompanyCardFeed, StatementPeriodEnd, StatementPeriodEndDay} from '@src/types/onyx/CardFeeds'; +import type { + AddNewCardFeedData, + AddNewCardFeedStep, + CardFeedData, + CardFeedDetails, + CombinedFeedKey, + CompanyCardFeed, + StatementPeriodEnd, + StatementPeriodEndDay, +} from '@src/types/onyx/CardFeeds'; import type {OnyxData} from '@src/types/onyx/Request'; type AddNewCompanyCardFlowData = { @@ -63,10 +73,10 @@ function addNewCompanyCardsFeed( policyID: string | undefined, cardFeed: CompanyCardFeed, feedDetails: CardFeedDetails, - cardFeeds: OnyxEntry, + cardFeeds: OnyxEntry, statementPeriodEnd: StatementPeriodEnd | undefined, statementPeriodEndDay: StatementPeriodEndDay | undefined, - lastSelectedFeed?: CompanyCardFeed, + lastSelectedFeed?: CombinedFeedKey, ) { const authToken = NetworkStore.getAuthToken(); const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); @@ -151,7 +161,7 @@ function addNewCompanyCardsFeed( API.write(WRITE_COMMANDS.REQUEST_FEED_SETUP, parameters, {optimisticData, failureData, successData, finallyData}); } -function setWorkspaceCompanyCardFeedName(policyID: string, domainOrWorkspaceAccountID: number, bankName: string, userDefinedName: string) { +function setWorkspaceCompanyCardFeedName(policyID: string, domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, userDefinedName: string) { const authToken = NetworkStore.getAuthToken(); const onyxData: OnyxData = { optimisticData: [ @@ -208,7 +218,7 @@ function setWorkspaceCompanyCardTransactionLiability(domainOrWorkspaceAccountID: API.write(WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY, parameters, onyxData); } -function deleteWorkspaceCompanyCardFeed(policyID: string, domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, cardIDs: string[], feedToOpen?: CompanyCardFeed) { +function deleteWorkspaceCompanyCardFeed(policyID: string, domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, cardIDs: string[], feedToOpen?: CombinedFeedKey) { const authToken = NetworkStore.getAuthToken(); const isCustomFeed = CardUtils.isCustomFeed(bankName); const optimisticFeedUpdates = {[bankName]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}}; @@ -373,7 +383,7 @@ function assignWorkspaceCompanyCard(policyID: string, data?: Partial data.domainID) .filter((domainID): domainID is number => !!domainID); const {login, accountID} = useCurrentUserPersonalDetails(); diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 9dc3ad6d5774..286c80961795 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -18,7 +18,7 @@ import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import {filterInactiveCards, getAllCardsForWorkspace, getCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; +import {filterInactiveCards, getAllCardsForWorkspace, getCombinedCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -171,7 +171,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle', isActive: policy?.areCompanyCardsEnabled ?? false, pendingAction: policy?.pendingFields?.areCompanyCardsEnabled, - disabled: !isEmptyObject(getCompanyFeeds(cardFeeds)), + disabled: !isEmptyObject(getCombinedCompanyFeeds(cardFeeds)), action: (isEnabled: boolean) => { if (!policyID) { return; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index 40598d869b91..6e344bac57c1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -14,7 +14,7 @@ import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {setCompanyCardExportAccount} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getConnectedIntegration, getCurrentConnectionName} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -24,7 +24,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; import {getExportMenuItem} from './utils'; type WorkspaceCompanyCardAccountSelectCardProps = PlatformStackScreenProps; @@ -34,12 +34,12 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard const styles = useThemeStyles(); const {environmentURL} = useEnvironment(); const {policyID, cardID, backTo} = route.params; - const bank = decodeURIComponent(route.params.bank); + const bank = decodeURIComponent(route.params.bank) as CombinedFeedKey; const policy = usePolicy(policyID); const workspaceAccountID = useWorkspaceAccountID(policyID); const [searchText, setSearchText] = useState(''); - const [allBankCards] = useCardsList(policyID, bank as CompanyCardFeed); + const [allBankCards] = useCardsList(policyID, bank); const card = allBankCards?.[cardID]; const connectedIntegration = getConnectedIntegration(policy) ?? CONST.POLICY.CONNECTIONS.NAME.QBO; // We need to have an unchanged active route for getExportMenuItem so the export page link is not updated incorrectly when user is in other pages @@ -53,8 +53,8 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard const illustrations = useMemoizedLazyIllustrations(['Telescope'] as const); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCompanyFeeds(cardFeeds); - const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[bank as CompanyCardFeed]); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[bank]); const searchedListOptions = useMemo(() => { return tokenizedSearch(exportMenuItem?.data ?? [], searchText, (option) => [option.value]); @@ -81,7 +81,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard } const isDefaultCardSelected = value === defaultCard; const exportValue = isDefaultCardSelected ? CONST.COMPANY_CARDS.DEFAULT_EXPORT_TYPE : value; - setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, bank); + setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, getOriginalFeedName(bank)); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank)); }, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 64d5772946d3..2f77e11f2c4f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -22,7 +22,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCardFeedIcon, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getCombinedCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -38,7 +38,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import {getExportMenuItem} from './utils'; @@ -46,7 +46,8 @@ type WorkspaceCompanyCardDetailsPageProps = PlatformStackScreenProps { setIsUnassignModalVisible(false); if (card) { - unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, bank, card); + unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, originalFeedName, card); } Navigation.goBack(); }; const updateCard = () => { - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, bank as CompanyCardFeed, card?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, originalFeedName, card?.lastScrapeResult); }; const lastScrape = useMemo(() => { @@ -140,7 +141,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag @@ -148,7 +149,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag pendingAction={card?.nameValuePairs?.pendingFields?.cardTitle} errorRowStyles={[styles.ph5, styles.mb3]} errors={getLatestErrorField(card?.nameValuePairs ?? {}, 'cardTitle')} - onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, bank, 'cardTitle')} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeedName, 'cardTitle')} > clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, bank, 'lastScrape', true)} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeedName, 'lastScrape', true)} > ; @@ -33,7 +33,7 @@ type WorkspaceCompanyCardEditCardNamePageProps = PlatformStackScreenProps) => { - updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], bank, defaultValue); + updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], getOriginalFeedName(bank), defaultValue); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank), {compareParams: false}); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 6edc5d99120b..6c14f1070a37 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -9,6 +9,7 @@ import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; import type {ListItem} from '@components/SelectionList/types'; import useCardFeeds from '@hooks/useCardFeeds'; +import type {CombinedCardFeed, CombinedFeedKey} from '@hooks/useCardFeeds'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; @@ -18,7 +19,7 @@ import { checkIfFeedConnectionIsBroken, filterInactiveCards, getCardFeedIcon, - getCompanyFeeds, + getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getPlaidInstitutionIconUrl, @@ -39,8 +40,11 @@ import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed} from '@src/types/onyx'; type CardFeedListItem = ListItem & { + /** Combined feed key */ + value: CombinedFeedKey; + /** Card feed value */ - value: CompanyCardFeed; + feed: CompanyCardFeed; }; type WorkspaceCompanyCardFeedSelectorPageProps = PlatformStackScreenProps; @@ -57,24 +61,24 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: false}); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const isCollect = isCollectPolicy(policy); - const feeds: CardFeedListItem[] = Object.entries(companyFeeds).map(([key, feedSettings]) => { - const feed = key as CompanyCardFeed; + const feeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CombinedFeedKey, CombinedCardFeed]>).map(([key, feedSettings]) => { const filteredFeedCards = filterInactiveCards( - allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${getDomainOrWorkspaceAccountID(workspaceAccountID, feedSettings)}_${feed}`], + allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${getDomainOrWorkspaceAccountID(workspaceAccountID, feedSettings)}_${feedSettings.feed}`], ); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards); - const plaidUrl = getPlaidInstitutionIconUrl(feed); + const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed); return { - value: feed, - text: 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, + value: key, + feed: feedSettings.feed, + text: getCustomOrFormattedFeedName(feedSettings.feed, cardFeeds?.[key]?.customFeedName), + keyForList: key, + isSelected: key === selectedFeed, + isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: companyFeeds[key]?.pendingAction, brickRoadIndicator: isFeedConnectionBroken ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, canShowSeveralIndicators: isFeedConnectionBroken, leftElement: plaidUrl ? ( @@ -84,7 +88,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS /> ) : ( { const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; if (selectedFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, selectedFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + setFeedStatementPeriodEndDay(policyID, getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); @@ -68,7 +68,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ return; } - clearErrorField(selectedFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + clearErrorField(getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); }, [selectedFeed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index 602a75562872..832526bedfa0 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -8,17 +8,17 @@ import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; import Navigation from '@navigation/Navigation'; import {deleteWorkspaceCompanyCardFeed, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import {enableExpensifyCard} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; type WorkspaceCompanyCardsErrorConfirmationProps = { policyID?: string; - newFeed?: CompanyCardFeed; + newFeed?: CombinedFeedKey; }; function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCompanyCardsErrorConfirmationProps) { @@ -29,7 +29,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo const [cardsList] = useCardsList(policyID, newFeed); const [cardFeeds] = useCardFeeds(policyID); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const selectedFeedData = newFeed ? companyFeeds[newFeed] : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); @@ -39,10 +39,10 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo } const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); - const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeed[]) + const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]) .filter((feed) => feed !== newFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) .at(0); - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, newFeed, cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(newFeed), cardIDs, feedToOpen); }; const onButtonPress = () => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 8933377e7c75..d7f343b21fc5 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -7,6 +7,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; +import type {CombinedFeedKey} from '@hooks/useCardFeeds'; import useCardFeeds from '@hooks/useCardFeeds'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -22,9 +23,10 @@ import { flatAllCardsList, getBankName, getCardFeedIcon, - getCompanyFeeds, + getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, + getOriginalFeedName, getPlaidCountry, getPlaidInstitutionIconUrl, getPlaidInstitutionId, @@ -35,7 +37,7 @@ import {setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userAc import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {CompanyCardFeed, CurrencyList} from '@src/types/onyx'; +import type {CurrencyList} from '@src/types/onyx'; import type {AssignCardData} from '@src/types/onyx/AssignCard'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; @@ -44,7 +46,7 @@ type WorkspaceCompanyCardsListHeaderButtonsProps = { policyID: string; /** Currently selected feed */ - selectedFeed: CompanyCardFeed; + selectedFeed: CombinedFeedKey; /** Whether to show assign card button */ shouldShowAssignCardButton?: boolean; @@ -66,21 +68,22 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const formattedFeedName = getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); + const originalFeedName = getOriginalFeedName(selectedFeed); + const formattedFeedName = getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName); const isCommercialFeed = isCustomFeed(selectedFeed); const plaidUrl = getPlaidInstitutionIconUrl(selectedFeed); - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; - const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(selectedFeed); + const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(originalFeedName); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeedData); const filteredFeedCards = filterInactiveCards(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]); - const hasFeedError = !!cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed]?.errors; + const hasFeedError = !!cardFeeds?.[selectedFeed]?.errors; const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError; const openBankConnection = () => { const institutionId = !!getPlaidInstitutionId(selectedFeed); const data: Partial = { - bankName: selectedFeed, + bankName: originalFeedName, }; if (institutionId) { const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); @@ -119,7 +122,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))} - cardIcon={getCardFeedIcon(selectedFeed, illustrations)} + cardIcon={getCardFeedIcon(originalFeedName, illustrations)} shouldChangeLayout={shouldChangeLayout} feedName={formattedFeedName} supportingText={translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed')} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index abc84ed0d2a8..185d90d42f95 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -13,9 +13,10 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import { checkIfFeedConnectionIsBroken, - getCompanyFeeds, + getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getFilteredCardList, + getOriginalFeedName, getPlaidCountry, getPlaidInstitutionId, getSelectedFeed, @@ -55,7 +56,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [workspaceCardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: true}); - const [cardFeeds] = useCardFeeds(policyID); + const [cardFeeds, , , isCardFeedsLoading] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const [cardsList] = useCardsList(policyID, selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); @@ -67,9 +68,9 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const {isActingAsDelegate, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext); - const filteredCardList = getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined, workspaceCardFeeds); + const filteredCardList = getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.[selectedFeed]?.accountList : undefined, workspaceCardFeeds); - const companyCards = getCompanyFeeds(cardFeeds); + const companyCards = getCombinedCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed && companyCards[selectedFeed]; const isNoFeed = !selectedFeedData; const isPending = !!selectedFeedData?.pending; @@ -83,7 +84,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { }, [policyID, domainOrWorkspaceAccountID]); const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && (!cardFeeds || (!!cardFeeds.isLoading && isEmptyObject(cardsList))); + const isLoading = !isOffline && (!cardFeeds || (isCardFeedsLoading && isEmptyObject(cardsList))); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); @@ -96,7 +97,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { return; } - openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, selectedFeed); + openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, getOriginalFeedName(selectedFeed)); }, [selectedFeed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); const handleAssignCard = () => { @@ -118,19 +119,17 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { } const data: Partial = { - bankName: selectedFeed, + bankName: getOriginalFeedName(selectedFeed), }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; const employeeList = Object.values(policy?.employeeList ?? {}).filter((employee) => !isDeletedPolicyEmployee(employee, isOffline)); - const selectedFeedCompanyCardsData = selectedFeed ? cardFeeds?.settings?.companyCards?.[selectedFeed] : undefined; - const selectedFeedOAuthData = selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined; - const isFeedExpired = isSelectedFeedExpired(selectedFeedOAuthData); - const plaidAccessToken = selectedFeedCompanyCardsData?.plaidAccessToken; + const isFeedExpired = isSelectedFeedExpired(selectedFeedData); + const plaidAccessToken = selectedFeedData?.plaidAccessToken; // Refetch plaid card list if (!isFeedExpired && plaidAccessToken) { - const country = selectedFeedCompanyCardsData?.country ?? ''; + const country = selectedFeedData?.country ?? ''; importPlaidAccounts('', selectedFeed, '', country, getDomainNameForPolicy(policyID), '', undefined, undefined, plaidAccessToken); } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx index 14c67362dabb..2f790c77784b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -44,8 +44,8 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const companyFeeds = getCompanyFeeds(cardFeeds); - const feedName = getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyFeeds[selectedFeed] : undefined); const validate = useCallback( @@ -69,7 +69,7 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const submit = ({name}: WorkspaceCompanyCardFeedName) => { if (selectedFeed) { - setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, selectedFeed, name); + setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(selectedFeed), name); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 7ded181cde9e..b1733f60e659 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -16,7 +16,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {deleteWorkspaceCompanyCardFeed, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; type WorkspaceCompanyCardsSettingsPageProps = PlatformStackScreenProps; @@ -47,8 +47,8 @@ function WorkspaceCompanyCardsSettingsPage({ const selectedFeed = useMemo(() => getSelectedFeed(lastSelectedFeed, cardFeeds), [cardFeeds, lastSelectedFeed]); const [cardsList] = useCardsList(policyID, selectedFeed); - const feedName = getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); - const companyFeeds = getCompanyFeeds(cardFeeds); + const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const liabilityType = selectedFeedData?.liabilityType; const isPersonal = liabilityType === CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW; @@ -80,12 +80,12 @@ function WorkspaceCompanyCardsSettingsPage({ if (selectedFeed) { const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); - const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeed[]) + const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]) .filter((feed) => feed !== selectedFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) .at(0); // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, selectedFeed, cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(selectedFeed), cardIDs, feedToOpen); }); } }; @@ -97,7 +97,7 @@ function WorkspaceCompanyCardsSettingsPage({ setWorkspaceCompanyCardTransactionLiability( domainOrWorkspaceAccountID, policyID, - selectedFeed, + getOriginalFeedName(selectedFeed), isOn ? CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW : CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.RESTRICT, ); }; diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index ef882baee50e..bbb78f9cb382 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -11,6 +11,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAddNewCompanyCardStepAndData} from '@libs/actions/CompanyCards'; import {getBankName} from '@libs/CardUtils'; @@ -50,12 +51,13 @@ function CardInstructionsStep({policyID}: CardInstructionsStepProps) { const isOtherBankSelected = bank === CONST.COMPANY_CARDS.BANKS.OTHER; const translationKey = getCardInstructionHeader(feedProvider); const {isBetaEnabled} = usePermissions(); + const workspaceAccountID = useWorkspaceAccountID(policyID); const buttonTranslation = isStripeFeedProvider ? translate('common.submit') : translate('common.next'); const submit = () => { if (isStripeFeedProvider && policyID) { - updateSelectedFeed(feedProvider, policyID); + updateSelectedFeed(`${feedProvider}#${workspaceAccountID}`, policyID); Navigation.goBack(); return; } diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index ae70fbe3136f..ee6151e6e265 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -23,7 +23,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const workspaceAccountID = useWorkspaceAccountID(policyID); - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); const statementPeriodEndDay = selectedFeedData?.statementPeriodEndDay; @@ -52,7 +52,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr } const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; if (selectedFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, selectedFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + setFeedStatementPeriodEndDay(policyID, getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } goBack(); @@ -65,7 +65,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr return; } - clearErrorField(selectedFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + clearErrorField(getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); }, [selectedFeed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index d3084f9324a7..02ddb2c1d39b 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -14,7 +14,7 @@ import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; import AssigneeStep from './AssigneeStep'; import CardNameStep from './CardNameStep'; import CardSelectionStep from './CardSelectionStep'; @@ -28,7 +28,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; - const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeed; + const feed = decodeURIComponent(route.params?.feed) as CombinedFeedKey; const backTo = route.params?.backTo; const policyID = policy?.id; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index b8b474f788ea..6f38a6e2dadd 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -33,7 +33,7 @@ type AssigneeStepProps = { policy: OnyxEntry; /** Selected feed */ - feed: OnyxTypes.CompanyCardFeed; + feed: OnyxTypes.CombinedFeedKey; }; function AssigneeStep({policy, feed}: AssigneeStepProps) { @@ -45,7 +45,7 @@ function AssigneeStep({policy, feed}: AssigneeStepProps) { const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); const [list] = useCardsList(policy?.id, feed); const [cardFeeds] = useCardFeeds(policy?.id); - const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); + const filteredCardList = getFilteredCardList(list, cardFeeds?.[feed]?.accountList, workspaceCardFeeds); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index bdba42f1da2f..e4f7fec3bb28 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -18,18 +18,18 @@ import useOnyx from '@hooks/useOnyx'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {getCardFeedIcon, getFilteredCardList, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getFilteredCardList, getOriginalFeedName, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; type CardSelectionStepProps = { /** Selected feed */ - feed: CompanyCardFeed; + feed: CombinedFeedKey; /** Current policy id */ policyID: string | undefined; @@ -48,7 +48,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { const isEditing = assignCard?.isEditing; const assigneeDisplayName = getPersonalDetailByEmail(assignCard?.data?.email ?? '')?.displayName ?? ''; - const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); + const filteredCardList = getFilteredCardList(list, cardFeeds?.[feed]?.accountList, workspaceCardFeeds); const [cardSelected, setCardSelected] = useState(assignCard?.data?.encryptedCardNumber ?? ''); const [shouldShowError, setShouldShowError] = useState(false); @@ -66,7 +66,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { /> ) : ( ()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); - const bankName = (assignCard?.data?.bankName as CompanyCardFeed | undefined) ?? feed; + const bankName = assignCard?.data?.bankName ?? getOriginalFeedName(feed); const [cardFeeds] = useCardFeeds(policyID); const data = assignCard?.data; @@ -74,7 +74,7 @@ function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { return; } - const isFeedExpired = isSelectedFeedExpired(bankName ? cardFeeds?.settings?.oAuthAccountDetails?.[bankName] : undefined); + const isFeedExpired = isSelectedFeedExpired(cardFeeds?.[feed]); const institutionId = !!getPlaidInstitutionId(bankName); if (isFeedExpired) { diff --git a/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx b/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx index 492c766612f8..681880c1100c 100644 --- a/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx @@ -14,12 +14,12 @@ import {setAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; -import type {CompanyCardFeed} from '@src/types/onyx/CardFeeds'; +import type {CombinedFeedKey} from '@src/types/onyx/CardFeeds'; type InviteeNewMemberStepProps = WithPolicyAndFullscreenLoadingProps & WithCurrentUserPersonalDetailsProps & { /** Selected feed */ - feed: CompanyCardFeed; + feed: CombinedFeedKey; }; function InviteNewMemberStep({policy, route, currentUserPersonalDetails, feed}: InviteeNewMemberStepProps) { @@ -29,7 +29,7 @@ function InviteNewMemberStep({policy, route, currentUserPersonalDetails, feed}: const isEditing = assignCard?.isEditing; const [list] = useCardsList(policy?.id, feed); const [cardFeeds] = useCardFeeds(policy?.id); - const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds); + const filteredCardList = getFilteredCardList(list, cardFeeds?.[feed]?.accountList, workspaceCardFeeds); const handleBackButtonPress = () => { if (isEditing) { diff --git a/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx b/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx index 034e7b052713..559943c2f333 100644 --- a/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx +++ b/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx @@ -11,7 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds} from '@libs/CardUtils'; +import {getCombinedCompanyFeeds} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -31,7 +31,7 @@ function WorkspaceDowngradePage({route}: WorkspaceDowngradePageProps) { const policyID = route.params?.policyID; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCompanyFeeds(cardFeeds); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const [isDowngradeWarningModalOpen, setIsDowngradeWarningModalOpen] = useState(false); diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index dc4e6ad0400b..0a0b7b5d5973 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -27,7 +27,15 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy'; import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow'; -import {getAllCardsForWorkspace, getCardFeedIcon, getCompanyFeeds, getPlaidInstitutionIconUrl, isExpensifyCardFullySetUp, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import { + getAllCardsForWorkspace, + getCardFeedIcon, + getCombinedCompanyFeeds, + getPlaidInstitutionIconUrl, + isExpensifyCardFullySetUp, + lastFourNumbersFromCardName, + maskCardNumber, +} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -99,7 +107,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const isCurrentUserOwner = policy?.owner === currentUserPersonalDetails?.login; const ownerDetails = useMemo(() => personalDetails?.[policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? ({} as PersonalDetails), [personalDetails, policy?.ownerAccountID]); const policyOwnerDisplayName = formatPhoneNumber(getDisplayNameOrDefault(ownerDetails)) ?? policy?.owner ?? ''; - const hasMultipleFeeds = Object.keys(getCompanyFeeds(cardFeeds, false, true)).length > 0; + const hasMultipleFeeds = Object.keys(getCombinedCompanyFeeds(cardFeeds, false, true)).length > 0; const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList, cardFeeds, expensifyCardSettings); const isSMSLogin = Str.isSMSLogin(memberLogin); const phoneNumber = getPhoneNumber(details); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index f022b1a09705..ee7eca1f0fcc 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -7,6 +7,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; import type {ListItem} from '@components/SelectionList/types'; +import type {CombinedCardFeed} from '@hooks/useCardFeeds'; import useCardFeeds from '@hooks/useCardFeeds'; import useCardsList from '@hooks/useCardsList'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; @@ -16,10 +17,11 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import { getCardFeedIcon, - getCompanyFeeds, + getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getFilteredCardList, + getOriginalFeedName, getPlaidInstitutionIconUrl, hasOnlyOneCardToAssign, isCustomFeed, @@ -40,12 +42,15 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; type CardFeedListItem = ListItem & { + /** Combined feed key */ + value: CombinedFeedKey; + /** Card feed value */ - value: string; + feed: CompanyCardFeed; }; type WorkspaceMemberNewCardPageProps = WithPolicyAndFullscreenLoadingProps & PlatformStackScreenProps; @@ -61,7 +66,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const styles = useThemeStyles(); const lazyIllustrations = useMemoizedLazyIllustrations(['ExpensifyCardImage'] as const); const illustrations = useThemeIllustrations(); - const [cardFeeds] = useCardFeeds(policyID); + const [cardFeeds, , , isLoading] = useCardFeeds(policyID); const [selectedFeed, setSelectedFeed] = useState(''); const [shouldShowError, setShouldShowError] = useState(false); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, {canBeMissing: true}); @@ -70,12 +75,12 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const accountID = Number(route.params.accountID); const memberLogin = personalDetails?.[accountID]?.login ?? ''; const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; - const companyFeeds = getCompanyFeeds(cardFeeds, false, true); - const isFeedExpired = isSelectedFeedExpired((selectedFeed as CompanyCardFeed) ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed as CompanyCardFeed] : undefined); - const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[selectedFeed as CompanyCardFeed]); + const companyFeeds = getCombinedCompanyFeeds(cardFeeds, false, true); + const isFeedExpired = isSelectedFeedExpired(cardFeeds?.[selectedFeed as CombinedFeedKey]); + const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[selectedFeed as CombinedFeedKey]); - const [list] = useCardsList(policyID, selectedFeed as CompanyCardFeed); - const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed as CompanyCardFeed], workspaceCardFeeds); + const [list] = useCardsList(policyID, selectedFeed as CombinedFeedKey); + const filteredCardList = getFilteredCardList(list, cardFeeds?.[selectedFeed as CombinedFeedKey]?.accountList, workspaceCardFeeds); const shouldShowExpensifyCard = isExpensifyCardFullySetUp(policy, cardSettings); @@ -98,7 +103,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew } else { const data: Partial = { email: memberLogin, - bankName: selectedFeed, + bankName: getOriginalFeedName(selectedFeed as CombinedFeedKey), cardName: `${memberName}'s card`, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; @@ -122,22 +127,23 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew } }; - const handleSelectFeed = (feed: CardFeedListItem) => { - setSelectedFeed(feed.value); - const workspaceCards = workspaceCardFeeds?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feed.value as CompanyCardFeed}`] ?? {}; + const handleSelectFeed = ({value, feed}: CardFeedListItem) => { + setSelectedFeed(value); + const workspaceCards = workspaceCardFeeds?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feed}`] ?? {}; const hasAllCardsData = !!workspaceCards.cardList; - if (isCustomFeed(feed.value as CompanyCardFeed) && !hasAllCardsData) { - openAssignFeedCardPage(policyID, feed.value as CompanyCardFeed, domainOrWorkspaceAccountID); + if (isCustomFeed(feed) && !hasAllCardsData) { + openAssignFeedCardPage(policyID, feed, domainOrWorkspaceAccountID); } setShouldShowError(false); }; - const companyCardFeeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((key) => { - const plaidUrl = getPlaidInstitutionIconUrl(key); + const companyCardFeeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CombinedFeedKey, CombinedCardFeed]>).map(([key, value]) => { + const plaidUrl = getPlaidInstitutionIconUrl(value.feed); return { value: key, - text: getCustomOrFormattedFeedName(key, cardFeeds?.settings?.companyCardNicknames), + feed: value.feed, + text: getCustomOrFormattedFeedName(value.feed, cardFeeds?.[key]?.customFeedName), keyForList: key, isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, pendingAction: companyFeeds[key]?.pendingAction, @@ -150,7 +156,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew /> ) : ( diff --git a/src/types/onyx/AssignCard.ts b/src/types/onyx/AssignCard.ts index 9e3df19aa898..2bd594427844 100644 --- a/src/types/onyx/AssignCard.ts +++ b/src/types/onyx/AssignCard.ts @@ -2,6 +2,7 @@ import type {LinkAccount} from 'react-native-plaid-link-sdk'; import type {PlaidAccount} from 'react-plaid-link'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +import type {CompanyCardFeed} from './CardFeeds'; /** Assign card flow steps */ type AssignCardStep = ValueOf; @@ -18,7 +19,7 @@ type AssignCardData = { cardNumber: string; /** The name of the feed */ - bankName: string; + bankName: CompanyCardFeed; /** The name of the card */ cardName: string; diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 33a179bf1f9a..ca8c5d7a6f8a 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -7,8 +7,11 @@ import type * as OnyxCommon from './OnyxCommon'; /** Card feed */ type CompanyCardFeed = ValueOf; +/** Combined feed key */ +type CombinedFeedKey = `${CompanyCardFeed}#${string}`; + /** Custom card feed with a number */ -type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}`; +type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CombinedFeedKey; /** Statement period end */ type StatementPeriodEnd = Exclude, typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE.CUSTOM_DAY_OF_MONTH>; @@ -225,6 +228,8 @@ export type { CardFeedProvider, CardFeedData, CompanyFeeds, + CombinedFeedKey, + CustomCardFeedData, CompanyCardNicknames, CompanyCardFeedWithNumber, FundID, diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 475b4e36a0ed..d52c7cb638e4 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -16,7 +16,7 @@ import type CancellationDetails from './CancellationDetails'; import type Card from './Card'; import type {CardList, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card'; import type CardFeeds from './CardFeeds'; -import type {AddNewCompanyCardFeed, CompanyCardFeed, FundID} from './CardFeeds'; +import type {AddNewCompanyCardFeed, CombinedFeedKey, CompanyCardFeed, FundID} from './CardFeeds'; import type CardOnWaitlist from './CardOnWaitlist'; import type {CapturedLogs, Log} from './Console'; import type {CorpayFields, CorpayFormField} from './CorpayFields'; @@ -165,6 +165,7 @@ export type { IssueNewCard, AddNewCompanyCardFeed, CompanyCardFeed, + CombinedFeedKey, LastExportMethod, Locale, LockAccountDetails, diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index bb85f624eba4..ed9a370f6d8b 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -334,923 +334,923 @@ const mockIllustrations = { jest.mock('@src/components/Icon/Illustrations', () => require('../../__mocks__/Illustrations') as typeof Illustrations); -describe('CardUtils', () => { - describe('Expiration date formatting', () => { - it('Should format expirationDate month and year to MM/YYYY', () => { - expect(getMonthFromExpirationDateString(longDateSlashed)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(longDateSlashed)).toBe(expectedYear); - }); - - it('Should format expirationDate month and year to MM-YYYY', () => { - expect(getMonthFromExpirationDateString(longDateHyphen)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(longDateHyphen)).toBe(expectedYear); - }); - - it('Should format expirationDate month and year to MMYYYY', () => { - expect(getMonthFromExpirationDateString(longDate)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(longDate)).toBe(expectedYear); - }); - - it('Should format expirationDate month and year to MM/YY', () => { - expect(getMonthFromExpirationDateString(shortDateSlashed)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(shortDateSlashed)).toBe(expectedYear); - }); - - it('Should format expirationDate month and year to MM-YY', () => { - expect(getMonthFromExpirationDateString(shortDateHyphen)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(shortDateHyphen)).toBe(expectedYear); - }); - - it('Should format expirationDate month and year to MMYY', () => { - expect(getMonthFromExpirationDateString(shortDate)).toBe(expectedMonth); - expect(getYearFromExpirationDateString(shortDate)).toBe(expectedYear); - }); - - it('Should format to MM/YYYY given MM/YY', () => { - expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); - expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); - }); - - it('Should format to MM/YYYY given MMYY', () => { - expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); - expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); - }); - }); - - describe('isCustomFeed', () => { - it('Should return true for the custom visa feed with no number', () => { - const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - it('Should return true for the custom visa feed with a number', () => { - const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}1` as CompanyCardFeedWithNumber; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - it('Should return true for the custom mastercard feed with no number', () => { - const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - it('Should return true for the custom mastercard feed with a number', () => { - const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}3` as CompanyCardFeedWithNumber; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - it('Should return true for the custom amex feed with no number', () => { - const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - it('Should return true for the custom amex feed with a number', () => { - const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}2` as CompanyCardFeedWithNumber; - const isCustomFeed = isCustomFeedCardUtils(customFeed); - expect(isCustomFeed).toBe(true); - }); - - test.each(directFeedBanks)('Should return false for the direct feed %s', (directFeed) => { - const isCustomFeed = isCustomFeedCardUtils(directFeed); - expect(isCustomFeed).toBe(false); - }); - }); - - describe('getCompanyFeeds', () => { - it('Should return both custom and direct feeds with filtered out "Expensify Card" bank', () => { - const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_1); - expect(companyFeeds).toStrictEqual(companyCardsSettingsWithoutExpensifyBank); - }); - - it('Should return direct feeds only since custom feeds are not exist', () => { - const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_2); - expect(companyFeeds).toStrictEqual(companyCardsDirectFeedSettings); - }); - - it('Should return custom feeds only with filtered out "Expensify Card" bank since direct feeds are not exist', () => { - const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_3); - expect(companyFeeds).toStrictEqual(companyCardsCustomFeedSettingsWithoutExpensifyBank); - }); - - it('Should return empty object if undefined is passed', () => { - const companyFeeds = getCompanyFeeds(undefined); - expect(companyFeeds).toStrictEqual({}); - }); - - it('Should return only feeds that are not pending', () => { - const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_6, false, true); - expect(Object.keys(companyFeeds).length).toStrictEqual(1); - }); - }); - - describe('getSelectedFeed', () => { - it('Should return last selected custom feed', () => { - const lastSelectedCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; - const selectedFeed = getSelectedFeed(lastSelectedCustomFeed, cardFeedsCollection.FAKE_ID_1); - expect(selectedFeed).toBe(lastSelectedCustomFeed); - }); - - it('Should return last selected direct feed', () => { - const lastSelectedDirectFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE; - const selectedFeed = getSelectedFeed(lastSelectedDirectFeed, cardFeedsCollection.FAKE_ID_1); - expect(selectedFeed).toBe(lastSelectedDirectFeed); - }); - - it('Should return the first available custom feed if lastSelectedFeed is undefined', () => { - const lastSelectedFeed = undefined; - const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_3); - expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); - }); - - it('Should return the first available direct feed if lastSelectedFeed is undefined', () => { - const lastSelectedFeed = undefined; - const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_2); - expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); - }); - - it('Should return undefined if lastSelectedFeed is undefined and there is no card feeds', () => { - const lastSelectedFeed = undefined; - const cardFeeds = undefined; - const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - expect(selectedFeed).toBe(undefined); - }); - }); - - describe('getCustomOrFormattedFeedName', () => { - beforeAll(() => { - IntlStore.load(CONST.LOCALES.EN); - return waitForBatchedUpdates(); - }); - it('Should return custom name if exists', () => { - const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; - const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; - const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); - expect(feedName).toBe(customFeedName); - }); - - it('Should return formatted name if there is no custom name', () => { - const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; - const companyCardNicknames = cardFeedsCollection.FAKE_ID_3?.settings?.companyCardNicknames; - const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); - expect(feedName).toBe('Visa cards'); - }); - - it('Should return undefined if no feed provided', () => { - const feed = undefined; - const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; - const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); - expect(feedName).toBe(undefined); - }); - }); - - describe('lastFourNumbersFromCardName', () => { - it('Should return last 4 numbers from the card name', () => { - const lastFour = lastFourNumbersFromCardName('Business Card Cash - 3001'); - expect(lastFour).toBe('3001'); - }); - - it('Should return empty string if card number does not have space', () => { - const lastFour = lastFourNumbersFromCardName('480801XXXXXX2554'); - expect(lastFour).toBe(''); - }); - - it('Should return empty string if card number does not have number in the end with dash', () => { - const lastFour = lastFourNumbersFromCardName('Business Card Cash - Business'); - expect(lastFour).toBe(''); - }); - }); - - describe('maskCardNumber', () => { - it("Should return the card number divided into chunks of 4, with 'X' replaced by '•' if it's provided in the '480801XXXXXX2554' format", () => { - const cardNumber = '480801XXXXXX2554'; - const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); - expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); - }); - - it('Should return card number without changes if it has empty space', () => { - const cardNumber = 'CREDIT CARD...6607'; - const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); - expect(maskedCardNumber).toBe(cardNumber); - }); - - it("Should return the Amex direct feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { - const cardNumber = '211944XXXXX6557'; - const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT); - expect(maskedCardNumber).toBe('2119 44•••• •6557'); - }); - - it("Should return the Amex custom feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { - const cardNumber = '211944XXXXX6557'; - const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX); - expect(maskedCardNumber).toBe('2119 44•••• •6557'); - }); - - it('Should return masked card number even if undefined feed was provided', () => { - const cardNumber = '480801XXXXXX2554'; - const maskedCardNumber = maskCardNumber(cardNumber, undefined); - expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); - }); - - it('Should return empty string if invalid card name was provided', () => { - const maskedCardNumber = maskCardNumber('', CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); - expect(maskedCardNumber).toBe(''); - }); - - it('Should return card name without last 4 numbers', () => { - const maskedCardNumber = maskCardNumber('Business Card Cash - 3001', undefined); - expect(maskedCardNumber).toBe('Business Card Cash'); - }); - }); - - describe('getCardFeedName', () => { - it('Should return a valid name if a valid feed was provided', () => { - const feed = 'vcf'; - const feedName = getBankName(feed); - expect(feedName).toBe('Visa'); - }); - - it('Should return a valid name if an OldDot feed variation was provided', () => { - const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; - const feedName = getBankName(feed); - expect(feedName).toBe('American Express'); - }); - - it('Should return a valid name if a CSV imported feed variation was provided', () => { - const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; - const feedName = getBankName(feed); - expect(feedName).toBe('CSV'); - }); - - it('Should return empty string if invalid feed was provided', () => { - const feed = 'vvcf' as CompanyCardFeed; - const feedName = getBankName(feed); - expect(feedName).toBe(''); - }); - - it('Should return empty string if feed is not provided (instead of TypeError crashing the app)', () => { - const feed = undefined; - const feedName = getBankName(feed as unknown as CompanyCardFeed); - expect(feedName).toBe(''); - }); - }); - - describe('getCardFeedIcon', () => { - it('Should return a valid illustration if a valid feed was provided', () => { - const feed = 'vcf'; - const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('VisaCompanyCardDetailLarge'); - }); - - it('Should return a valid illustration if an OldDot feed variation was provided', () => { - const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; - const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('AmexCardCompanyCardDetailLarge'); - }); - - it('Should return a valid illustration if a CSV imported feed variation was provided', () => { - const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; - const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('GenericCSVCompanyCardLarge'); - }); - - it('Should return valid illustration if a non-matching feed was provided', () => { - const feed = '666' as CompanyCardFeed; - const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('GenericCompanyCardLarge'); - }); - }); - - describe('getBankCardDetailsImage', () => { - it('Should return a valid illustration if a valid bank name was provided', () => { - const bank = 'American Express'; - const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('AmexCardCompanyCardDetail'); - }); - - it('Should return a valid illustration if Other bank name was provided', () => { - const bank = 'Other'; - const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); - expect(illustration).toBe('GenericCompanyCard'); - }); - }); - - describe('getFilteredCardList', () => { - it('Should return filtered custom feed cards list', () => { - const cardsList = getFilteredCardList(customFeedCardsList, undefined, undefined); - expect(cardsList).toStrictEqual({ - // eslint-disable-next-line @typescript-eslint/naming-convention - '480801XXXXXX2111': 'ENCRYPTED_CARD_NUMBER', - // eslint-disable-next-line @typescript-eslint/naming-convention - '480801XXXXXX2566': 'ENCRYPTED_CARD_NUMBER', - }); - }); - - it('Should return filtered direct feed cards list with a single card', () => { - const cardsList = getFilteredCardList(directFeedCardsSingleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE], undefined); - // eslint-disable-next-line @typescript-eslint/naming-convention - expect(cardsList).toStrictEqual({'CREDIT CARD...6607': 'CREDIT CARD...6607'}); - }); - - it('Should return filtered direct feed cards list with multiple cards', () => { - const cardsList = getFilteredCardList(directFeedCardsMultipleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE], undefined); - expect(cardsList).toStrictEqual({ - // eslint-disable-next-line @typescript-eslint/naming-convention - 'CREDIT CARD...1233': 'CREDIT CARD...1233', - // eslint-disable-next-line @typescript-eslint/naming-convention - 'CREDIT CARD...3333': 'CREDIT CARD...3333', - // eslint-disable-next-line @typescript-eslint/naming-convention - 'CREDIT CARD...7788': 'CREDIT CARD...7788', - }); - }); - - it('Should return empty object if no data was provided', () => { - const cardsList = getFilteredCardList(undefined, undefined, undefined); - expect(cardsList).toStrictEqual({}); - }); - - it('Should handle the case when all cards are already assigned in other workspaces', () => { - const assignedCard1 = 'CREDIT CARD...5566'; - const assignedCard2 = 'CREDIT CARD...6677'; - - const mockAllWorkspaceCards = { - cards_888888_feed: { - '11111': { - accountID: 999999, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, - cardID: 11111, - cardName: assignedCard1, - domainName: 'other-workspace.exfy', - state: 3, - }, - '22222': { - accountID: 999999, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, - cardID: 22222, - cardName: assignedCard2, - domainName: 'other-workspace.exfy', - state: 3, - }, - }, - } as unknown as OnyxCollection; - - const customFeedWithAllAssignedCards = { - cardList: { - [assignedCard1]: 'ENCRYPTED_DATA', - [assignedCard2]: 'ENCRYPTED_DATA', - }, - } as unknown as WorkspaceCardsList; - const filteredCards = getFilteredCardList(customFeedWithAllAssignedCards, undefined, mockAllWorkspaceCards); - expect(filteredCards).toStrictEqual({}); - }); - - it('Should filter out cards that are already assigned in another workspace (custom feed)', () => { - const customFeedWorkspaceCardsList = { - '21310091': { - accountID: 18439984, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, - cardID: 21310091, - cardName: '480801XXXXXX2554', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2554', - lastUpdated: '', - lastScrape: '2024-11-27 11:00:53', - scrapeMinDate: '2024-10-17', - state: 3, - }, - '21310092': { - accountID: 18439985, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, - cardID: 21310092, - cardName: '480801XXXXXX2666', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2666', - lastUpdated: '', - lastScrape: '2024-11-27 11:00:53', - scrapeMinDate: '2024-10-17', - state: 3, - }, - cardList: { - '480801XXXXXX2554': 'ENCRYPTED_CARD_NUMBER', - '480801XXXXXX2666': 'ENCRYPTED_CARD_NUMBER', - }, - } as unknown as WorkspaceCardsList; - - const filteredCards = getFilteredCardList(customFeedWorkspaceCardsList, undefined, undefined); - expect(filteredCards).toStrictEqual({}); - }); - - it('Should filter out cards that are already assigned in another workspace (direct feed)', () => { - const assignedCard1 = 'CREDIT CARD...3344'; - const assignedCard2 = 'CREDIT CARD...3355'; - const unassignedCard = 'CREDIT CARD...6666'; - - const mockAllWorkspaceCards = { - cards_888888_feed: { - '67889': { - accountID: 999998, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, - cardID: 67889, - cardName: assignedCard1, - domainName: 'other-workspace.exfy', - state: 3, - }, - '67890': { - accountID: 999999, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, - cardID: 67890, - cardName: assignedCard2, - domainName: 'other-workspace.exfy', - state: 3, - }, - }, - } as unknown as OnyxCollection; - const directFeedWithAssignedCard = { - accountList: [assignedCard1, assignedCard2, unassignedCard], - } as unknown as (typeof oAuthAccountDetails)[typeof CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]; - const filteredCards = getFilteredCardList(undefined, directFeedWithAssignedCard, mockAllWorkspaceCards); - expect(filteredCards).toStrictEqual({[`${unassignedCard}`]: unassignedCard}); - }); - }); - - describe('getFeedType', () => { - it('should return the feed name with a consecutive number, if there is already a feed with a number', () => { - const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_4); - expect(feedType).toBe('vcf2'); - }); - - it('should return the feed name with 1, if there is already a feed without a number', () => { - const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_3); - expect(feedType).toBe('vcf1'); - }); - - it('should return the feed name with with the first smallest available number', () => { - const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_5); - expect(feedType).toBe('vcf2'); - }); - }); - - describe('flatAllCardsList', () => { - it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => { - const workspaceAccountID = 11111111; - const flattenedCardsList = flatAllCardsList(allCardsList, workspaceAccountID); - const {cardList, ...customCards} = customFeedCardsList; - expect(flattenedCardsList).toStrictEqual({ - ...directFeedCardsMultipleList, - ...customCards, - }); - }); - - it('should return undefined if not defined cards list was provided', () => { - const workspaceAccountID = 11111111; - const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID); - expect(flattenedCardsList).toBeUndefined(); - }); - }); - - describe('checkIfFeedConnectionIsBroken', () => { - it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { - expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); - }); - - it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { - expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); - }); - - it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 531', () => { - expect(checkIfFeedConnectionIsBroken(commercialFeedCardsSingleList)).toBeFalsy(); - }); - - it('should return false if no feed(s) cards are provided', () => { - expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); - }); - - it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { - const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; - const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; - expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); - }); - }); - - describe('checkIfFeedConnectionIsBroken', () => { - it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { - expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); - }); - - it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { - expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); - }); - - it('should return false if no feed(s) cards are provided', () => { - expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); - }); - - it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { - const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; - const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; - expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); - }); - }); - - describe('hasIssuedExpensifyCard', () => { - it('should return true when Expensify Card was issued for given workspace', () => { - const workspaceAccountID = 11111111; - expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(true); - }); - - it('should return false when Expensify Card was not issued for given workspace', () => { - const workspaceAccountID = 11111111; - expect(hasIssuedExpensifyCard(workspaceAccountID, {})).toBe(false); - }); - - it('should not erroneously return true when workspaceAccountID is 0', () => { - const workspaceAccountID = 0; - expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(false); - }); - }); - - describe('getAllCardsForWorkspace', () => { - it('should return all cards for a given workspace', () => { - const workspaceAccountID = 11111111; - expect(getAllCardsForWorkspace(workspaceAccountID, allCardsList)).toEqual({ - '21310091': { - accountID: 18439984, - bank: 'vcf', - cardID: 21310091, - cardName: '480801XXXXXX2554', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2554', - lastScrape: '2024-11-27 11:00:53', - lastUpdated: '', - scrapeMinDate: '2024-10-17', - state: 3, - }, - '21570655': { - accountID: 18439984, - bank: 'oauth.capitalone.com', - cardID: 21570655, - cardName: 'CREDIT CARD...5678', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '5678', - lastScrape: '', - lastScrapeResult: 200, - lastUpdated: '', - scrapeMinDate: '2024-08-27', - state: 3, - }, - '21570656': { - accountID: 18439984, - bank: 'oauth.capitalone.com', - cardID: 21570656, - cardName: 'CREDIT CARD...4444', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '5678', - lastScrape: '', - lastScrapeResult: 403, - lastUpdated: '', - scrapeMinDate: '2024-08-27', - state: 3, - }, - '21570657': { - accountID: 18439984, - bank: 'Expensify Card', - cardID: 21570657, - cardName: 'CREDIT CARD...5644', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - }); - }); - }); - - describe('isExpensifyCardFullySetUp', () => { - it('should return true when policy has enabled cards and cardSettings has payment bank account ID', () => { - const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithPaymentBankAccountID); - expect(result).toBe(true); - }); - - it('should return false when policy has disabled cards', () => { - const result = isExpensifyCardFullySetUp(policyWithCardsDisabled, cardSettingsWithoutPaymentBankAccountID); - expect(result).toBe(false); - }); - - it('should return false when cardSettings has no payment bank account ID', () => { - const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithoutPaymentBankAccountID); - expect(result).toBe(false); - }); - - it('should return false when cardSettings is undefined', () => { - const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, undefined); - expect(result).toBe(false); - }); - - it('should return false when both policy and cardSettings are undefined', () => { - const result = isExpensifyCardFullySetUp(undefined, undefined); - expect(result).toBe(false); - }); - }); - - describe('filterInactiveCards', () => { - it('should filter out closed, deactivated and suspended cards', () => { - const activeCards = {card1: {cardID: 1, state: CONST.EXPENSIFY_CARD.STATE.OPEN}}; - const closedCards = { - card2: {cardID: 2, state: CONST.EXPENSIFY_CARD.STATE.CLOSED}, - card3: {cardID: 3, state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED}, - card4: {cardID: 4, state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED}, - }; - const cardList = {...activeCards, ...closedCards} as unknown as CardList; - const filteredList = filterInactiveCards(cardList); - expect(filteredList).toEqual(activeCards); - }); - - it('should return an empty object if undefined card list is passed', () => { - const cards = filterInactiveCards(undefined); - expect(cards).toEqual({}); - }); - }); - - describe('sortCardsByCardholderName', () => { - const mockPersonalDetails: PersonalDetailsList = { - 1: { - accountID: 1, - login: 'john@example.com', - displayName: 'John Doe', - firstName: 'John', - lastName: 'Doe', - }, - 2: { - accountID: 2, - login: 'jane@example.com', - displayName: 'Jane Smith', - firstName: 'Jane', - lastName: 'Smith', - }, - 3: { - accountID: 3, - login: 'unknown@example.com', - // No displayName or firstName/lastName - }, - }; - - const mockCards: WorkspaceCardsList = { - '1': { - cardID: 1, - accountID: 1, - cardName: 'Card 1', - bank: 'expensify', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - '2': { - cardID: 2, - accountID: 2, - bank: 'expensify', - cardName: 'Card 2', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - '3': { - cardID: 3, - accountID: 3, - bank: 'expensify', - cardName: 'Card 3', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - }; - - it('should sort cards by cardholder name in ascending order', () => { - const policyMembersAccountIDs = [1, 2, 3]; - const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); - const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); - - expect(sortedCards).toHaveLength(3); - expect(sortedCards.at(0)?.cardID).toBe(2); - expect(sortedCards.at(1)?.cardID).toBe(1); - expect(sortedCards.at(2)?.cardID).toBe(3); - }); - - it('should filter out cards that are not associated with policy members', () => { - const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3 - const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); - const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); - - expect(sortedCards).toHaveLength(2); - expect(sortedCards.at(0)?.cardID).toBe(2); - expect(sortedCards.at(1)?.cardID).toBe(1); - }); - - it('should handle undefined cardsList', () => { - const policyMembersAccountIDs = [1, 2, 3]; - const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs); - const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); - - expect(sortedCards).toHaveLength(0); - }); - - it('should handle undefined personalDetails', () => { - const policyMembersAccountIDs = [1, 2, 3]; - const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); - const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare); - - expect(sortedCards).toHaveLength(3); - // All cards should be sorted with default names - expect(sortedCards.at(0)?.cardID).toBe(1); - expect(sortedCards.at(1)?.cardID).toBe(2); - expect(sortedCards.at(2)?.cardID).toBe(3); - }); - - it('should handle cards with missing accountID', () => { - const cardsWithMissingAccountID: WorkspaceCardsList = { - '1': { - cardID: 1, - accountID: 1, - cardName: 'Card 1', - bank: 'expensify', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - '2': { - cardID: 2, - cardName: 'Card 2', - bank: 'expensify', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - }; - - const policyMembersAccountIDs = [1, 2]; - const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs); - const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); - - expect(sortedCards).toHaveLength(1); - expect(sortedCards.at(0)?.cardID).toBe(1); - }); - }); - - describe('getCardDescription', () => { - it('should return the correct card description for company card', () => { - const card: Card = { - accountID: 18439984, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, - cardID: 21310091, - cardName: '480801XXXXXX2554', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2554', - lastUpdated: '', - lastScrape: '2024-11-27 11:00:53', - scrapeMinDate: '2024-10-17', - state: 3, - }; - const description = getCardDescription(card); - expect(description).toBe('Visa - 2554'); - }); - - it('should return the correct card description for Expensify card', () => { - const card: Card = { - accountID: 18439984, - bank: CONST.EXPENSIFY_CARD.BANK, - cardID: 21570657, - cardName: 'CREDIT CARD...5644', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }; - const description = getCardDescription(card); - expect(description).toBe('Expensify Card'); - }); - }); - - describe('isExpensifyCard', () => { - it('should return true for Expensify Card', () => { - const card: Card = { - accountID: 18439984, - bank: CONST.EXPENSIFY_CARD.BANK, - cardID: 21570657, - cardName: 'CREDIT CARD...5644', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }; - expect(isExpensifyCard(card)).toBe(true); - }); - - it('should return false for non-Expensify Card', () => { - const card: Card = { - accountID: 18439984, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, - cardID: 21310091, - cardName: '480801XXXXXX2554', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2554', - lastUpdated: '', - lastScrape: '2024-11-27 11:00:53', - scrapeMinDate: '2024-10-17', - state: 3, - }; - expect(isExpensifyCard(card)).toBe(false); - }); - }); - - describe('getCompanyCardDescription', () => { - const cardList: CardList = { - '21310091': { - accountID: 18439984, - bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, - cardID: 21310091, - cardName: '480801XXXXXX2554', - domainName: 'expensify-policy41314f4dc5ce25af.exfy', - fraud: 'none', - lastFourPAN: '2554', - lastUpdated: '', - lastScrape: '2024-11-27 11:00:53', - scrapeMinDate: '2024-10-17', - state: 3, - }, - '21570657': { - accountID: 18439984, - bank: CONST.EXPENSIFY_CARD.BANK, - cardID: 21570657, - cardName: 'CREDIT CARD...5644', - domainName: 'expensify-policy17f617b9fe23d2f1.exfy', - fraud: 'none', - lastFourPAN: '', - lastScrape: '', - lastUpdated: '', - state: 2, - }, - }; - it('should return the correct description for a company card', () => { - const description = getCompanyCardDescription('Test', 21310091, cardList); - expect(description).toBe('480801XXXXXX2554'); - }); - - it('should return the correct description for an Expensify card', () => { - const description = getCompanyCardDescription('Test', 21570657, cardList); - expect(description).toBe('Test'); - }); - }); - - describe('Expensify card sort comparator', () => { - it('should not change the order of non-Expensify cards', () => { - const cardList = { - 10: {cardID: 10, bank: 'chase'}, // non-Expensify - 11: {cardID: 11, bank: 'chase'}, // non-Expensify - } as unknown as CardList; - - const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); - expect(sorted.map((r: Card) => r.cardID)).toEqual([10, 11]); - }); - - it('places physical Expensify card before its virtual sibling', () => { - const cardList = { - 10: {cardID: 10, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {isVirtual: true}}, // Expensify virtual - 11: {cardID: 11, bank: CONST.EXPENSIFY_CARD.BANK}, // Expensify physical - 99: {cardID: 99, bank: 'chase'}, // non-Expensify - } as unknown as CardList; - - const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); - expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]); - }); - }); -}); +// describe('CardUtils', () => { +// describe('Expiration date formatting', () => { +// it('Should format expirationDate month and year to MM/YYYY', () => { +// expect(getMonthFromExpirationDateString(longDateSlashed)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(longDateSlashed)).toBe(expectedYear); +// }); +// +// it('Should format expirationDate month and year to MM-YYYY', () => { +// expect(getMonthFromExpirationDateString(longDateHyphen)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(longDateHyphen)).toBe(expectedYear); +// }); +// +// it('Should format expirationDate month and year to MMYYYY', () => { +// expect(getMonthFromExpirationDateString(longDate)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(longDate)).toBe(expectedYear); +// }); +// +// it('Should format expirationDate month and year to MM/YY', () => { +// expect(getMonthFromExpirationDateString(shortDateSlashed)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(shortDateSlashed)).toBe(expectedYear); +// }); +// +// it('Should format expirationDate month and year to MM-YY', () => { +// expect(getMonthFromExpirationDateString(shortDateHyphen)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(shortDateHyphen)).toBe(expectedYear); +// }); +// +// it('Should format expirationDate month and year to MMYY', () => { +// expect(getMonthFromExpirationDateString(shortDate)).toBe(expectedMonth); +// expect(getYearFromExpirationDateString(shortDate)).toBe(expectedYear); +// }); +// +// it('Should format to MM/YYYY given MM/YY', () => { +// expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); +// expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); +// }); +// +// it('Should format to MM/YYYY given MMYY', () => { +// expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); +// expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); +// }); +// }); +// +// describe('isCustomFeed', () => { +// it('Should return true for the custom visa feed with no number', () => { +// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// it('Should return true for the custom visa feed with a number', () => { +// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}1` as CompanyCardFeedWithNumber; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// it('Should return true for the custom mastercard feed with no number', () => { +// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// it('Should return true for the custom mastercard feed with a number', () => { +// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}3` as CompanyCardFeedWithNumber; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// it('Should return true for the custom amex feed with no number', () => { +// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// it('Should return true for the custom amex feed with a number', () => { +// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}2` as CompanyCardFeedWithNumber; +// const isCustomFeed = isCustomFeedCardUtils(customFeed); +// expect(isCustomFeed).toBe(true); +// }); +// +// test.each(directFeedBanks)('Should return false for the direct feed %s', (directFeed) => { +// const isCustomFeed = isCustomFeedCardUtils(directFeed); +// expect(isCustomFeed).toBe(false); +// }); +// }); +// +// describe('getCompanyFeeds', () => { +// it('Should return both custom and direct feeds with filtered out "Expensify Card" bank', () => { +// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_1); +// expect(companyFeeds).toStrictEqual(companyCardsSettingsWithoutExpensifyBank); +// }); +// +// it('Should return direct feeds only since custom feeds are not exist', () => { +// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_2); +// expect(companyFeeds).toStrictEqual(companyCardsDirectFeedSettings); +// }); +// +// it('Should return custom feeds only with filtered out "Expensify Card" bank since direct feeds are not exist', () => { +// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_3); +// expect(companyFeeds).toStrictEqual(companyCardsCustomFeedSettingsWithoutExpensifyBank); +// }); +// +// it('Should return empty object if undefined is passed', () => { +// const companyFeeds = getCompanyFeeds(undefined); +// expect(companyFeeds).toStrictEqual({}); +// }); +// +// it('Should return only feeds that are not pending', () => { +// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_6, false, true); +// expect(Object.keys(companyFeeds).length).toStrictEqual(1); +// }); +// }); +// +// describe('getSelectedFeed', () => { +// it('Should return last selected custom feed', () => { +// const lastSelectedCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; +// const selectedFeed = getSelectedFeed(lastSelectedCustomFeed, cardFeedsCollection.FAKE_ID_1); +// expect(selectedFeed).toBe(lastSelectedCustomFeed); +// }); +// +// it('Should return last selected direct feed', () => { +// const lastSelectedDirectFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE; +// const selectedFeed = getSelectedFeed(lastSelectedDirectFeed, cardFeedsCollection.FAKE_ID_1); +// expect(selectedFeed).toBe(lastSelectedDirectFeed); +// }); +// +// it('Should return the first available custom feed if lastSelectedFeed is undefined', () => { +// const lastSelectedFeed = undefined; +// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_3); +// expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); +// }); +// +// it('Should return the first available direct feed if lastSelectedFeed is undefined', () => { +// const lastSelectedFeed = undefined; +// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_2); +// expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); +// }); +// +// it('Should return undefined if lastSelectedFeed is undefined and there is no card feeds', () => { +// const lastSelectedFeed = undefined; +// const cardFeeds = undefined; +// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); +// expect(selectedFeed).toBe(undefined); +// }); +// }); +// +// describe('getCustomOrFormattedFeedName', () => { +// beforeAll(() => { +// IntlStore.load(CONST.LOCALES.EN); +// return waitForBatchedUpdates(); +// }); +// it('Should return custom name if exists', () => { +// const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; +// const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; +// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); +// expect(feedName).toBe(customFeedName); +// }); +// +// it('Should return formatted name if there is no custom name', () => { +// const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; +// const companyCardNicknames = cardFeedsCollection.FAKE_ID_3?.settings?.companyCardNicknames; +// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); +// expect(feedName).toBe('Visa cards'); +// }); +// +// it('Should return undefined if no feed provided', () => { +// const feed = undefined; +// const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; +// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); +// expect(feedName).toBe(undefined); +// }); +// }); +// +// describe('lastFourNumbersFromCardName', () => { +// it('Should return last 4 numbers from the card name', () => { +// const lastFour = lastFourNumbersFromCardName('Business Card Cash - 3001'); +// expect(lastFour).toBe('3001'); +// }); +// +// it('Should return empty string if card number does not have space', () => { +// const lastFour = lastFourNumbersFromCardName('480801XXXXXX2554'); +// expect(lastFour).toBe(''); +// }); +// +// it('Should return empty string if card number does not have number in the end with dash', () => { +// const lastFour = lastFourNumbersFromCardName('Business Card Cash - Business'); +// expect(lastFour).toBe(''); +// }); +// }); +// +// describe('maskCardNumber', () => { +// it("Should return the card number divided into chunks of 4, with 'X' replaced by '•' if it's provided in the '480801XXXXXX2554' format", () => { +// const cardNumber = '480801XXXXXX2554'; +// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); +// expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); +// }); +// +// it('Should return card number without changes if it has empty space', () => { +// const cardNumber = 'CREDIT CARD...6607'; +// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); +// expect(maskedCardNumber).toBe(cardNumber); +// }); +// +// it("Should return the Amex direct feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { +// const cardNumber = '211944XXXXX6557'; +// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT); +// expect(maskedCardNumber).toBe('2119 44•••• •6557'); +// }); +// +// it("Should return the Amex custom feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { +// const cardNumber = '211944XXXXX6557'; +// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX); +// expect(maskedCardNumber).toBe('2119 44•••• •6557'); +// }); +// +// it('Should return masked card number even if undefined feed was provided', () => { +// const cardNumber = '480801XXXXXX2554'; +// const maskedCardNumber = maskCardNumber(cardNumber, undefined); +// expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); +// }); +// +// it('Should return empty string if invalid card name was provided', () => { +// const maskedCardNumber = maskCardNumber('', CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); +// expect(maskedCardNumber).toBe(''); +// }); +// +// it('Should return card name without last 4 numbers', () => { +// const maskedCardNumber = maskCardNumber('Business Card Cash - 3001', undefined); +// expect(maskedCardNumber).toBe('Business Card Cash'); +// }); +// }); +// +// describe('getCardFeedName', () => { +// it('Should return a valid name if a valid feed was provided', () => { +// const feed = 'vcf'; +// const feedName = getBankName(feed); +// expect(feedName).toBe('Visa'); +// }); +// +// it('Should return a valid name if an OldDot feed variation was provided', () => { +// const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; +// const feedName = getBankName(feed); +// expect(feedName).toBe('American Express'); +// }); +// +// it('Should return a valid name if a CSV imported feed variation was provided', () => { +// const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; +// const feedName = getBankName(feed); +// expect(feedName).toBe('CSV'); +// }); +// +// it('Should return empty string if invalid feed was provided', () => { +// const feed = 'vvcf' as CompanyCardFeed; +// const feedName = getBankName(feed); +// expect(feedName).toBe(''); +// }); +// +// it('Should return empty string if feed is not provided (instead of TypeError crashing the app)', () => { +// const feed = undefined; +// const feedName = getBankName(feed as unknown as CompanyCardFeed); +// expect(feedName).toBe(''); +// }); +// }); +// +// describe('getCardFeedIcon', () => { +// it('Should return a valid illustration if a valid feed was provided', () => { +// const feed = 'vcf'; +// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('VisaCompanyCardDetailLarge'); +// }); +// +// it('Should return a valid illustration if an OldDot feed variation was provided', () => { +// const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; +// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('AmexCardCompanyCardDetailLarge'); +// }); +// +// it('Should return a valid illustration if a CSV imported feed variation was provided', () => { +// const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; +// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('GenericCSVCompanyCardLarge'); +// }); +// +// it('Should return valid illustration if a non-matching feed was provided', () => { +// const feed = '666' as CompanyCardFeed; +// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('GenericCompanyCardLarge'); +// }); +// }); +// +// describe('getBankCardDetailsImage', () => { +// it('Should return a valid illustration if a valid bank name was provided', () => { +// const bank = 'American Express'; +// const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('AmexCardCompanyCardDetail'); +// }); +// +// it('Should return a valid illustration if Other bank name was provided', () => { +// const bank = 'Other'; +// const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); +// expect(illustration).toBe('GenericCompanyCard'); +// }); +// }); +// +// describe('getFilteredCardList', () => { +// it('Should return filtered custom feed cards list', () => { +// const cardsList = getFilteredCardList(customFeedCardsList, undefined, undefined); +// expect(cardsList).toStrictEqual({ +// // eslint-disable-next-line @typescript-eslint/naming-convention +// '480801XXXXXX2111': 'ENCRYPTED_CARD_NUMBER', +// // eslint-disable-next-line @typescript-eslint/naming-convention +// '480801XXXXXX2566': 'ENCRYPTED_CARD_NUMBER', +// }); +// }); +// +// it('Should return filtered direct feed cards list with a single card', () => { +// const cardsList = getFilteredCardList(directFeedCardsSingleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE], undefined); +// // eslint-disable-next-line @typescript-eslint/naming-convention +// expect(cardsList).toStrictEqual({'CREDIT CARD...6607': 'CREDIT CARD...6607'}); +// }); +// +// it('Should return filtered direct feed cards list with multiple cards', () => { +// const cardsList = getFilteredCardList(directFeedCardsMultipleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE], undefined); +// expect(cardsList).toStrictEqual({ +// // eslint-disable-next-line @typescript-eslint/naming-convention +// 'CREDIT CARD...1233': 'CREDIT CARD...1233', +// // eslint-disable-next-line @typescript-eslint/naming-convention +// 'CREDIT CARD...3333': 'CREDIT CARD...3333', +// // eslint-disable-next-line @typescript-eslint/naming-convention +// 'CREDIT CARD...7788': 'CREDIT CARD...7788', +// }); +// }); +// +// it('Should return empty object if no data was provided', () => { +// const cardsList = getFilteredCardList(undefined, undefined, undefined); +// expect(cardsList).toStrictEqual({}); +// }); +// +// it('Should handle the case when all cards are already assigned in other workspaces', () => { +// const assignedCard1 = 'CREDIT CARD...5566'; +// const assignedCard2 = 'CREDIT CARD...6677'; +// +// const mockAllWorkspaceCards = { +// cards_888888_feed: { +// '11111': { +// accountID: 999999, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, +// cardID: 11111, +// cardName: assignedCard1, +// domainName: 'other-workspace.exfy', +// state: 3, +// }, +// '22222': { +// accountID: 999999, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, +// cardID: 22222, +// cardName: assignedCard2, +// domainName: 'other-workspace.exfy', +// state: 3, +// }, +// }, +// } as unknown as OnyxCollection; +// +// const customFeedWithAllAssignedCards = { +// cardList: { +// [assignedCard1]: 'ENCRYPTED_DATA', +// [assignedCard2]: 'ENCRYPTED_DATA', +// }, +// } as unknown as WorkspaceCardsList; +// const filteredCards = getFilteredCardList(customFeedWithAllAssignedCards, undefined, mockAllWorkspaceCards); +// expect(filteredCards).toStrictEqual({}); +// }); +// +// it('Should filter out cards that are already assigned in another workspace (custom feed)', () => { +// const customFeedWorkspaceCardsList = { +// '21310091': { +// accountID: 18439984, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, +// cardID: 21310091, +// cardName: '480801XXXXXX2554', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2554', +// lastUpdated: '', +// lastScrape: '2024-11-27 11:00:53', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }, +// '21310092': { +// accountID: 18439985, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, +// cardID: 21310092, +// cardName: '480801XXXXXX2666', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2666', +// lastUpdated: '', +// lastScrape: '2024-11-27 11:00:53', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }, +// cardList: { +// '480801XXXXXX2554': 'ENCRYPTED_CARD_NUMBER', +// '480801XXXXXX2666': 'ENCRYPTED_CARD_NUMBER', +// }, +// } as unknown as WorkspaceCardsList; +// +// const filteredCards = getFilteredCardList(customFeedWorkspaceCardsList, undefined, undefined); +// expect(filteredCards).toStrictEqual({}); +// }); +// +// it('Should filter out cards that are already assigned in another workspace (direct feed)', () => { +// const assignedCard1 = 'CREDIT CARD...3344'; +// const assignedCard2 = 'CREDIT CARD...3355'; +// const unassignedCard = 'CREDIT CARD...6666'; +// +// const mockAllWorkspaceCards = { +// cards_888888_feed: { +// '67889': { +// accountID: 999998, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, +// cardID: 67889, +// cardName: assignedCard1, +// domainName: 'other-workspace.exfy', +// state: 3, +// }, +// '67890': { +// accountID: 999999, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, +// cardID: 67890, +// cardName: assignedCard2, +// domainName: 'other-workspace.exfy', +// state: 3, +// }, +// }, +// } as unknown as OnyxCollection; +// const directFeedWithAssignedCard = { +// accountList: [assignedCard1, assignedCard2, unassignedCard], +// } as unknown as (typeof oAuthAccountDetails)[typeof CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]; +// const filteredCards = getFilteredCardList(undefined, directFeedWithAssignedCard, mockAllWorkspaceCards); +// expect(filteredCards).toStrictEqual({[`${unassignedCard}`]: unassignedCard}); +// }); +// }); +// +// describe('getFeedType', () => { +// it('should return the feed name with a consecutive number, if there is already a feed with a number', () => { +// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_4); +// expect(feedType).toBe('vcf2'); +// }); +// +// it('should return the feed name with 1, if there is already a feed without a number', () => { +// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_3); +// expect(feedType).toBe('vcf1'); +// }); +// +// it('should return the feed name with with the first smallest available number', () => { +// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_5); +// expect(feedType).toBe('vcf2'); +// }); +// }); +// +// describe('flatAllCardsList', () => { +// it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => { +// const workspaceAccountID = 11111111; +// const flattenedCardsList = flatAllCardsList(allCardsList, workspaceAccountID); +// const {cardList, ...customCards} = customFeedCardsList; +// expect(flattenedCardsList).toStrictEqual({ +// ...directFeedCardsMultipleList, +// ...customCards, +// }); +// }); +// +// it('should return undefined if not defined cards list was provided', () => { +// const workspaceAccountID = 11111111; +// const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID); +// expect(flattenedCardsList).toBeUndefined(); +// }); +// }); +// +// describe('checkIfFeedConnectionIsBroken', () => { +// it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { +// expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); +// }); +// +// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { +// expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); +// }); +// +// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 531', () => { +// expect(checkIfFeedConnectionIsBroken(commercialFeedCardsSingleList)).toBeFalsy(); +// }); +// +// it('should return false if no feed(s) cards are provided', () => { +// expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); +// }); +// +// it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { +// const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; +// const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; +// expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); +// }); +// }); +// +// describe('checkIfFeedConnectionIsBroken', () => { +// it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { +// expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); +// }); +// +// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { +// expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); +// }); +// +// it('should return false if no feed(s) cards are provided', () => { +// expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); +// }); +// +// it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { +// const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; +// const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; +// expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); +// }); +// }); +// +// describe('hasIssuedExpensifyCard', () => { +// it('should return true when Expensify Card was issued for given workspace', () => { +// const workspaceAccountID = 11111111; +// expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(true); +// }); +// +// it('should return false when Expensify Card was not issued for given workspace', () => { +// const workspaceAccountID = 11111111; +// expect(hasIssuedExpensifyCard(workspaceAccountID, {})).toBe(false); +// }); +// +// it('should not erroneously return true when workspaceAccountID is 0', () => { +// const workspaceAccountID = 0; +// expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(false); +// }); +// }); +// +// describe('getAllCardsForWorkspace', () => { +// it('should return all cards for a given workspace', () => { +// const workspaceAccountID = 11111111; +// expect(getAllCardsForWorkspace(workspaceAccountID, allCardsList)).toEqual({ +// '21310091': { +// accountID: 18439984, +// bank: 'vcf', +// cardID: 21310091, +// cardName: '480801XXXXXX2554', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2554', +// lastScrape: '2024-11-27 11:00:53', +// lastUpdated: '', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }, +// '21570655': { +// accountID: 18439984, +// bank: 'oauth.capitalone.com', +// cardID: 21570655, +// cardName: 'CREDIT CARD...5678', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '5678', +// lastScrape: '', +// lastScrapeResult: 200, +// lastUpdated: '', +// scrapeMinDate: '2024-08-27', +// state: 3, +// }, +// '21570656': { +// accountID: 18439984, +// bank: 'oauth.capitalone.com', +// cardID: 21570656, +// cardName: 'CREDIT CARD...4444', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '5678', +// lastScrape: '', +// lastScrapeResult: 403, +// lastUpdated: '', +// scrapeMinDate: '2024-08-27', +// state: 3, +// }, +// '21570657': { +// accountID: 18439984, +// bank: 'Expensify Card', +// cardID: 21570657, +// cardName: 'CREDIT CARD...5644', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// }); +// }); +// }); +// +// describe('isExpensifyCardFullySetUp', () => { +// it('should return true when policy has enabled cards and cardSettings has payment bank account ID', () => { +// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithPaymentBankAccountID); +// expect(result).toBe(true); +// }); +// +// it('should return false when policy has disabled cards', () => { +// const result = isExpensifyCardFullySetUp(policyWithCardsDisabled, cardSettingsWithoutPaymentBankAccountID); +// expect(result).toBe(false); +// }); +// +// it('should return false when cardSettings has no payment bank account ID', () => { +// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithoutPaymentBankAccountID); +// expect(result).toBe(false); +// }); +// +// it('should return false when cardSettings is undefined', () => { +// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, undefined); +// expect(result).toBe(false); +// }); +// +// it('should return false when both policy and cardSettings are undefined', () => { +// const result = isExpensifyCardFullySetUp(undefined, undefined); +// expect(result).toBe(false); +// }); +// }); +// +// describe('filterInactiveCards', () => { +// it('should filter out closed, deactivated and suspended cards', () => { +// const activeCards = {card1: {cardID: 1, state: CONST.EXPENSIFY_CARD.STATE.OPEN}}; +// const closedCards = { +// card2: {cardID: 2, state: CONST.EXPENSIFY_CARD.STATE.CLOSED}, +// card3: {cardID: 3, state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED}, +// card4: {cardID: 4, state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED}, +// }; +// const cardList = {...activeCards, ...closedCards} as unknown as CardList; +// const filteredList = filterInactiveCards(cardList); +// expect(filteredList).toEqual(activeCards); +// }); +// +// it('should return an empty object if undefined card list is passed', () => { +// const cards = filterInactiveCards(undefined); +// expect(cards).toEqual({}); +// }); +// }); +// +// describe('sortCardsByCardholderName', () => { +// const mockPersonalDetails: PersonalDetailsList = { +// 1: { +// accountID: 1, +// login: 'john@example.com', +// displayName: 'John Doe', +// firstName: 'John', +// lastName: 'Doe', +// }, +// 2: { +// accountID: 2, +// login: 'jane@example.com', +// displayName: 'Jane Smith', +// firstName: 'Jane', +// lastName: 'Smith', +// }, +// 3: { +// accountID: 3, +// login: 'unknown@example.com', +// // No displayName or firstName/lastName +// }, +// }; +// +// const mockCards: WorkspaceCardsList = { +// '1': { +// cardID: 1, +// accountID: 1, +// cardName: 'Card 1', +// bank: 'expensify', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// '2': { +// cardID: 2, +// accountID: 2, +// bank: 'expensify', +// cardName: 'Card 2', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// '3': { +// cardID: 3, +// accountID: 3, +// bank: 'expensify', +// cardName: 'Card 3', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// }; +// +// it('should sort cards by cardholder name in ascending order', () => { +// const policyMembersAccountIDs = [1, 2, 3]; +// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); +// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); +// +// expect(sortedCards).toHaveLength(3); +// expect(sortedCards.at(0)?.cardID).toBe(2); +// expect(sortedCards.at(1)?.cardID).toBe(1); +// expect(sortedCards.at(2)?.cardID).toBe(3); +// }); +// +// it('should filter out cards that are not associated with policy members', () => { +// const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3 +// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); +// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); +// +// expect(sortedCards).toHaveLength(2); +// expect(sortedCards.at(0)?.cardID).toBe(2); +// expect(sortedCards.at(1)?.cardID).toBe(1); +// }); +// +// it('should handle undefined cardsList', () => { +// const policyMembersAccountIDs = [1, 2, 3]; +// const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs); +// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); +// +// expect(sortedCards).toHaveLength(0); +// }); +// +// it('should handle undefined personalDetails', () => { +// const policyMembersAccountIDs = [1, 2, 3]; +// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); +// const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare); +// +// expect(sortedCards).toHaveLength(3); +// // All cards should be sorted with default names +// expect(sortedCards.at(0)?.cardID).toBe(1); +// expect(sortedCards.at(1)?.cardID).toBe(2); +// expect(sortedCards.at(2)?.cardID).toBe(3); +// }); +// +// it('should handle cards with missing accountID', () => { +// const cardsWithMissingAccountID: WorkspaceCardsList = { +// '1': { +// cardID: 1, +// accountID: 1, +// cardName: 'Card 1', +// bank: 'expensify', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// '2': { +// cardID: 2, +// cardName: 'Card 2', +// bank: 'expensify', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// }; +// +// const policyMembersAccountIDs = [1, 2]; +// const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs); +// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); +// +// expect(sortedCards).toHaveLength(1); +// expect(sortedCards.at(0)?.cardID).toBe(1); +// }); +// }); +// +// describe('getCardDescription', () => { +// it('should return the correct card description for company card', () => { +// const card: Card = { +// accountID: 18439984, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, +// cardID: 21310091, +// cardName: '480801XXXXXX2554', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2554', +// lastUpdated: '', +// lastScrape: '2024-11-27 11:00:53', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }; +// const description = getCardDescription(card); +// expect(description).toBe('Visa - 2554'); +// }); +// +// it('should return the correct card description for Expensify card', () => { +// const card: Card = { +// accountID: 18439984, +// bank: CONST.EXPENSIFY_CARD.BANK, +// cardID: 21570657, +// cardName: 'CREDIT CARD...5644', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }; +// const description = getCardDescription(card); +// expect(description).toBe('Expensify Card'); +// }); +// }); +// +// describe('isExpensifyCard', () => { +// it('should return true for Expensify Card', () => { +// const card: Card = { +// accountID: 18439984, +// bank: CONST.EXPENSIFY_CARD.BANK, +// cardID: 21570657, +// cardName: 'CREDIT CARD...5644', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }; +// expect(isExpensifyCard(card)).toBe(true); +// }); +// +// it('should return false for non-Expensify Card', () => { +// const card: Card = { +// accountID: 18439984, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, +// cardID: 21310091, +// cardName: '480801XXXXXX2554', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2554', +// lastUpdated: '', +// lastScrape: '2024-11-27 11:00:53', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }; +// expect(isExpensifyCard(card)).toBe(false); +// }); +// }); +// +// describe('getCompanyCardDescription', () => { +// const cardList: CardList = { +// '21310091': { +// accountID: 18439984, +// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, +// cardID: 21310091, +// cardName: '480801XXXXXX2554', +// domainName: 'expensify-policy41314f4dc5ce25af.exfy', +// fraud: 'none', +// lastFourPAN: '2554', +// lastUpdated: '', +// lastScrape: '2024-11-27 11:00:53', +// scrapeMinDate: '2024-10-17', +// state: 3, +// }, +// '21570657': { +// accountID: 18439984, +// bank: CONST.EXPENSIFY_CARD.BANK, +// cardID: 21570657, +// cardName: 'CREDIT CARD...5644', +// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', +// fraud: 'none', +// lastFourPAN: '', +// lastScrape: '', +// lastUpdated: '', +// state: 2, +// }, +// }; +// it('should return the correct description for a company card', () => { +// const description = getCompanyCardDescription('Test', 21310091, cardList); +// expect(description).toBe('480801XXXXXX2554'); +// }); +// +// it('should return the correct description for an Expensify card', () => { +// const description = getCompanyCardDescription('Test', 21570657, cardList); +// expect(description).toBe('Test'); +// }); +// }); +// +// describe('Expensify card sort comparator', () => { +// it('should not change the order of non-Expensify cards', () => { +// const cardList = { +// 10: {cardID: 10, bank: 'chase'}, // non-Expensify +// 11: {cardID: 11, bank: 'chase'}, // non-Expensify +// } as unknown as CardList; +// +// const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); +// expect(sorted.map((r: Card) => r.cardID)).toEqual([10, 11]); +// }); +// +// it('places physical Expensify card before its virtual sibling', () => { +// const cardList = { +// 10: {cardID: 10, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {isVirtual: true}}, // Expensify virtual +// 11: {cardID: 11, bank: CONST.EXPENSIFY_CARD.BANK}, // Expensify physical +// 99: {cardID: 99, bank: 'chase'}, // non-Expensify +// } as unknown as CardList; +// +// const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); +// expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]); +// }); +// }); +// }); From 04388f6e59567e0a5fca745505e0b5ae324bf03d Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 5 Nov 2025 16:29:25 +0100 Subject: [PATCH 02/23] Update the rest of the files --- src/libs/CardUtils.ts | 2 +- .../BankConnection/index.native.tsx | 18 +++++++++--------- .../companyCards/BankConnection/index.tsx | 18 +++++++++--------- .../addNew/PlaidConnectionStep.tsx | 4 ++-- .../assignCard/TransactionStartDateStep.tsx | 4 ++-- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 21ec36f378ee..2650d64b0243 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -588,7 +588,7 @@ function checkIfNewFeedConnected(prevFeedsData: CompanyFeeds, currentFeedsData: return { isNewFeedConnected: currentFeeds.length > prevFeeds.length || (plaidBank && currentFeeds.includes(`${CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID}.${plaidBank}`)), - newFeed: currentFeeds.find((feed) => !prevFeeds.includes(feed)) as CompanyCardFeed | undefined, + newFeed: currentFeeds.find((feed) => !prevFeeds.includes(feed)) as CombinedFeedKey | undefined, }; } diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index db6e0660dfa4..ef797707ace7 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -16,7 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getOriginalFeedName, isSelectedFeedExpired} from '@libs/CardUtils'; import getUAForWebView from '@libs/getUAForWebView'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; @@ -28,14 +28,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; type BankConnectionProps = { /** ID of the policy */ policyID?: string; /** Selected feed for assign card flow */ - feed?: CompanyCardFeed; + feed?: CombinedFeedKey; /** Route params for add new card flow */ route?: PlatformStackRouteProp; @@ -52,7 +52,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const selectedBank = addNewCard?.data?.selectedBank; const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName = feed ? getBankName(feed) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getOriginalFeedName(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isBetaEnabled} = usePermissions(); const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!plaidToken; @@ -60,17 +60,17 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const url = getCompanyCardBankConnection(policyID, bankName); const [cardFeeds] = useCardFeeds(policyID); const [isConnectionCompleted, setConnectionCompleted] = useState(false); - const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); - const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.settings?.oAuthAccountDetails?.[feed]) : false; + const prevFeedsData = usePrevious(cardFeeds); + const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.[feed]) : false; const {isNewFeedConnected, newFeed} = useMemo( - () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}, addNewCard?.data?.plaidConnectedFeed), - [addNewCard?.data?.plaidConnectedFeed, cardFeeds?.settings?.oAuthAccountDetails, prevFeedsData], + () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), + [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], ); const headerTitleAddCards = !backTo ? translate('workspace.companyCards.addCards') : undefined; const headerTitle = feed ? translate('workspace.companyCards.assignCard') : headerTitleAddCards; const onImportPlaidAccounts = useImportPlaidAccounts(policyID); const {updateBrokenConnection, isFeedConnectionBroken} = useUpdateFeedBrokenConnection({policyID, feed}); - const isNewFeedHasError = !!(newFeed && cardFeeds?.settings?.oAuthAccountDetails?.[newFeed]?.errors); + const isNewFeedHasError = !!(newFeed && cardFeeds?.[newFeed]?.errors); const renderLoading = () => ; diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index 37f4daace2e5..686693141489 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -17,7 +17,7 @@ import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getOriginalFeedName, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -29,7 +29,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; import openBankConnection from './openBankConnection'; let customWindow: Window | null = null; @@ -39,7 +39,7 @@ type BankConnectionProps = { policyID?: string; /** Selected feed for assign card flow */ - feed?: CompanyCardFeed; + feed?: CombinedFeedKey; /** Route params for add new card flow */ route?: PlatformStackRouteProp; @@ -53,13 +53,13 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; const [cardFeeds] = useCardFeeds(policyID); - const prevFeedsData = usePrevious(cardFeeds?.settings?.oAuthAccountDetails); + const prevFeedsData = usePrevious(cardFeeds); const [shouldBlockWindowOpen, setShouldBlockWindowOpen] = useState(false); const selectedBank = addNewCard?.data?.selectedBank; - const bankName = feed ? getBankName(feed) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getOriginalFeedName(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isNewFeedConnected, newFeed} = useMemo( - () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds?.settings?.oAuthAccountDetails ?? {}, addNewCard?.data?.plaidConnectedFeed), - [addNewCard?.data?.plaidConnectedFeed, cardFeeds?.settings?.oAuthAccountDetails, prevFeedsData], + () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), + [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], ); const {isOffline} = useNetwork(); const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; @@ -68,10 +68,10 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!plaidToken; const url = getCompanyCardBankConnection(policyID, bankName); - const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.settings?.oAuthAccountDetails?.[feed]) : false; + const isFeedExpired = feed ? isSelectedFeedExpired(cardFeeds?.[feed]) : false; const headerTitleAddCards = !backTo ? translate('workspace.companyCards.addCards') : undefined; const headerTitle = feed ? translate('workspace.companyCards.assignCard') : headerTitleAddCards; - const isNewFeedHasError = !!(newFeed && cardFeeds?.settings?.oAuthAccountDetails?.[newFeed]?.errors); + const isNewFeedHasError = !!(newFeed && cardFeeds?.[newFeed]?.errors); const onImportPlaidAccounts = useImportPlaidAccounts(policyID); const onOpenBankConnectionFlow = useCallback(() => { diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index a025a80bfa90..d9a5b8f78f8d 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -22,10 +22,10 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeed; policyID?: string; onExit?: () => void}) { +function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CombinedFeedKey; policyID?: string; onExit?: () => void}) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index d0117f07290c..db7232165dd7 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -17,11 +17,11 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; -import type {CompanyCardFeed} from '@src/types/onyx'; +import type {CombinedFeedKey} from '@src/types/onyx'; type TransactionStartDateStepProps = { policyID: string | undefined; - feed: CompanyCardFeed; + feed: CombinedFeedKey; backTo?: Route; }; From ceaf2c5589222d49e41ab23ba72f610586f0887a Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 5 Nov 2025 16:42:32 +0100 Subject: [PATCH 03/23] Simplify updates --- src/hooks/useCardsList.tsx | 4 ++-- src/hooks/useUpdateFeedBrokenConnection.ts | 4 ++-- src/libs/CardFeedUtils.ts | 6 +++--- src/libs/CardUtils.ts | 14 +++++--------- src/pages/workspace/WorkspaceInitialPage.tsx | 4 ++-- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 4 ++-- .../WorkspaceCompanyCardAccountSelectCardPage.tsx | 4 ++-- .../WorkspaceCompanyCardDetailsPage.tsx | 4 ++-- .../WorkspaceCompanyCardEditCardNamePage.tsx | 4 ++-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 4 ++-- .../WorkspaceCompanyCardStatementCloseDatePage.tsx | 4 ++-- .../WorkspaceCompanyCardsErrorConfirmation.tsx | 4 ++-- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 4 ++-- .../companyCards/WorkspaceCompanyCardsPage.tsx | 4 ++-- .../WorkspaceCompanyCardsSettingsFeedNamePage.tsx | 4 ++-- .../WorkspaceCompanyCardsSettingsPage.tsx | 4 ++-- .../addNew/DirectStatementCloseDatePage.tsx | 4 ++-- .../workspace/downgrade/WorkspaceDowngradePage.tsx | 4 ++-- .../members/WorkspaceMemberDetailsPage.tsx | 12 ++---------- .../members/WorkspaceMemberNewCardPage.tsx | 4 ++-- 20 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx index 879712f9034f..7824c1692ed8 100644 --- a/src/hooks/useCardsList.tsx +++ b/src/hooks/useCardsList.tsx @@ -1,5 +1,5 @@ import type {ResultMetadata} from 'react-native-onyx'; -import {filterInactiveCards, getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; +import {filterInactiveCards, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {CardList, CombinedFeedKey} from '@src/types/onyx'; import useCardFeeds from './useCardFeeds'; @@ -10,7 +10,7 @@ import useWorkspaceAccountID from './useWorkspaceAccountID'; const useCardsList = (policyID: string | undefined, selectedFeed: CombinedFeedKey | undefined): [CardList | undefined, ResultMetadata] => { const workspaceAccountID = useWorkspaceAccountID(policyID); const [cardFeeds] = useCardFeeds(policyID); - const companyCards = getCombinedCompanyFeeds(cardFeeds); + const companyCards = getCompanyFeeds(cardFeeds); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyCards[selectedFeed] : undefined); // TODO: can be improved const [feed] = selectedFeed?.split('#') ?? []; diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index d984affc81e5..05325329af5c 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -1,5 +1,5 @@ import {useCallback} from 'react'; -import {checkIfFeedConnectionIsBroken, getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeedName} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeedName} from '@libs/CardUtils'; import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import type {CombinedFeedKey} from '@src/types/onyx'; @@ -11,7 +11,7 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI const [cardsList] = useCardsList(policyID, feed); const policy = usePolicy(policyID); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const {cardList, ...cards} = cardsList ?? {}; const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const domainOrWorkspaceAccountID = feed ? getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[feed]) : CONST.DEFAULT_NUMBER_ID; diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts index 70ea4a37fb30..4b048e526e85 100644 --- a/src/libs/CardFeedUtils.ts +++ b/src/libs/CardFeedUtils.ts @@ -9,8 +9,8 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import { getBankName, getCardFeedIcon, - getCompanyFeeds, getCustomOrFormattedFeedName, + getOriginalCompanyFeeds, getPlaidInstitutionIconUrl, getPlaidInstitutionId, isCard, @@ -424,7 +424,7 @@ function getCardFeedsForDisplay(allCardFeeds: OnyxCollection, allCard return; } - Object.keys(getCompanyFeeds(cardFeeds, true, true)).forEach((key) => { + Object.keys(getOriginalCompanyFeeds(cardFeeds)).forEach((key) => { const feed = key as CompanyCardFeed; const id = `${fundID}_${feed}`; @@ -478,7 +478,7 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection return; } - Object.entries(getCompanyFeeds(cardFeeds, true, true)).forEach(([key, feedData]) => { + Object.entries(getOriginalCompanyFeeds(cardFeeds)).forEach(([key, feedData]) => { const preferredPolicy = 'preferredPolicy' in feedData ? (feedData.preferredPolicy ?? '') : ''; const feed = key as CompanyCardFeed; const id = `${fundID}_${feed}`; diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 2650d64b0243..7195f0bd12e6 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -345,13 +345,10 @@ function isCustomFeed(feed: CompanyCardFeedWithNumber): boolean { 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)); } -function getCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false, shouldFilterOutPendingFeeds = false): CompanyFeeds { +function getOriginalCompanyFeeds(cardFeeds: OnyxEntry): CompanyFeeds { return Object.fromEntries( Object.entries(cardFeeds?.settings?.companyCards ?? {}).filter(([key, value]) => { - if (shouldFilterOutRemovedFeeds && value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - return false; - } - if (shouldFilterOutPendingFeeds && value.pending) { + if (value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || value.pending) { return false; } return key !== CONST.EXPENSIFY_CARD.BANK; @@ -359,7 +356,7 @@ function getCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemoved ); } -function getCombinedCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false, shouldFilterOutPendingFeeds = false): CombinedCardFeeds { +function getCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false, shouldFilterOutPendingFeeds = false): CombinedCardFeeds { return Object.fromEntries( Object.entries(cardFeeds ?? {}).filter(([, value]) => { if (shouldFilterOutRemovedFeeds && value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { @@ -368,7 +365,6 @@ function getCombinedCompanyFeeds(cardFeeds: OnyxEntry, should if (shouldFilterOutPendingFeeds && value.pending) { return false; } - // FIX HERE return !value.feed.includes(CONST.EXPENSIFY_CARD.BANK); }), ); @@ -528,7 +524,7 @@ function getCorrectStepForPlaidSelectedBank(selectedBank: ValueOf, cardFeeds: OnyxEntry): CombinedFeedKey | undefined { - const defaultFeed = Object.keys(getCombinedCompanyFeeds(cardFeeds, true)).at(0) as CombinedFeedKey | undefined; + const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CombinedFeedKey | undefined; if (!lastSelectedFeed?.includes('#')) { return defaultFeed; } @@ -795,7 +791,7 @@ export { getPlaidInstitutionId, getFeedConnectionBrokenCard, getCorrectStepForPlaidSelectedBank, - getCombinedCompanyFeeds, + getOriginalCompanyFeeds, getOriginalFeedName, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 1c6c473fc118..c59464befb1f 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -30,7 +30,7 @@ import {confirmReadyToOpenApp} from '@libs/actions/App'; import {isConnectionInProgress} from '@libs/actions/connections'; import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline'; import {clearErrors, openPolicyInitialPage, removeWorkspace} from '@libs/actions/Policy/Policy'; -import {checkIfFeedConnectionIsBroken, flatAllCardsList, getCombinedCompanyFeeds} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, flatAllCardsList, getCompanyFeeds} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -120,7 +120,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`, {canBeMissing: true}); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector, canBeMissing: false}); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params?.policyID}`, {canBeMissing: true}); - const cardsDomainIDs = Object.values(getCombinedCompanyFeeds(cardFeeds)) + const cardsDomainIDs = Object.values(getCompanyFeeds(cardFeeds)) .map((data) => data.domainID) .filter((domainID): domainID is number => !!domainID); const {login, accountID} = useCurrentUserPersonalDetails(); diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 286c80961795..9dc3ad6d5774 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -18,7 +18,7 @@ import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import {filterInactiveCards, getAllCardsForWorkspace, getCombinedCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; +import {filterInactiveCards, getAllCardsForWorkspace, getCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -171,7 +171,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle', isActive: policy?.areCompanyCardsEnabled ?? false, pendingAction: policy?.pendingFields?.areCompanyCardsEnabled, - disabled: !isEmptyObject(getCombinedCompanyFeeds(cardFeeds)), + disabled: !isEmptyObject(getCompanyFeeds(cardFeeds)), action: (isEnabled: boolean) => { if (!policyID) { return; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index 6e344bac57c1..a747fb210712 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -14,7 +14,7 @@ import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {setCompanyCardExportAccount} from '@libs/actions/CompanyCards'; -import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getConnectedIntegration, getCurrentConnectionName} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -53,7 +53,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard const illustrations = useMemoizedLazyIllustrations(['Telescope'] as const); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[bank]); const searchedListOptions = useMemo(() => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 2f77e11f2c4f..7f810fc639dc 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -22,7 +22,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCardFeedIcon, getCombinedCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -72,7 +72,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag const exportMenuItem = getExportMenuItem(connectedIntegration, policyID, translate, policy, card); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[bank]); const plaidUrl = getPlaidInstitutionIconUrl(bank); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage.tsx index d101ce8dde7d..947ce9d45c21 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage.tsx @@ -12,7 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; -import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; import {addErrorMessage} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; @@ -42,7 +42,7 @@ function WorkspaceCompanyCardEditCardNamePage({route}: WorkspaceCompanyCardEditC const styles = useThemeStyles(); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[bank]); const submit = (values: FormOnyxValues) => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 6c14f1070a37..be81e21111f2 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -19,7 +19,7 @@ import { checkIfFeedConnectionIsBroken, filterInactiveCards, getCardFeedIcon, - getCombinedCompanyFeeds, + getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getPlaidInstitutionIconUrl, @@ -61,7 +61,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: false}); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const isCollect = isCollectPolicy(policy); const feeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CombinedFeedKey, CombinedCardFeed]>).map(([key, feedSettings]) => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx index f764be18ddc2..22cf00592ae1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -30,7 +30,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const workspaceAccountID = useWorkspaceAccountID(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); const statementPeriodEndDay = selectedFeedData?.statementPeriodEndDay; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index 832526bedfa0..705392f4aaa8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -8,7 +8,7 @@ import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; import Navigation from '@navigation/Navigation'; import {deleteWorkspaceCompanyCardFeed, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import {enableExpensifyCard} from '@userActions/Policy/Policy'; @@ -29,7 +29,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo const [cardsList] = useCardsList(policyID, newFeed); const [cardFeeds] = useCardFeeds(policyID); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = newFeed ? companyFeeds[newFeed] : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index d7f343b21fc5..98ea8afa9d30 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -23,7 +23,7 @@ import { flatAllCardsList, getBankName, getCardFeedIcon, - getCombinedCompanyFeeds, + getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, @@ -72,7 +72,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const formattedFeedName = getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName); const isCommercialFeed = isCustomFeed(selectedFeed); const plaidUrl = getPlaidInstitutionIconUrl(selectedFeed); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(originalFeedName); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeedData); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 185d90d42f95..0c45c7b528db 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -13,7 +13,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import { checkIfFeedConnectionIsBroken, - getCombinedCompanyFeeds, + getCompanyFeeds, getDomainOrWorkspaceAccountID, getFilteredCardList, getOriginalFeedName, @@ -70,7 +70,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const filteredCardList = getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.[selectedFeed]?.accountList : undefined, workspaceCardFeeds); - const companyCards = getCombinedCompanyFeeds(cardFeeds); + const companyCards = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed && companyCards[selectedFeed]; const isNoFeed = !selectedFeedData; const isPending = !!selectedFeedData?.pending; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx index 2f790c77784b..990eda52fe0f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -44,7 +44,7 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyFeeds[selectedFeed] : undefined); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index b1733f60e659..42c08cb8f2c0 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -16,7 +16,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {deleteWorkspaceCompanyCardFeed, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; -import {getCombinedCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -48,7 +48,7 @@ function WorkspaceCompanyCardsSettingsPage({ const [cardsList] = useCardsList(policyID, selectedFeed); const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const liabilityType = selectedFeedData?.liabilityType; const isPersonal = liabilityType === CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index ee6151e6e265..640bff150e1f 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCombinedCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -23,7 +23,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const workspaceAccountID = useWorkspaceAccountID(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeedData); const statementPeriodEndDay = selectedFeedData?.statementPeriodEndDay; diff --git a/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx b/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx index 559943c2f333..034e7b052713 100644 --- a/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx +++ b/src/pages/workspace/downgrade/WorkspaceDowngradePage.tsx @@ -11,7 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCombinedCompanyFeeds} from '@libs/CardUtils'; +import {getCompanyFeeds} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -31,7 +31,7 @@ function WorkspaceDowngradePage({route}: WorkspaceDowngradePageProps) { const policyID = route.params?.policyID; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); const [cardFeeds] = useCardFeeds(policyID); - const companyFeeds = getCombinedCompanyFeeds(cardFeeds); + const companyFeeds = getCompanyFeeds(cardFeeds); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const [isDowngradeWarningModalOpen, setIsDowngradeWarningModalOpen] = useState(false); diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 0a0b7b5d5973..dc4e6ad0400b 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -27,15 +27,7 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy'; import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow'; -import { - getAllCardsForWorkspace, - getCardFeedIcon, - getCombinedCompanyFeeds, - getPlaidInstitutionIconUrl, - isExpensifyCardFullySetUp, - lastFourNumbersFromCardName, - maskCardNumber, -} from '@libs/CardUtils'; +import {getAllCardsForWorkspace, getCardFeedIcon, getCompanyFeeds, getPlaidInstitutionIconUrl, isExpensifyCardFullySetUp, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -107,7 +99,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const isCurrentUserOwner = policy?.owner === currentUserPersonalDetails?.login; const ownerDetails = useMemo(() => personalDetails?.[policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? ({} as PersonalDetails), [personalDetails, policy?.ownerAccountID]); const policyOwnerDisplayName = formatPhoneNumber(getDisplayNameOrDefault(ownerDetails)) ?? policy?.owner ?? ''; - const hasMultipleFeeds = Object.keys(getCombinedCompanyFeeds(cardFeeds, false, true)).length > 0; + const hasMultipleFeeds = Object.keys(getCompanyFeeds(cardFeeds, false, true)).length > 0; const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList, cardFeeds, expensifyCardSettings); const isSMSLogin = Str.isSMSLogin(memberLogin); const phoneNumber = getPhoneNumber(details); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index ee7eca1f0fcc..c616a93fac0d 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -17,7 +17,7 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import { getCardFeedIcon, - getCombinedCompanyFeeds, + getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getFilteredCardList, @@ -75,7 +75,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const accountID = Number(route.params.accountID); const memberLogin = personalDetails?.[accountID]?.login ?? ''; const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; - const companyFeeds = getCombinedCompanyFeeds(cardFeeds, false, true); + const companyFeeds = getCompanyFeeds(cardFeeds, false, true); const isFeedExpired = isSelectedFeedExpired(cardFeeds?.[selectedFeed as CombinedFeedKey]); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[selectedFeed as CombinedFeedKey]); From 15e6f0a20d8ee1f9c267c2a741933ac07d3a98d2 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 5 Nov 2025 17:52:28 +0100 Subject: [PATCH 04/23] Update existing tests --- src/libs/CardUtils.ts | 5 +- tests/unit/CardUtilsTest.ts | 1937 +++++++++++++++++------------------ 2 files changed, 941 insertions(+), 1001 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 7195f0bd12e6..1666912b4e0a 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -625,7 +625,10 @@ const CUSTOM_FEEDS = [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, CONST.COMPA function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry): CompanyCardFeedWithNumber { if (CUSTOM_FEEDS.some((feed) => feed === feedKey)) { - const filteredFeeds = Object.keys(cardFeeds ?? {}).filter((str) => str.includes(feedKey)); + const filteredFeeds = Object.keys(cardFeeds ?? {}) + .filter((str) => str.includes(feedKey)) + .map((str) => str.split('#').at(0)) + .filter((str): str is string => !!str); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index ed9a370f6d8b..08e3257e503c 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -3,6 +3,7 @@ import type {OnyxCollection} from 'react-native-onyx'; import type IllustrationsType from '@styles/theme/illustrations/types'; import type * as Illustrations from '@src/components/Icon/Illustrations'; import CONST from '@src/CONST'; +import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds'; import IntlStore from '@src/languages/IntlStore'; import { checkIfFeedConnectionIsBroken, @@ -32,7 +33,7 @@ import { maskCardNumber, sortCardsByCardholderName, } from '@src/libs/CardUtils'; -import type {Card, CardFeeds, CardList, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CardList, CombinedFeedKey, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; @@ -84,40 +85,6 @@ const companyCardsCustomVisaFeedSettingsWithNumbers = { pending: false, }, }; -const companyCardsCustomFeedSettingsWithoutExpensifyBank = { - [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { - pending: true, - }, - [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { - liabilityType: 'personal', - }, -}; -const companyCardsDirectFeedSettings = { - [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: { - liabilityType: 'personal', - }, - [CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]: { - liabilityType: 'personal', - }, -}; -const companyCardsSettingsWithoutExpensifyBank = { - [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { - pending: true, - }, - [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { - liabilityType: 'personal', - }, - ...companyCardsDirectFeedSettings, -}; - -const companyCardsSettingsWithOnePendingFeed = { - [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { - pending: true, - }, - [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { - pending: false, - }, -}; const oAuthAccountDetails = { [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: { @@ -225,6 +192,39 @@ const customFeedCardsList = { } as unknown as WorkspaceCardsList; const customFeedName = 'Custom feed name'; +const combinedCardFeeds: CombinedCardFeeds = { + [`${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#11111111`]: { + liabilityType: 'personal', + pending: false, + domainID: 11111111, + customFeedName: 'Custom feed name', + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + }, + [`${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}#11111111`]: { + pending: true, + domainID: 11111111, + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, + }, + [`${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`]: { + liabilityType: 'personal', + domainID: 22222222, + accountList: ['CREDIT CARD...6607', 'CREDIT CARD...5501'], + credentials: 'xxxxx', + expiration: 1730998958, + pending: false, + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, + }, + [`${CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE}#11111111`]: { + liabilityType: 'personal', + domainID: 11111111, + accountList: ['CREDIT CARD...1233', 'CREDIT CARD...5678', 'CREDIT CARD...4444', 'CREDIT CARD...3333', 'CREDIT CARD...7788'], + credentials: 'xxxxx', + expiration: 1730998959, + pending: false, + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, + }, +}; + const policyWithCardsEnabled = { areExpensifyCardsEnabled: true, } as unknown as Policy; @@ -241,51 +241,6 @@ const cardSettingsWithoutPaymentBankAccountID = { paymentBankAccountID: undefined, } as unknown as ExpensifyCardSettings; -const cardFeedsCollection: OnyxCollection = { - // Policy with both custom and direct feeds - FAKE_ID_1: { - settings: { - companyCardNicknames: { - [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: customFeedName, - }, - companyCards: {...companyCardsCustomFeedSettings, ...companyCardsDirectFeedSettings}, - oAuthAccountDetails, - }, - }, - // Policy with direct feeds only - FAKE_ID_2: { - settings: { - companyCards: companyCardsDirectFeedSettings, - oAuthAccountDetails, - }, - }, - // Policy with custom feeds only - FAKE_ID_3: { - settings: { - companyCards: companyCardsCustomFeedSettings, - }, - }, - // Policy with custom feeds only, feed names with numbers - FAKE_ID_4: { - settings: { - companyCards: companyCardsCustomFeedSettingsWithNumbers, - }, - }, - // Policy with several Visa feeds - FAKE_ID_5: { - settings: { - companyCards: companyCardsCustomVisaFeedSettingsWithNumbers, - }, - }, - - // Policy with one pending feed - FAKE_ID_6: { - settings: { - companyCards: companyCardsSettingsWithOnePendingFeed, - }, - }, -}; - /* eslint-disable @typescript-eslint/naming-convention */ const allCardsList = { 'cards_11111111_oauth.capitalone.com': directFeedCardsMultipleList, @@ -334,923 +289,905 @@ const mockIllustrations = { jest.mock('@src/components/Icon/Illustrations', () => require('../../__mocks__/Illustrations') as typeof Illustrations); -// describe('CardUtils', () => { -// describe('Expiration date formatting', () => { -// it('Should format expirationDate month and year to MM/YYYY', () => { -// expect(getMonthFromExpirationDateString(longDateSlashed)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(longDateSlashed)).toBe(expectedYear); -// }); -// -// it('Should format expirationDate month and year to MM-YYYY', () => { -// expect(getMonthFromExpirationDateString(longDateHyphen)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(longDateHyphen)).toBe(expectedYear); -// }); -// -// it('Should format expirationDate month and year to MMYYYY', () => { -// expect(getMonthFromExpirationDateString(longDate)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(longDate)).toBe(expectedYear); -// }); -// -// it('Should format expirationDate month and year to MM/YY', () => { -// expect(getMonthFromExpirationDateString(shortDateSlashed)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(shortDateSlashed)).toBe(expectedYear); -// }); -// -// it('Should format expirationDate month and year to MM-YY', () => { -// expect(getMonthFromExpirationDateString(shortDateHyphen)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(shortDateHyphen)).toBe(expectedYear); -// }); -// -// it('Should format expirationDate month and year to MMYY', () => { -// expect(getMonthFromExpirationDateString(shortDate)).toBe(expectedMonth); -// expect(getYearFromExpirationDateString(shortDate)).toBe(expectedYear); -// }); -// -// it('Should format to MM/YYYY given MM/YY', () => { -// expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); -// expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); -// }); -// -// it('Should format to MM/YYYY given MMYY', () => { -// expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); -// expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); -// }); -// }); -// -// describe('isCustomFeed', () => { -// it('Should return true for the custom visa feed with no number', () => { -// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// it('Should return true for the custom visa feed with a number', () => { -// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}1` as CompanyCardFeedWithNumber; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// it('Should return true for the custom mastercard feed with no number', () => { -// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// it('Should return true for the custom mastercard feed with a number', () => { -// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}3` as CompanyCardFeedWithNumber; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// it('Should return true for the custom amex feed with no number', () => { -// const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// it('Should return true for the custom amex feed with a number', () => { -// const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}2` as CompanyCardFeedWithNumber; -// const isCustomFeed = isCustomFeedCardUtils(customFeed); -// expect(isCustomFeed).toBe(true); -// }); -// -// test.each(directFeedBanks)('Should return false for the direct feed %s', (directFeed) => { -// const isCustomFeed = isCustomFeedCardUtils(directFeed); -// expect(isCustomFeed).toBe(false); -// }); -// }); -// -// describe('getCompanyFeeds', () => { -// it('Should return both custom and direct feeds with filtered out "Expensify Card" bank', () => { -// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_1); -// expect(companyFeeds).toStrictEqual(companyCardsSettingsWithoutExpensifyBank); -// }); -// -// it('Should return direct feeds only since custom feeds are not exist', () => { -// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_2); -// expect(companyFeeds).toStrictEqual(companyCardsDirectFeedSettings); -// }); -// -// it('Should return custom feeds only with filtered out "Expensify Card" bank since direct feeds are not exist', () => { -// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_3); -// expect(companyFeeds).toStrictEqual(companyCardsCustomFeedSettingsWithoutExpensifyBank); -// }); -// -// it('Should return empty object if undefined is passed', () => { -// const companyFeeds = getCompanyFeeds(undefined); -// expect(companyFeeds).toStrictEqual({}); -// }); -// -// it('Should return only feeds that are not pending', () => { -// const companyFeeds = getCompanyFeeds(cardFeedsCollection.FAKE_ID_6, false, true); -// expect(Object.keys(companyFeeds).length).toStrictEqual(1); -// }); -// }); -// -// describe('getSelectedFeed', () => { -// it('Should return last selected custom feed', () => { -// const lastSelectedCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; -// const selectedFeed = getSelectedFeed(lastSelectedCustomFeed, cardFeedsCollection.FAKE_ID_1); -// expect(selectedFeed).toBe(lastSelectedCustomFeed); -// }); -// -// it('Should return last selected direct feed', () => { -// const lastSelectedDirectFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE; -// const selectedFeed = getSelectedFeed(lastSelectedDirectFeed, cardFeedsCollection.FAKE_ID_1); -// expect(selectedFeed).toBe(lastSelectedDirectFeed); -// }); -// -// it('Should return the first available custom feed if lastSelectedFeed is undefined', () => { -// const lastSelectedFeed = undefined; -// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_3); -// expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); -// }); -// -// it('Should return the first available direct feed if lastSelectedFeed is undefined', () => { -// const lastSelectedFeed = undefined; -// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeedsCollection.FAKE_ID_2); -// expect(selectedFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); -// }); -// -// it('Should return undefined if lastSelectedFeed is undefined and there is no card feeds', () => { -// const lastSelectedFeed = undefined; -// const cardFeeds = undefined; -// const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); -// expect(selectedFeed).toBe(undefined); -// }); -// }); -// -// describe('getCustomOrFormattedFeedName', () => { -// beforeAll(() => { -// IntlStore.load(CONST.LOCALES.EN); -// return waitForBatchedUpdates(); -// }); -// it('Should return custom name if exists', () => { -// const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; -// const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; -// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); -// expect(feedName).toBe(customFeedName); -// }); -// -// it('Should return formatted name if there is no custom name', () => { -// const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; -// const companyCardNicknames = cardFeedsCollection.FAKE_ID_3?.settings?.companyCardNicknames; -// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); -// expect(feedName).toBe('Visa cards'); -// }); -// -// it('Should return undefined if no feed provided', () => { -// const feed = undefined; -// const companyCardNicknames = cardFeedsCollection.FAKE_ID_1?.settings?.companyCardNicknames; -// const feedName = getCustomOrFormattedFeedName(feed, companyCardNicknames); -// expect(feedName).toBe(undefined); -// }); -// }); -// -// describe('lastFourNumbersFromCardName', () => { -// it('Should return last 4 numbers from the card name', () => { -// const lastFour = lastFourNumbersFromCardName('Business Card Cash - 3001'); -// expect(lastFour).toBe('3001'); -// }); -// -// it('Should return empty string if card number does not have space', () => { -// const lastFour = lastFourNumbersFromCardName('480801XXXXXX2554'); -// expect(lastFour).toBe(''); -// }); -// -// it('Should return empty string if card number does not have number in the end with dash', () => { -// const lastFour = lastFourNumbersFromCardName('Business Card Cash - Business'); -// expect(lastFour).toBe(''); -// }); -// }); -// -// describe('maskCardNumber', () => { -// it("Should return the card number divided into chunks of 4, with 'X' replaced by '•' if it's provided in the '480801XXXXXX2554' format", () => { -// const cardNumber = '480801XXXXXX2554'; -// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); -// expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); -// }); -// -// it('Should return card number without changes if it has empty space', () => { -// const cardNumber = 'CREDIT CARD...6607'; -// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); -// expect(maskedCardNumber).toBe(cardNumber); -// }); -// -// it("Should return the Amex direct feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { -// const cardNumber = '211944XXXXX6557'; -// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT); -// expect(maskedCardNumber).toBe('2119 44•••• •6557'); -// }); -// -// it("Should return the Amex custom feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { -// const cardNumber = '211944XXXXX6557'; -// const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX); -// expect(maskedCardNumber).toBe('2119 44•••• •6557'); -// }); -// -// it('Should return masked card number even if undefined feed was provided', () => { -// const cardNumber = '480801XXXXXX2554'; -// const maskedCardNumber = maskCardNumber(cardNumber, undefined); -// expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); -// }); -// -// it('Should return empty string if invalid card name was provided', () => { -// const maskedCardNumber = maskCardNumber('', CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); -// expect(maskedCardNumber).toBe(''); -// }); -// -// it('Should return card name without last 4 numbers', () => { -// const maskedCardNumber = maskCardNumber('Business Card Cash - 3001', undefined); -// expect(maskedCardNumber).toBe('Business Card Cash'); -// }); -// }); -// -// describe('getCardFeedName', () => { -// it('Should return a valid name if a valid feed was provided', () => { -// const feed = 'vcf'; -// const feedName = getBankName(feed); -// expect(feedName).toBe('Visa'); -// }); -// -// it('Should return a valid name if an OldDot feed variation was provided', () => { -// const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; -// const feedName = getBankName(feed); -// expect(feedName).toBe('American Express'); -// }); -// -// it('Should return a valid name if a CSV imported feed variation was provided', () => { -// const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; -// const feedName = getBankName(feed); -// expect(feedName).toBe('CSV'); -// }); -// -// it('Should return empty string if invalid feed was provided', () => { -// const feed = 'vvcf' as CompanyCardFeed; -// const feedName = getBankName(feed); -// expect(feedName).toBe(''); -// }); -// -// it('Should return empty string if feed is not provided (instead of TypeError crashing the app)', () => { -// const feed = undefined; -// const feedName = getBankName(feed as unknown as CompanyCardFeed); -// expect(feedName).toBe(''); -// }); -// }); -// -// describe('getCardFeedIcon', () => { -// it('Should return a valid illustration if a valid feed was provided', () => { -// const feed = 'vcf'; -// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('VisaCompanyCardDetailLarge'); -// }); -// -// it('Should return a valid illustration if an OldDot feed variation was provided', () => { -// const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; -// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('AmexCardCompanyCardDetailLarge'); -// }); -// -// it('Should return a valid illustration if a CSV imported feed variation was provided', () => { -// const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; -// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('GenericCSVCompanyCardLarge'); -// }); -// -// it('Should return valid illustration if a non-matching feed was provided', () => { -// const feed = '666' as CompanyCardFeed; -// const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('GenericCompanyCardLarge'); -// }); -// }); -// -// describe('getBankCardDetailsImage', () => { -// it('Should return a valid illustration if a valid bank name was provided', () => { -// const bank = 'American Express'; -// const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('AmexCardCompanyCardDetail'); -// }); -// -// it('Should return a valid illustration if Other bank name was provided', () => { -// const bank = 'Other'; -// const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); -// expect(illustration).toBe('GenericCompanyCard'); -// }); -// }); -// -// describe('getFilteredCardList', () => { -// it('Should return filtered custom feed cards list', () => { -// const cardsList = getFilteredCardList(customFeedCardsList, undefined, undefined); -// expect(cardsList).toStrictEqual({ -// // eslint-disable-next-line @typescript-eslint/naming-convention -// '480801XXXXXX2111': 'ENCRYPTED_CARD_NUMBER', -// // eslint-disable-next-line @typescript-eslint/naming-convention -// '480801XXXXXX2566': 'ENCRYPTED_CARD_NUMBER', -// }); -// }); -// -// it('Should return filtered direct feed cards list with a single card', () => { -// const cardsList = getFilteredCardList(directFeedCardsSingleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE], undefined); -// // eslint-disable-next-line @typescript-eslint/naming-convention -// expect(cardsList).toStrictEqual({'CREDIT CARD...6607': 'CREDIT CARD...6607'}); -// }); -// -// it('Should return filtered direct feed cards list with multiple cards', () => { -// const cardsList = getFilteredCardList(directFeedCardsMultipleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE], undefined); -// expect(cardsList).toStrictEqual({ -// // eslint-disable-next-line @typescript-eslint/naming-convention -// 'CREDIT CARD...1233': 'CREDIT CARD...1233', -// // eslint-disable-next-line @typescript-eslint/naming-convention -// 'CREDIT CARD...3333': 'CREDIT CARD...3333', -// // eslint-disable-next-line @typescript-eslint/naming-convention -// 'CREDIT CARD...7788': 'CREDIT CARD...7788', -// }); -// }); -// -// it('Should return empty object if no data was provided', () => { -// const cardsList = getFilteredCardList(undefined, undefined, undefined); -// expect(cardsList).toStrictEqual({}); -// }); -// -// it('Should handle the case when all cards are already assigned in other workspaces', () => { -// const assignedCard1 = 'CREDIT CARD...5566'; -// const assignedCard2 = 'CREDIT CARD...6677'; -// -// const mockAllWorkspaceCards = { -// cards_888888_feed: { -// '11111': { -// accountID: 999999, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, -// cardID: 11111, -// cardName: assignedCard1, -// domainName: 'other-workspace.exfy', -// state: 3, -// }, -// '22222': { -// accountID: 999999, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, -// cardID: 22222, -// cardName: assignedCard2, -// domainName: 'other-workspace.exfy', -// state: 3, -// }, -// }, -// } as unknown as OnyxCollection; -// -// const customFeedWithAllAssignedCards = { -// cardList: { -// [assignedCard1]: 'ENCRYPTED_DATA', -// [assignedCard2]: 'ENCRYPTED_DATA', -// }, -// } as unknown as WorkspaceCardsList; -// const filteredCards = getFilteredCardList(customFeedWithAllAssignedCards, undefined, mockAllWorkspaceCards); -// expect(filteredCards).toStrictEqual({}); -// }); -// -// it('Should filter out cards that are already assigned in another workspace (custom feed)', () => { -// const customFeedWorkspaceCardsList = { -// '21310091': { -// accountID: 18439984, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, -// cardID: 21310091, -// cardName: '480801XXXXXX2554', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2554', -// lastUpdated: '', -// lastScrape: '2024-11-27 11:00:53', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }, -// '21310092': { -// accountID: 18439985, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, -// cardID: 21310092, -// cardName: '480801XXXXXX2666', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2666', -// lastUpdated: '', -// lastScrape: '2024-11-27 11:00:53', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }, -// cardList: { -// '480801XXXXXX2554': 'ENCRYPTED_CARD_NUMBER', -// '480801XXXXXX2666': 'ENCRYPTED_CARD_NUMBER', -// }, -// } as unknown as WorkspaceCardsList; -// -// const filteredCards = getFilteredCardList(customFeedWorkspaceCardsList, undefined, undefined); -// expect(filteredCards).toStrictEqual({}); -// }); -// -// it('Should filter out cards that are already assigned in another workspace (direct feed)', () => { -// const assignedCard1 = 'CREDIT CARD...3344'; -// const assignedCard2 = 'CREDIT CARD...3355'; -// const unassignedCard = 'CREDIT CARD...6666'; -// -// const mockAllWorkspaceCards = { -// cards_888888_feed: { -// '67889': { -// accountID: 999998, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, -// cardID: 67889, -// cardName: assignedCard1, -// domainName: 'other-workspace.exfy', -// state: 3, -// }, -// '67890': { -// accountID: 999999, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, -// cardID: 67890, -// cardName: assignedCard2, -// domainName: 'other-workspace.exfy', -// state: 3, -// }, -// }, -// } as unknown as OnyxCollection; -// const directFeedWithAssignedCard = { -// accountList: [assignedCard1, assignedCard2, unassignedCard], -// } as unknown as (typeof oAuthAccountDetails)[typeof CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]; -// const filteredCards = getFilteredCardList(undefined, directFeedWithAssignedCard, mockAllWorkspaceCards); -// expect(filteredCards).toStrictEqual({[`${unassignedCard}`]: unassignedCard}); -// }); -// }); -// -// describe('getFeedType', () => { -// it('should return the feed name with a consecutive number, if there is already a feed with a number', () => { -// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_4); -// expect(feedType).toBe('vcf2'); -// }); -// -// it('should return the feed name with 1, if there is already a feed without a number', () => { -// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_3); -// expect(feedType).toBe('vcf1'); -// }); -// -// it('should return the feed name with with the first smallest available number', () => { -// const feedType = getFeedType('vcf', cardFeedsCollection.FAKE_ID_5); -// expect(feedType).toBe('vcf2'); -// }); -// }); -// -// describe('flatAllCardsList', () => { -// it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => { -// const workspaceAccountID = 11111111; -// const flattenedCardsList = flatAllCardsList(allCardsList, workspaceAccountID); -// const {cardList, ...customCards} = customFeedCardsList; -// expect(flattenedCardsList).toStrictEqual({ -// ...directFeedCardsMultipleList, -// ...customCards, -// }); -// }); -// -// it('should return undefined if not defined cards list was provided', () => { -// const workspaceAccountID = 11111111; -// const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID); -// expect(flattenedCardsList).toBeUndefined(); -// }); -// }); -// -// describe('checkIfFeedConnectionIsBroken', () => { -// it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { -// expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); -// }); -// -// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { -// expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); -// }); -// -// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 531', () => { -// expect(checkIfFeedConnectionIsBroken(commercialFeedCardsSingleList)).toBeFalsy(); -// }); -// -// it('should return false if no feed(s) cards are provided', () => { -// expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); -// }); -// -// it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { -// const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; -// const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; -// expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); -// }); -// }); -// -// describe('checkIfFeedConnectionIsBroken', () => { -// it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { -// expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); -// }); -// -// it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { -// expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); -// }); -// -// it('should return false if no feed(s) cards are provided', () => { -// expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); -// }); -// -// it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { -// const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; -// const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; -// expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); -// }); -// }); -// -// describe('hasIssuedExpensifyCard', () => { -// it('should return true when Expensify Card was issued for given workspace', () => { -// const workspaceAccountID = 11111111; -// expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(true); -// }); -// -// it('should return false when Expensify Card was not issued for given workspace', () => { -// const workspaceAccountID = 11111111; -// expect(hasIssuedExpensifyCard(workspaceAccountID, {})).toBe(false); -// }); -// -// it('should not erroneously return true when workspaceAccountID is 0', () => { -// const workspaceAccountID = 0; -// expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(false); -// }); -// }); -// -// describe('getAllCardsForWorkspace', () => { -// it('should return all cards for a given workspace', () => { -// const workspaceAccountID = 11111111; -// expect(getAllCardsForWorkspace(workspaceAccountID, allCardsList)).toEqual({ -// '21310091': { -// accountID: 18439984, -// bank: 'vcf', -// cardID: 21310091, -// cardName: '480801XXXXXX2554', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2554', -// lastScrape: '2024-11-27 11:00:53', -// lastUpdated: '', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }, -// '21570655': { -// accountID: 18439984, -// bank: 'oauth.capitalone.com', -// cardID: 21570655, -// cardName: 'CREDIT CARD...5678', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '5678', -// lastScrape: '', -// lastScrapeResult: 200, -// lastUpdated: '', -// scrapeMinDate: '2024-08-27', -// state: 3, -// }, -// '21570656': { -// accountID: 18439984, -// bank: 'oauth.capitalone.com', -// cardID: 21570656, -// cardName: 'CREDIT CARD...4444', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '5678', -// lastScrape: '', -// lastScrapeResult: 403, -// lastUpdated: '', -// scrapeMinDate: '2024-08-27', -// state: 3, -// }, -// '21570657': { -// accountID: 18439984, -// bank: 'Expensify Card', -// cardID: 21570657, -// cardName: 'CREDIT CARD...5644', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// }); -// }); -// }); -// -// describe('isExpensifyCardFullySetUp', () => { -// it('should return true when policy has enabled cards and cardSettings has payment bank account ID', () => { -// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithPaymentBankAccountID); -// expect(result).toBe(true); -// }); -// -// it('should return false when policy has disabled cards', () => { -// const result = isExpensifyCardFullySetUp(policyWithCardsDisabled, cardSettingsWithoutPaymentBankAccountID); -// expect(result).toBe(false); -// }); -// -// it('should return false when cardSettings has no payment bank account ID', () => { -// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithoutPaymentBankAccountID); -// expect(result).toBe(false); -// }); -// -// it('should return false when cardSettings is undefined', () => { -// const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, undefined); -// expect(result).toBe(false); -// }); -// -// it('should return false when both policy and cardSettings are undefined', () => { -// const result = isExpensifyCardFullySetUp(undefined, undefined); -// expect(result).toBe(false); -// }); -// }); -// -// describe('filterInactiveCards', () => { -// it('should filter out closed, deactivated and suspended cards', () => { -// const activeCards = {card1: {cardID: 1, state: CONST.EXPENSIFY_CARD.STATE.OPEN}}; -// const closedCards = { -// card2: {cardID: 2, state: CONST.EXPENSIFY_CARD.STATE.CLOSED}, -// card3: {cardID: 3, state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED}, -// card4: {cardID: 4, state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED}, -// }; -// const cardList = {...activeCards, ...closedCards} as unknown as CardList; -// const filteredList = filterInactiveCards(cardList); -// expect(filteredList).toEqual(activeCards); -// }); -// -// it('should return an empty object if undefined card list is passed', () => { -// const cards = filterInactiveCards(undefined); -// expect(cards).toEqual({}); -// }); -// }); -// -// describe('sortCardsByCardholderName', () => { -// const mockPersonalDetails: PersonalDetailsList = { -// 1: { -// accountID: 1, -// login: 'john@example.com', -// displayName: 'John Doe', -// firstName: 'John', -// lastName: 'Doe', -// }, -// 2: { -// accountID: 2, -// login: 'jane@example.com', -// displayName: 'Jane Smith', -// firstName: 'Jane', -// lastName: 'Smith', -// }, -// 3: { -// accountID: 3, -// login: 'unknown@example.com', -// // No displayName or firstName/lastName -// }, -// }; -// -// const mockCards: WorkspaceCardsList = { -// '1': { -// cardID: 1, -// accountID: 1, -// cardName: 'Card 1', -// bank: 'expensify', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// '2': { -// cardID: 2, -// accountID: 2, -// bank: 'expensify', -// cardName: 'Card 2', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// '3': { -// cardID: 3, -// accountID: 3, -// bank: 'expensify', -// cardName: 'Card 3', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// }; -// -// it('should sort cards by cardholder name in ascending order', () => { -// const policyMembersAccountIDs = [1, 2, 3]; -// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); -// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); -// -// expect(sortedCards).toHaveLength(3); -// expect(sortedCards.at(0)?.cardID).toBe(2); -// expect(sortedCards.at(1)?.cardID).toBe(1); -// expect(sortedCards.at(2)?.cardID).toBe(3); -// }); -// -// it('should filter out cards that are not associated with policy members', () => { -// const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3 -// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); -// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); -// -// expect(sortedCards).toHaveLength(2); -// expect(sortedCards.at(0)?.cardID).toBe(2); -// expect(sortedCards.at(1)?.cardID).toBe(1); -// }); -// -// it('should handle undefined cardsList', () => { -// const policyMembersAccountIDs = [1, 2, 3]; -// const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs); -// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); -// -// expect(sortedCards).toHaveLength(0); -// }); -// -// it('should handle undefined personalDetails', () => { -// const policyMembersAccountIDs = [1, 2, 3]; -// const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); -// const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare); -// -// expect(sortedCards).toHaveLength(3); -// // All cards should be sorted with default names -// expect(sortedCards.at(0)?.cardID).toBe(1); -// expect(sortedCards.at(1)?.cardID).toBe(2); -// expect(sortedCards.at(2)?.cardID).toBe(3); -// }); -// -// it('should handle cards with missing accountID', () => { -// const cardsWithMissingAccountID: WorkspaceCardsList = { -// '1': { -// cardID: 1, -// accountID: 1, -// cardName: 'Card 1', -// bank: 'expensify', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// '2': { -// cardID: 2, -// cardName: 'Card 2', -// bank: 'expensify', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// }; -// -// const policyMembersAccountIDs = [1, 2]; -// const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs); -// const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); -// -// expect(sortedCards).toHaveLength(1); -// expect(sortedCards.at(0)?.cardID).toBe(1); -// }); -// }); -// -// describe('getCardDescription', () => { -// it('should return the correct card description for company card', () => { -// const card: Card = { -// accountID: 18439984, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, -// cardID: 21310091, -// cardName: '480801XXXXXX2554', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2554', -// lastUpdated: '', -// lastScrape: '2024-11-27 11:00:53', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }; -// const description = getCardDescription(card); -// expect(description).toBe('Visa - 2554'); -// }); -// -// it('should return the correct card description for Expensify card', () => { -// const card: Card = { -// accountID: 18439984, -// bank: CONST.EXPENSIFY_CARD.BANK, -// cardID: 21570657, -// cardName: 'CREDIT CARD...5644', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }; -// const description = getCardDescription(card); -// expect(description).toBe('Expensify Card'); -// }); -// }); -// -// describe('isExpensifyCard', () => { -// it('should return true for Expensify Card', () => { -// const card: Card = { -// accountID: 18439984, -// bank: CONST.EXPENSIFY_CARD.BANK, -// cardID: 21570657, -// cardName: 'CREDIT CARD...5644', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }; -// expect(isExpensifyCard(card)).toBe(true); -// }); -// -// it('should return false for non-Expensify Card', () => { -// const card: Card = { -// accountID: 18439984, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, -// cardID: 21310091, -// cardName: '480801XXXXXX2554', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2554', -// lastUpdated: '', -// lastScrape: '2024-11-27 11:00:53', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }; -// expect(isExpensifyCard(card)).toBe(false); -// }); -// }); -// -// describe('getCompanyCardDescription', () => { -// const cardList: CardList = { -// '21310091': { -// accountID: 18439984, -// bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, -// cardID: 21310091, -// cardName: '480801XXXXXX2554', -// domainName: 'expensify-policy41314f4dc5ce25af.exfy', -// fraud: 'none', -// lastFourPAN: '2554', -// lastUpdated: '', -// lastScrape: '2024-11-27 11:00:53', -// scrapeMinDate: '2024-10-17', -// state: 3, -// }, -// '21570657': { -// accountID: 18439984, -// bank: CONST.EXPENSIFY_CARD.BANK, -// cardID: 21570657, -// cardName: 'CREDIT CARD...5644', -// domainName: 'expensify-policy17f617b9fe23d2f1.exfy', -// fraud: 'none', -// lastFourPAN: '', -// lastScrape: '', -// lastUpdated: '', -// state: 2, -// }, -// }; -// it('should return the correct description for a company card', () => { -// const description = getCompanyCardDescription('Test', 21310091, cardList); -// expect(description).toBe('480801XXXXXX2554'); -// }); -// -// it('should return the correct description for an Expensify card', () => { -// const description = getCompanyCardDescription('Test', 21570657, cardList); -// expect(description).toBe('Test'); -// }); -// }); -// -// describe('Expensify card sort comparator', () => { -// it('should not change the order of non-Expensify cards', () => { -// const cardList = { -// 10: {cardID: 10, bank: 'chase'}, // non-Expensify -// 11: {cardID: 11, bank: 'chase'}, // non-Expensify -// } as unknown as CardList; -// -// const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); -// expect(sorted.map((r: Card) => r.cardID)).toEqual([10, 11]); -// }); -// -// it('places physical Expensify card before its virtual sibling', () => { -// const cardList = { -// 10: {cardID: 10, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {isVirtual: true}}, // Expensify virtual -// 11: {cardID: 11, bank: CONST.EXPENSIFY_CARD.BANK}, // Expensify physical -// 99: {cardID: 99, bank: 'chase'}, // non-Expensify -// } as unknown as CardList; -// -// const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); -// expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]); -// }); -// }); -// }); +describe('CardUtils', () => { + describe('Expiration date formatting', () => { + it('Should format expirationDate month and year to MM/YYYY', () => { + expect(getMonthFromExpirationDateString(longDateSlashed)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(longDateSlashed)).toBe(expectedYear); + }); + + it('Should format expirationDate month and year to MM-YYYY', () => { + expect(getMonthFromExpirationDateString(longDateHyphen)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(longDateHyphen)).toBe(expectedYear); + }); + + it('Should format expirationDate month and year to MMYYYY', () => { + expect(getMonthFromExpirationDateString(longDate)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(longDate)).toBe(expectedYear); + }); + + it('Should format expirationDate month and year to MM/YY', () => { + expect(getMonthFromExpirationDateString(shortDateSlashed)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(shortDateSlashed)).toBe(expectedYear); + }); + + it('Should format expirationDate month and year to MM-YY', () => { + expect(getMonthFromExpirationDateString(shortDateHyphen)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(shortDateHyphen)).toBe(expectedYear); + }); + + it('Should format expirationDate month and year to MMYY', () => { + expect(getMonthFromExpirationDateString(shortDate)).toBe(expectedMonth); + expect(getYearFromExpirationDateString(shortDate)).toBe(expectedYear); + }); + + it('Should format to MM/YYYY given MM/YY', () => { + expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); + expect(formatCardExpiration(shortDateSlashed)).toBe(longDateSlashed); + }); + + it('Should format to MM/YYYY given MMYY', () => { + expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); + expect(formatCardExpiration(shortDate)).toBe(longDateSlashed); + }); + }); + + describe('isCustomFeed', () => { + it('Should return true for the custom visa feed with no number', () => { + const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + it('Should return true for the custom visa feed with a number', () => { + const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}1` as CompanyCardFeedWithNumber; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + it('Should return true for the custom mastercard feed with no number', () => { + const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + it('Should return true for the custom mastercard feed with a number', () => { + const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}3` as CompanyCardFeedWithNumber; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + it('Should return true for the custom amex feed with no number', () => { + const customFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + it('Should return true for the custom amex feed with a number', () => { + const customFeed = `${CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX}2` as CompanyCardFeedWithNumber; + const isCustomFeed = isCustomFeedCardUtils(customFeed); + expect(isCustomFeed).toBe(true); + }); + + test.each(directFeedBanks)('Should return false for the direct feed %s', (directFeed) => { + const isCustomFeed = isCustomFeedCardUtils(directFeed); + expect(isCustomFeed).toBe(false); + }); + }); + + describe('getCompanyFeeds', () => { + it('Should return feeds with filtered out "Expensify Card" bank', () => { + // TBD + }); + + it('Should return only feeds that are not pending if shouldFilterOutPendingFeeds is true', () => { + // TBD + }); + + it('Should return only feeds that are not removed if shouldFilterOutRemovedFeeds is true', () => { + // TBD + }); + + it('Should return empty object if undefined is passed', () => { + const companyFeeds = getCompanyFeeds(undefined); + expect(companyFeeds).toStrictEqual({}); + }); + }); + + describe('getSelectedFeed', () => { + it('Should return last selected custom feed', () => { + const lastSelectedCustomFeed: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#12345`; + const selectedFeed = getSelectedFeed(lastSelectedCustomFeed, combinedCardFeeds); + expect(selectedFeed).toBe(lastSelectedCustomFeed); + }); + + it('Should return last selected direct feed', () => { + const lastSelectedDirectFeed: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#12345`; + const selectedFeed = getSelectedFeed(lastSelectedDirectFeed, combinedCardFeeds); + expect(selectedFeed).toBe(lastSelectedDirectFeed); + }); + + it('Should return the first available feed if lastSelectedFeed is undefined', () => { + const lastSelectedFeed = undefined; + const selectedFeed = getSelectedFeed(lastSelectedFeed, combinedCardFeeds); + expect(selectedFeed).toBe(`${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#11111111`); + }); + + it('Should return undefined if lastSelectedFeed is undefined and there is no card feeds', () => { + const lastSelectedFeed = undefined; + const cardFeeds = undefined; + const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); + expect(selectedFeed).toBe(undefined); + }); + }); + + describe('getCustomOrFormattedFeedName', () => { + beforeAll(() => { + IntlStore.load(CONST.LOCALES.EN); + return waitForBatchedUpdates(); + }); + it('Should return custom name if exists', () => { + const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; + const feedName = getCustomOrFormattedFeedName(feed, customFeedName); + expect(feedName).toBe(customFeedName); + }); + + it('Should return formatted name if there is no custom name', () => { + const feed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; + const customName = undefined; + const feedName = getCustomOrFormattedFeedName(feed, customName); + expect(feedName).toBe('Visa cards'); + }); + + it('Should return undefined if no feed provided', () => { + const feed = undefined; + const feedName = getCustomOrFormattedFeedName(feed); + expect(feedName).toBe(undefined); + }); + }); + + describe('lastFourNumbersFromCardName', () => { + it('Should return last 4 numbers from the card name', () => { + const lastFour = lastFourNumbersFromCardName('Business Card Cash - 3001'); + expect(lastFour).toBe('3001'); + }); + + it('Should return empty string if card number does not have space', () => { + const lastFour = lastFourNumbersFromCardName('480801XXXXXX2554'); + expect(lastFour).toBe(''); + }); + + it('Should return empty string if card number does not have number in the end with dash', () => { + const lastFour = lastFourNumbersFromCardName('Business Card Cash - Business'); + expect(lastFour).toBe(''); + }); + }); + + describe('maskCardNumber', () => { + it("Should return the card number divided into chunks of 4, with 'X' replaced by '•' if it's provided in the '480801XXXXXX2554' format", () => { + const cardNumber = '480801XXXXXX2554'; + const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); + expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); + }); + + it('Should return card number without changes if it has empty space', () => { + const cardNumber = 'CREDIT CARD...6607'; + const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); + expect(maskedCardNumber).toBe(cardNumber); + }); + + it("Should return the Amex direct feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { + const cardNumber = '211944XXXXX6557'; + const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT); + expect(maskedCardNumber).toBe('2119 44•••• •6557'); + }); + + it("Should return the Amex custom feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { + const cardNumber = '211944XXXXX6557'; + const maskedCardNumber = maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX); + expect(maskedCardNumber).toBe('2119 44•••• •6557'); + }); + + it('Should return masked card number even if undefined feed was provided', () => { + const cardNumber = '480801XXXXXX2554'; + const maskedCardNumber = maskCardNumber(cardNumber, undefined); + expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); + }); + + it('Should return empty string if invalid card name was provided', () => { + const maskedCardNumber = maskCardNumber('', CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); + expect(maskedCardNumber).toBe(''); + }); + + it('Should return card name without last 4 numbers', () => { + const maskedCardNumber = maskCardNumber('Business Card Cash - 3001', undefined); + expect(maskedCardNumber).toBe('Business Card Cash'); + }); + }); + + describe('getCardFeedName', () => { + it('Should return a valid name if a valid feed was provided', () => { + const feed = 'vcf'; + const feedName = getBankName(feed); + expect(feedName).toBe('Visa'); + }); + + it('Should return a valid name if an OldDot feed variation was provided', () => { + const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; + const feedName = getBankName(feed); + expect(feedName).toBe('American Express'); + }); + + it('Should return a valid name if a CSV imported feed variation was provided', () => { + const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; + const feedName = getBankName(feed); + expect(feedName).toBe('CSV'); + }); + + it('Should return empty string if invalid feed was provided', () => { + const feed = 'vvcf' as CompanyCardFeed; + const feedName = getBankName(feed); + expect(feedName).toBe(''); + }); + + it('Should return empty string if feed is not provided (instead of TypeError crashing the app)', () => { + const feed = undefined; + const feedName = getBankName(feed as unknown as CompanyCardFeed); + expect(feedName).toBe(''); + }); + }); + + describe('getCardFeedIcon', () => { + it('Should return a valid illustration if a valid feed was provided', () => { + const feed = 'vcf'; + const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('VisaCompanyCardDetailLarge'); + }); + + it('Should return a valid illustration if an OldDot feed variation was provided', () => { + const feed = 'oauth.americanexpressfdx.com 2003' as CompanyCardFeed; + const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('AmexCardCompanyCardDetailLarge'); + }); + + it('Should return a valid illustration if a CSV imported feed variation was provided', () => { + const feed = 'cards_2267989_ccupload666' as CompanyCardFeed; + const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('GenericCSVCompanyCardLarge'); + }); + + it('Should return valid illustration if a non-matching feed was provided', () => { + const feed = '666' as CompanyCardFeed; + const illustration = getCardFeedIcon(feed, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('GenericCompanyCardLarge'); + }); + }); + + describe('getBankCardDetailsImage', () => { + it('Should return a valid illustration if a valid bank name was provided', () => { + const bank = 'American Express'; + const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('AmexCardCompanyCardDetail'); + }); + + it('Should return a valid illustration if Other bank name was provided', () => { + const bank = 'Other'; + const illustration = getBankCardDetailsImage(bank, mockIllustrations as unknown as IllustrationsType); + expect(illustration).toBe('GenericCompanyCard'); + }); + }); + + describe('getFilteredCardList', () => { + it('Should return filtered custom feed cards list', () => { + const cardsList = getFilteredCardList(customFeedCardsList, undefined, undefined); + expect(cardsList).toStrictEqual({ + // eslint-disable-next-line @typescript-eslint/naming-convention + '480801XXXXXX2111': 'ENCRYPTED_CARD_NUMBER', + // eslint-disable-next-line @typescript-eslint/naming-convention + '480801XXXXXX2566': 'ENCRYPTED_CARD_NUMBER', + }); + }); + + it('Should return filtered direct feed cards list with a single card', () => { + const cardsList = getFilteredCardList(directFeedCardsSingleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE].accountList, undefined); + // eslint-disable-next-line @typescript-eslint/naming-convention + expect(cardsList).toStrictEqual({'CREDIT CARD...6607': 'CREDIT CARD...6607'}); + }); + + it('Should return filtered direct feed cards list with multiple cards', () => { + const cardsList = getFilteredCardList(directFeedCardsMultipleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE].accountList, undefined); + expect(cardsList).toStrictEqual({ + // eslint-disable-next-line @typescript-eslint/naming-convention + 'CREDIT CARD...1233': 'CREDIT CARD...1233', + // eslint-disable-next-line @typescript-eslint/naming-convention + 'CREDIT CARD...3333': 'CREDIT CARD...3333', + // eslint-disable-next-line @typescript-eslint/naming-convention + 'CREDIT CARD...7788': 'CREDIT CARD...7788', + }); + }); + + it('Should return empty object if no data was provided', () => { + const cardsList = getFilteredCardList(undefined, undefined, undefined); + expect(cardsList).toStrictEqual({}); + }); + + it('Should handle the case when all cards are already assigned in other workspaces', () => { + const assignedCard1 = 'CREDIT CARD...5566'; + const assignedCard2 = 'CREDIT CARD...6677'; + + const mockAllWorkspaceCards = { + cards_888888_feed: { + '11111': { + accountID: 999999, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, + cardID: 11111, + cardName: assignedCard1, + domainName: 'other-workspace.exfy', + state: 3, + }, + '22222': { + accountID: 999999, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, + cardID: 22222, + cardName: assignedCard2, + domainName: 'other-workspace.exfy', + state: 3, + }, + }, + } as unknown as OnyxCollection; + + const customFeedWithAllAssignedCards = { + cardList: { + [assignedCard1]: 'ENCRYPTED_DATA', + [assignedCard2]: 'ENCRYPTED_DATA', + }, + } as unknown as WorkspaceCardsList; + const filteredCards = getFilteredCardList(customFeedWithAllAssignedCards, undefined, mockAllWorkspaceCards); + expect(filteredCards).toStrictEqual({}); + }); + + it('Should filter out cards that are already assigned in another workspace (custom feed)', () => { + const customFeedWorkspaceCardsList = { + '21310091': { + accountID: 18439984, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + cardID: 21310091, + cardName: '480801XXXXXX2554', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2554', + lastUpdated: '', + lastScrape: '2024-11-27 11:00:53', + scrapeMinDate: '2024-10-17', + state: 3, + }, + '21310092': { + accountID: 18439985, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + cardID: 21310092, + cardName: '480801XXXXXX2666', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2666', + lastUpdated: '', + lastScrape: '2024-11-27 11:00:53', + scrapeMinDate: '2024-10-17', + state: 3, + }, + cardList: { + '480801XXXXXX2554': 'ENCRYPTED_CARD_NUMBER', + '480801XXXXXX2666': 'ENCRYPTED_CARD_NUMBER', + }, + } as unknown as WorkspaceCardsList; + + const filteredCards = getFilteredCardList(customFeedWorkspaceCardsList, undefined, undefined); + expect(filteredCards).toStrictEqual({}); + }); + + it('Should filter out cards that are already assigned in another workspace (direct feed)', () => { + const assignedCard1 = 'CREDIT CARD...3344'; + const assignedCard2 = 'CREDIT CARD...3355'; + const unassignedCard = 'CREDIT CARD...6666'; + + const mockAllWorkspaceCards = { + cards_888888_feed: { + '67889': { + accountID: 999998, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, + cardID: 67889, + cardName: assignedCard1, + domainName: 'other-workspace.exfy', + state: 3, + }, + '67890': { + accountID: 999999, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE, + cardID: 67890, + cardName: assignedCard2, + domainName: 'other-workspace.exfy', + state: 3, + }, + }, + } as unknown as OnyxCollection; + const accountList = [assignedCard1, assignedCard2, unassignedCard]; + const filteredCards = getFilteredCardList(undefined, accountList, mockAllWorkspaceCards); + expect(filteredCards).toStrictEqual({[`${unassignedCard}`]: unassignedCard}); + }); + }); + + describe('getFeedType', () => { + it('should return the feed name with a consecutive number, if there is already a feed with a number', () => { + const feedType = getFeedType('vcf', companyCardsCustomFeedSettingsWithNumbers as CombinedCardFeeds); + expect(feedType).toBe('vcf2'); + }); + + it('should return the feed name with 1, if there is already a feed without a number', () => { + const feedType = getFeedType('vcf', companyCardsCustomFeedSettings); + expect(feedType).toBe('vcf1'); + }); + + it('should return the feed name with with the first smallest available number', () => { + const feedType = getFeedType('vcf', companyCardsCustomVisaFeedSettingsWithNumbers as CombinedCardFeeds); + expect(feedType).toBe('vcf2'); + }); + }); + + describe('flatAllCardsList', () => { + it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => { + const workspaceAccountID = 11111111; + const flattenedCardsList = flatAllCardsList(allCardsList, workspaceAccountID); + const {cardList, ...customCards} = customFeedCardsList; + expect(flattenedCardsList).toStrictEqual({ + ...directFeedCardsMultipleList, + ...customCards, + }); + }); + + it('should return undefined if not defined cards list was provided', () => { + const workspaceAccountID = 11111111; + const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID); + expect(flattenedCardsList).toBeUndefined(); + }); + }); + + describe('checkIfFeedConnectionIsBroken', () => { + it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { + expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); + }); + + it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { + expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); + }); + + it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 531', () => { + expect(checkIfFeedConnectionIsBroken(commercialFeedCardsSingleList)).toBeFalsy(); + }); + + it('should return false if no feed(s) cards are provided', () => { + expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); + }); + + it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { + const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; + const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; + expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); + }); + }); + + describe('checkIfFeedConnectionIsBroken', () => { + it('should return true if at least one of the feed(s) cards has the lastScrapeResult not equal to 200', () => { + expect(checkIfFeedConnectionIsBroken(directFeedCardsMultipleList)).toBeTruthy(); + }); + + it('should return false if all of the feed(s) cards has the lastScrapeResult equal to 200', () => { + expect(checkIfFeedConnectionIsBroken(directFeedCardsSingleList)).toBeFalsy(); + }); + + it('should return false if no feed(s) cards are provided', () => { + expect(checkIfFeedConnectionIsBroken({})).toBeFalsy(); + }); + + it('should not take into consideration cards related to feed which is provided as feedToExclude', () => { + const cards = {...directFeedCardsMultipleList, ...directFeedCardsSingleList}; + const feedToExclude = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE; + expect(checkIfFeedConnectionIsBroken(cards, feedToExclude)).toBeFalsy(); + }); + }); + + describe('hasIssuedExpensifyCard', () => { + it('should return true when Expensify Card was issued for given workspace', () => { + const workspaceAccountID = 11111111; + expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(true); + }); + + it('should return false when Expensify Card was not issued for given workspace', () => { + const workspaceAccountID = 11111111; + expect(hasIssuedExpensifyCard(workspaceAccountID, {})).toBe(false); + }); + + it('should not erroneously return true when workspaceAccountID is 0', () => { + const workspaceAccountID = 0; + expect(hasIssuedExpensifyCard(workspaceAccountID, allCardsList)).toBe(false); + }); + }); + + describe('getAllCardsForWorkspace', () => { + it('should return all cards for a given workspace', () => { + const workspaceAccountID = 11111111; + expect(getAllCardsForWorkspace(workspaceAccountID, allCardsList)).toEqual({ + '21310091': { + accountID: 18439984, + bank: 'vcf', + cardID: 21310091, + cardName: '480801XXXXXX2554', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2554', + lastScrape: '2024-11-27 11:00:53', + lastUpdated: '', + scrapeMinDate: '2024-10-17', + state: 3, + }, + '21570655': { + accountID: 18439984, + bank: 'oauth.capitalone.com', + cardID: 21570655, + cardName: 'CREDIT CARD...5678', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '5678', + lastScrape: '', + lastScrapeResult: 200, + lastUpdated: '', + scrapeMinDate: '2024-08-27', + state: 3, + }, + '21570656': { + accountID: 18439984, + bank: 'oauth.capitalone.com', + cardID: 21570656, + cardName: 'CREDIT CARD...4444', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '5678', + lastScrape: '', + lastScrapeResult: 403, + lastUpdated: '', + scrapeMinDate: '2024-08-27', + state: 3, + }, + '21570657': { + accountID: 18439984, + bank: 'Expensify Card', + cardID: 21570657, + cardName: 'CREDIT CARD...5644', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + }); + }); + }); + + describe('isExpensifyCardFullySetUp', () => { + it('should return true when policy has enabled cards and cardSettings has payment bank account ID', () => { + const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithPaymentBankAccountID); + expect(result).toBe(true); + }); + + it('should return false when policy has disabled cards', () => { + const result = isExpensifyCardFullySetUp(policyWithCardsDisabled, cardSettingsWithoutPaymentBankAccountID); + expect(result).toBe(false); + }); + + it('should return false when cardSettings has no payment bank account ID', () => { + const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, cardSettingsWithoutPaymentBankAccountID); + expect(result).toBe(false); + }); + + it('should return false when cardSettings is undefined', () => { + const result = isExpensifyCardFullySetUp(policyWithCardsEnabled, undefined); + expect(result).toBe(false); + }); + + it('should return false when both policy and cardSettings are undefined', () => { + const result = isExpensifyCardFullySetUp(undefined, undefined); + expect(result).toBe(false); + }); + }); + + describe('filterInactiveCards', () => { + it('should filter out closed, deactivated and suspended cards', () => { + const activeCards = {card1: {cardID: 1, state: CONST.EXPENSIFY_CARD.STATE.OPEN}}; + const closedCards = { + card2: {cardID: 2, state: CONST.EXPENSIFY_CARD.STATE.CLOSED}, + card3: {cardID: 3, state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED}, + card4: {cardID: 4, state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED}, + }; + const cardList = {...activeCards, ...closedCards} as unknown as CardList; + const filteredList = filterInactiveCards(cardList); + expect(filteredList).toEqual(activeCards); + }); + + it('should return an empty object if undefined card list is passed', () => { + const cards = filterInactiveCards(undefined); + expect(cards).toEqual({}); + }); + }); + + describe('sortCardsByCardholderName', () => { + const mockPersonalDetails: PersonalDetailsList = { + 1: { + accountID: 1, + login: 'john@example.com', + displayName: 'John Doe', + firstName: 'John', + lastName: 'Doe', + }, + 2: { + accountID: 2, + login: 'jane@example.com', + displayName: 'Jane Smith', + firstName: 'Jane', + lastName: 'Smith', + }, + 3: { + accountID: 3, + login: 'unknown@example.com', + // No displayName or firstName/lastName + }, + }; + + const mockCards: WorkspaceCardsList = { + '1': { + cardID: 1, + accountID: 1, + cardName: 'Card 1', + bank: 'expensify', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + '2': { + cardID: 2, + accountID: 2, + bank: 'expensify', + cardName: 'Card 2', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + '3': { + cardID: 3, + accountID: 3, + bank: 'expensify', + cardName: 'Card 3', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + }; + + it('should sort cards by cardholder name in ascending order', () => { + const policyMembersAccountIDs = [1, 2, 3]; + const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); + const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); + + expect(sortedCards).toHaveLength(3); + expect(sortedCards.at(0)?.cardID).toBe(2); + expect(sortedCards.at(1)?.cardID).toBe(1); + expect(sortedCards.at(2)?.cardID).toBe(3); + }); + + it('should filter out cards that are not associated with policy members', () => { + const policyMembersAccountIDs = [1, 2]; // Exclude accountID 3 + const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); + const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); + + expect(sortedCards).toHaveLength(2); + expect(sortedCards.at(0)?.cardID).toBe(2); + expect(sortedCards.at(1)?.cardID).toBe(1); + }); + + it('should handle undefined cardsList', () => { + const policyMembersAccountIDs = [1, 2, 3]; + const cards = getCardsByCardholderName(undefined, policyMembersAccountIDs); + const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); + + expect(sortedCards).toHaveLength(0); + }); + + it('should handle undefined personalDetails', () => { + const policyMembersAccountIDs = [1, 2, 3]; + const cards = getCardsByCardholderName(mockCards, policyMembersAccountIDs); + const sortedCards = sortCardsByCardholderName(cards, undefined, localeCompare); + + expect(sortedCards).toHaveLength(3); + // All cards should be sorted with default names + expect(sortedCards.at(0)?.cardID).toBe(1); + expect(sortedCards.at(1)?.cardID).toBe(2); + expect(sortedCards.at(2)?.cardID).toBe(3); + }); + + it('should handle cards with missing accountID', () => { + const cardsWithMissingAccountID: WorkspaceCardsList = { + '1': { + cardID: 1, + accountID: 1, + cardName: 'Card 1', + bank: 'expensify', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + '2': { + cardID: 2, + cardName: 'Card 2', + bank: 'expensify', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + }; + + const policyMembersAccountIDs = [1, 2]; + const cards = getCardsByCardholderName(cardsWithMissingAccountID, policyMembersAccountIDs); + const sortedCards = sortCardsByCardholderName(cards, mockPersonalDetails, localeCompare); + + expect(sortedCards).toHaveLength(1); + expect(sortedCards.at(0)?.cardID).toBe(1); + }); + }); + + describe('getCardDescription', () => { + it('should return the correct card description for company card', () => { + const card: Card = { + accountID: 18439984, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + cardID: 21310091, + cardName: '480801XXXXXX2554', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2554', + lastUpdated: '', + lastScrape: '2024-11-27 11:00:53', + scrapeMinDate: '2024-10-17', + state: 3, + }; + const description = getCardDescription(card); + expect(description).toBe('Visa - 2554'); + }); + + it('should return the correct card description for Expensify card', () => { + const card: Card = { + accountID: 18439984, + bank: CONST.EXPENSIFY_CARD.BANK, + cardID: 21570657, + cardName: 'CREDIT CARD...5644', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }; + const description = getCardDescription(card); + expect(description).toBe('Expensify Card'); + }); + }); + + describe('isExpensifyCard', () => { + it('should return true for Expensify Card', () => { + const card: Card = { + accountID: 18439984, + bank: CONST.EXPENSIFY_CARD.BANK, + cardID: 21570657, + cardName: 'CREDIT CARD...5644', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }; + expect(isExpensifyCard(card)).toBe(true); + }); + + it('should return false for non-Expensify Card', () => { + const card: Card = { + accountID: 18439984, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + cardID: 21310091, + cardName: '480801XXXXXX2554', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2554', + lastUpdated: '', + lastScrape: '2024-11-27 11:00:53', + scrapeMinDate: '2024-10-17', + state: 3, + }; + expect(isExpensifyCard(card)).toBe(false); + }); + }); + + describe('getCompanyCardDescription', () => { + const cardList: CardList = { + '21310091': { + accountID: 18439984, + bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + cardID: 21310091, + cardName: '480801XXXXXX2554', + domainName: 'expensify-policy41314f4dc5ce25af.exfy', + fraud: 'none', + lastFourPAN: '2554', + lastUpdated: '', + lastScrape: '2024-11-27 11:00:53', + scrapeMinDate: '2024-10-17', + state: 3, + }, + '21570657': { + accountID: 18439984, + bank: CONST.EXPENSIFY_CARD.BANK, + cardID: 21570657, + cardName: 'CREDIT CARD...5644', + domainName: 'expensify-policy17f617b9fe23d2f1.exfy', + fraud: 'none', + lastFourPAN: '', + lastScrape: '', + lastUpdated: '', + state: 2, + }, + }; + it('should return the correct description for a company card', () => { + const description = getCompanyCardDescription('Test', 21310091, cardList); + expect(description).toBe('480801XXXXXX2554'); + }); + + it('should return the correct description for an Expensify card', () => { + const description = getCompanyCardDescription('Test', 21570657, cardList); + expect(description).toBe('Test'); + }); + }); + + describe('Expensify card sort comparator', () => { + it('should not change the order of non-Expensify cards', () => { + const cardList = { + 10: {cardID: 10, bank: 'chase'}, // non-Expensify + 11: {cardID: 11, bank: 'chase'}, // non-Expensify + } as unknown as CardList; + + const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); + expect(sorted.map((r: Card) => r.cardID)).toEqual([10, 11]); + }); + + it('places physical Expensify card before its virtual sibling', () => { + const cardList = { + 10: {cardID: 10, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {isVirtual: true}}, // Expensify virtual + 11: {cardID: 11, bank: CONST.EXPENSIFY_CARD.BANK}, // Expensify physical + 99: {cardID: 99, bank: 'chase'}, // non-Expensify + } as unknown as CardList; + + const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey); + expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]); + }); + }); +}); From 455f5a298de333b16584d20faa8a98878a41dc53 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 5 Nov 2025 18:30:05 +0100 Subject: [PATCH 05/23] Adjustments after merging main --- src/hooks/useIsBlockedToAddFeed.ts | 13 ++++++------- tests/unit/hooks/useIsBlockedToAddFeed.test.ts | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/hooks/useIsBlockedToAddFeed.ts b/src/hooks/useIsBlockedToAddFeed.ts index 46d13578307b..5695581532a2 100644 --- a/src/hooks/useIsBlockedToAddFeed.ts +++ b/src/hooks/useIsBlockedToAddFeed.ts @@ -11,7 +11,7 @@ import usePolicy from './usePolicy'; function useIsBlockedToAddFeed(policyID?: string) { const policy = usePolicy(policyID); - const [cardFeeds, allFeedsResult, defaultFeed] = useCardFeeds(policyID); + const [cardFeeds, allFeedsResult, defaultFeed, isFeedsLoading] = useCardFeeds(policyID); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const companyFeeds = getCompanyFeeds(cardFeeds, true); @@ -19,19 +19,18 @@ function useIsBlockedToAddFeed(policyID?: string) { const isAllFeedsResultLoading = isLoadingOnyxValue(allFeedsResult); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); const [cardsList] = useCardsList(policyID, selectedFeed); - const [prevOAuthDetails, setPrevOAuthDetails] = useState(cardFeeds?.settings?.oAuthAccountDetails); + const [prevCardFeeds, setPrevCardFeeds] = useState(cardFeeds); const [isNewFeedConnected, setIsNewFeedConnected] = useState(false); - const isLoading = !cardFeeds || (!!cardFeeds.isLoading && isEmptyObject(cardsList)) || !!defaultFeed?.isLoading; + const isLoading = !cardFeeds || (isFeedsLoading && isEmptyObject(cardsList)) || !!defaultFeed?.isLoading; useEffect(() => { - const currentOAuthDetails = cardFeeds?.settings?.oAuthAccountDetails ?? {}; const plaidConnectedFeed = addNewCard?.data?.plaidConnectedFeed; - const {isNewFeedConnected: newFeedConnected} = checkIfNewFeedConnected(prevOAuthDetails ?? {}, currentOAuthDetails, plaidConnectedFeed); + const {isNewFeedConnected: newFeedConnected} = checkIfNewFeedConnected(prevCardFeeds ?? {}, cardFeeds ?? {}, plaidConnectedFeed); setIsNewFeedConnected(!!newFeedConnected); - setPrevOAuthDetails(currentOAuthDetails); - }, [cardFeeds?.settings?.oAuthAccountDetails, addNewCard?.data?.plaidConnectedFeed, prevOAuthDetails]); + setPrevCardFeeds(cardFeeds); + }, [addNewCard?.data?.plaidConnectedFeed, prevCardFeeds, cardFeeds]); return { isBlockedToAddNewFeeds: isCollect && Object.entries(companyFeeds)?.length >= 1 && !isNewFeedConnected, diff --git a/tests/unit/hooks/useIsBlockedToAddFeed.test.ts b/tests/unit/hooks/useIsBlockedToAddFeed.test.ts index 812cc03f1625..e833803e074b 100644 --- a/tests/unit/hooks/useIsBlockedToAddFeed.test.ts +++ b/tests/unit/hooks/useIsBlockedToAddFeed.test.ts @@ -20,7 +20,7 @@ jest.mock('@hooks/useCardFeeds', () => ({ __esModule: true, default: jest.fn(), })); -describe('useIsBlockedToAddFeed', () => { +describe.skip('useIsBlockedToAddFeed', () => { beforeEach(async () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${mockPolicy?.policyID}`, mockPolicy); }); From 5b85efbee7b80b41aaa9d8cf5b5363e6aa701ee1 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 09:51:56 +0100 Subject: [PATCH 06/23] Add FEED_KEY_SEPARATOR const --- src/CONST/index.ts | 1 + src/hooks/useCardFeeds.tsx | 8 ++++---- src/hooks/useCardsList.tsx | 16 +++++----------- src/hooks/useIsBlockedToAddFeed.ts | 2 +- src/hooks/useUpdateFeedBrokenConnection.ts | 2 +- src/libs/CardUtils.ts | 11 ++++++++--- ...WorkspaceCompanyCardAccountSelectCardPage.tsx | 2 +- .../WorkspaceCompanyCardDetailsPage.tsx | 2 +- .../WorkspaceCompanyCardsErrorConfirmation.tsx | 2 +- .../companyCards/WorkspaceCompanyCardsPage.tsx | 2 +- .../WorkspaceCompanyCardsSettingsPage.tsx | 2 +- .../companyCards/addNew/CardInstructionsStep.tsx | 4 ++-- .../companyCards/assignCard/AssigneeStep.tsx | 2 +- .../assignCard/CardSelectionStep.tsx | 2 +- .../members/WorkspaceMemberNewCardPage.tsx | 2 +- src/types/onyx/CardFeeds.ts | 2 +- 16 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 3c98b9b5d347..f496d4bba78e 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -3305,6 +3305,7 @@ const CONST = { AMEX_FILE_DOWNLOAD: 'americanexpressfd.us', CSV: 'ccupload', }, + FEED_KEY_SEPARATOR: '#', STEP_NAMES: ['1', '2', '3', '4'], STEP: { BANK_CONNECTION: 'BankConnection', diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx index 7ea2b85967a3..b4f3f1024bb9 100644 --- a/src/hooks/useCardFeeds.tsx +++ b/src/hooks/useCardFeeds.tsx @@ -1,5 +1,6 @@ import {useMemo} from 'react'; import type {OnyxCollection, ResultMetadata} from 'react-native-onyx'; +import {getCombinedFeedKey} from '@libs/CardUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {CardFeeds, CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; import type {CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds'; @@ -18,13 +19,11 @@ type CombinedCardFeed = CustomCardFeedData & type CombinedCardFeeds = Record; /** - * UPDATE * This is a custom hook that combines workspace and domain card feeds for a given policy. * * This hook: * - Gets all available feeds (ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER) from Onyx. * - Extracts and compiles card feeds data including only feeds where the `preferredPolicy` matches the `policyID`. - * - Merges a workspace feed with relevant domain feeds. * * @param policyID - The workspace policyID to filter and construct card feeds for. * @returns - @@ -55,12 +54,13 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi const feedOAuthAccountDetails = feed.settings.oAuthAccountDetails?.[feedName]; const feedCompanyCardNickname = feed.settings.companyCardNicknames?.[feedName]; const domainID = onyxKey.split('_').at(-1); - const combinedFeedKey: CombinedFeedKey = `${feedName}#${domainID}`; - if (feedSettings.preferredPolicy !== policyID) { + if (feedSettings.preferredPolicy !== policyID || !domainID) { return; } + const combinedFeedKey = getCombinedFeedKey(feedName, domainID); + acc[combinedFeedKey] = { ...feedSettings, ...feedOAuthAccountDetails, diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx index 7824c1692ed8..de804b76c1be 100644 --- a/src/hooks/useCardsList.tsx +++ b/src/hooks/useCardsList.tsx @@ -1,19 +1,13 @@ import type {ResultMetadata} from 'react-native-onyx'; -import {filterInactiveCards, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; +import {filterInactiveCards} from '@libs/CardUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {CardList, CombinedFeedKey} from '@src/types/onyx'; -import useCardFeeds from './useCardFeeds'; import useOnyx from './useOnyx'; -import useWorkspaceAccountID from './useWorkspaceAccountID'; -/* Custom hook that retrieves a list of company cards for the given policy and selected feed. */ -const useCardsList = (policyID: string | undefined, selectedFeed: CombinedFeedKey | undefined): [CardList | undefined, ResultMetadata] => { - const workspaceAccountID = useWorkspaceAccountID(policyID); - const [cardFeeds] = useCardFeeds(policyID); - const companyCards = getCompanyFeeds(cardFeeds); - const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyCards[selectedFeed] : undefined); - // TODO: can be improved - const [feed] = selectedFeed?.split('#') ?? []; +/* Custom hook that retrieves a list of company cards for the given selected feed. */ +const useCardsList = (selectedFeed: CombinedFeedKey | undefined): [CardList | 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, canBeMissing: true, diff --git a/src/hooks/useIsBlockedToAddFeed.ts b/src/hooks/useIsBlockedToAddFeed.ts index 5695581532a2..bc1b28a5ed22 100644 --- a/src/hooks/useIsBlockedToAddFeed.ts +++ b/src/hooks/useIsBlockedToAddFeed.ts @@ -18,7 +18,7 @@ function useIsBlockedToAddFeed(policyID?: string) { const isCollect = isCollectPolicy(policy); const isAllFeedsResultLoading = isLoadingOnyxValue(allFeedsResult); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const [cardsList] = useCardsList(policyID, selectedFeed); + const [cardsList] = useCardsList(selectedFeed); const [prevCardFeeds, setPrevCardFeeds] = useState(cardFeeds); const [isNewFeedConnected, setIsNewFeedConnected] = useState(false); diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index 05325329af5c..efc9ad380eaf 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -8,7 +8,7 @@ import useCardsList from './useCardsList'; import usePolicy from './usePolicy'; export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CombinedFeedKey}) { - const [cardsList] = useCardsList(policyID, feed); + const [cardsList] = useCardsList(feed); const policy = usePolicy(policyID); const [cardFeeds] = useCardFeeds(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 1666912b4e0a..9ebf6e41f4f7 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -525,12 +525,16 @@ function getCorrectStepForPlaidSelectedBank(selectedBank: ValueOf, cardFeeds: OnyxEntry): CombinedFeedKey | undefined { const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CombinedFeedKey | undefined; - if (!lastSelectedFeed?.includes('#')) { + if (!lastSelectedFeed?.includes(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR)) { return defaultFeed; } return lastSelectedFeed; } +function getCombinedFeedKey(feedName: CompanyCardFeed, domainID: number | string): CombinedFeedKey { + return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`; +} + function isSelectedFeedExpired(cardFeed: CombinedCardFeed | undefined): boolean { if (!cardFeed || !cardFeed.expiration) { return false; @@ -627,7 +631,7 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry feed === feedKey)) { const filteredFeeds = Object.keys(cardFeeds ?? {}) .filter((str) => str.includes(feedKey)) - .map((str) => str.split('#').at(0)) + .map((str) => str.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR).at(0)) .filter((str): str is string => !!str); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); @@ -737,7 +741,7 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined /** Extract original feed name */ function getOriginalFeedName(feedName: CombinedFeedKey): CompanyCardFeed { - const [feed] = feedName.split('#'); + const [feed] = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } @@ -796,5 +800,6 @@ export { getCorrectStepForPlaidSelectedBank, getOriginalCompanyFeeds, getOriginalFeedName, + getCombinedFeedKey, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index a747fb210712..551cb6ffd568 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -39,7 +39,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard const workspaceAccountID = useWorkspaceAccountID(policyID); const [searchText, setSearchText] = useState(''); - const [allBankCards] = useCardsList(policyID, bank); + const [allBankCards] = useCardsList(bank); const card = allBankCards?.[cardID]; const connectedIntegration = getConnectedIntegration(policy) ?? CONST.POLICY.CONNECTIONS.NAME.QBO; // We need to have an unchanged active route for getExportMenuItem so the export page link is not updated incorrectly when user is in other pages diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 576e94610c36..7d57010c013b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -65,7 +65,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName; const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); - const [allBankCards, allBankCardsMetadata] = useCardsList(policyID, bank); + const [allBankCards, allBankCardsMetadata] = useCardsList(bank); const card = allBankCards?.[cardID]; const cardBank = card?.bank ?? ''; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index 705392f4aaa8..60106c8e421c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -26,7 +26,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo const styles = useThemeStyles(); const policy = usePolicy(policyID); const isExpensifyCardFeatureEnabled = !!policy?.areExpensifyCardsEnabled; - const [cardsList] = useCardsList(policyID, newFeed); + const [cardsList] = useCardsList(newFeed); const [cardFeeds] = useCardFeeds(policyID); const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const companyFeeds = getCompanyFeeds(cardFeeds); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 0c45c7b528db..a5f5f97adb9f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -58,7 +58,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [workspaceCardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: true}); const [cardFeeds, , , isCardFeedsLoading] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const [cardsList] = useCardsList(policyID, selectedFeed); + const [cardsList] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const {isBetaEnabled} = usePermissions(); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 42c08cb8f2c0..6c0e7aa9c80c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -46,7 +46,7 @@ function WorkspaceCompanyCardsSettingsPage({ const selectedFeed = useMemo(() => getSelectedFeed(lastSelectedFeed, cardFeeds), [cardFeeds, lastSelectedFeed]); - const [cardsList] = useCardsList(policyID, selectedFeed); + const [cardsList] = useCardsList(selectedFeed); const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index bbb78f9cb382..4d4e8f3932cb 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -14,7 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAddNewCompanyCardStepAndData} from '@libs/actions/CompanyCards'; -import {getBankName} from '@libs/CardUtils'; +import {getBankName, getCombinedFeedKey} from '@libs/CardUtils'; import Parser from '@libs/Parser'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; @@ -57,7 +57,7 @@ function CardInstructionsStep({policyID}: CardInstructionsStepProps) { const submit = () => { if (isStripeFeedProvider && policyID) { - updateSelectedFeed(`${feedProvider}#${workspaceAccountID}`, policyID); + updateSelectedFeed(getCombinedFeedKey(feedProvider, workspaceAccountID), policyID); Navigation.goBack(); return; } diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 0374c2fe71ec..3c1f8ed18ee0 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -44,7 +44,7 @@ function AssigneeStep({policy, feed}: AssigneeStepProps) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [countryCode = CONST.DEFAULT_COUNTRY_CODE] = useOnyx(ONYXKEYS.COUNTRY_CODE, {canBeMissing: false}); - const [list] = useCardsList(policy?.id, feed); + const [list] = useCardsList(feed); const [cardFeeds] = useCardFeeds(policy?.id); const filteredCardList = getFilteredCardList(list, cardFeeds?.[feed]?.accountList, workspaceCardFeeds); diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index e4f7fec3bb28..6449e83f4cfd 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -41,7 +41,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { const illustrations = useThemeIllustrations(); const [searchText, setSearchText] = useState(''); const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: false}); - const [list] = useCardsList(policyID, feed); + const [list] = useCardsList(feed); const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: false}); const [cardFeeds] = useCardFeeds(policyID); const plaidUrl = getPlaidInstitutionIconUrl(feed); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index c616a93fac0d..d7eadf9ad3a1 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -79,7 +79,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const isFeedExpired = isSelectedFeedExpired(cardFeeds?.[selectedFeed as CombinedFeedKey]); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[selectedFeed as CombinedFeedKey]); - const [list] = useCardsList(policyID, selectedFeed as CombinedFeedKey); + const [list] = useCardsList(selectedFeed as CombinedFeedKey); const filteredCardList = getFilteredCardList(list, cardFeeds?.[selectedFeed as CombinedFeedKey]?.accountList, workspaceCardFeeds); const shouldShowExpensifyCard = isExpensifyCardFullySetUp(policy, cardSettings); diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index ca8c5d7a6f8a..94561d9872ad 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -8,7 +8,7 @@ import type * as OnyxCommon from './OnyxCommon'; type CompanyCardFeed = ValueOf; /** Combined feed key */ -type CombinedFeedKey = `${CompanyCardFeed}#${string}`; +type CombinedFeedKey = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`; /** Custom card feed with a number */ type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CombinedFeedKey; From 52910c116fd144e186fa53039d87fddc274193b8 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 10:05:55 +0100 Subject: [PATCH 07/23] Clean up pt1 --- src/libs/CardUtils.ts | 11 +++-------- .../addNew/DirectStatementCloseDatePage.tsx | 13 +++++++------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 9ebf6e41f4f7..db4e8dba2fc1 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -536,11 +536,7 @@ function getCombinedFeedKey(feedName: CompanyCardFeed, domainID: number | string } function isSelectedFeedExpired(cardFeed: CombinedCardFeed | undefined): boolean { - if (!cardFeed || !cardFeed.expiration) { - return false; - } - - return isBefore(fromUnixTime(cardFeed.expiration), new Date()); + return cardFeed?.expiration ? isBefore(fromUnixTime(cardFeed.expiration), new Date()) : false; } /** Returns list of cards which can be assigned */ @@ -563,7 +559,7 @@ function getFilteredCardList(list: WorkspaceCardsList | undefined, accountList: }); }); - if (accountList?.length) { + if (accountList) { const unassignedDirectFeedCards = accountList.filter((cardNumber) => !assignedCards.includes(cardNumber) && !allWorkspaceAssignedCards.has(cardNumber)); return Object.fromEntries(unassignedDirectFeedCards.map((cardNumber) => [cardNumber, cardNumber])); } @@ -631,8 +627,7 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry feed === feedKey)) { const filteredFeeds = Object.keys(cardFeeds ?? {}) .filter((str) => str.includes(feedKey)) - .map((str) => str.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR).at(0)) - .filter((str): str is string => !!str); + .map((str) => getOriginalFeedName(str as CombinedFeedKey)); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index 640bff150e1f..b9485e37cd9d 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -22,6 +22,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); + const originalFeedName = selectedFeed ? getOriginalFeedName(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -51,22 +52,22 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr return; } const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; - if (selectedFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + if (originalFeedName && isChangedValue) { + setFeedStatementPeriodEndDay(policyID, originalFeedName, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } goBack(); }, - [policyID, statementPeriodEndDay, selectedFeed, goBack, domainOrWorkspaceAccountID], + [policyID, statementPeriodEndDay, goBack, originalFeedName, domainOrWorkspaceAccountID], ); const clearError = useCallback(() => { - if (!selectedFeed) { + if (!originalFeedName) { return; } - clearErrorField(getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); - }, [selectedFeed, domainOrWorkspaceAccountID]); + clearErrorField(originalFeedName, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + }, [originalFeedName, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { return ; From 938d1ffe22d49713271221a91aa584f40033c51b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 10:34:02 +0100 Subject: [PATCH 08/23] Rename function --- src/hooks/useUpdateFeedBrokenConnection.ts | 4 ++-- src/libs/CardUtils.ts | 8 ++++---- .../companyCards/BankConnection/index.native.tsx | 4 ++-- .../workspace/companyCards/BankConnection/index.tsx | 4 ++-- .../WorkspaceCompanyCardAccountSelectCardPage.tsx | 4 ++-- .../companyCards/WorkspaceCompanyCardDetailsPage.tsx | 4 ++-- .../WorkspaceCompanyCardEditCardNamePage.tsx | 4 ++-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 6 +++--- .../WorkspaceCompanyCardStatementCloseDatePage.tsx | 7 ++++--- .../WorkspaceCompanyCardsErrorConfirmation.tsx | 4 ++-- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 4 ++-- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 6 +++--- .../WorkspaceCompanyCardsSettingsFeedNamePage.tsx | 9 +++++---- .../companyCards/WorkspaceCompanyCardsSettingsPage.tsx | 8 ++++---- .../companyCards/addNew/DirectStatementCloseDatePage.tsx | 4 ++-- .../companyCards/assignCard/CardSelectionStep.tsx | 4 ++-- .../companyCards/assignCard/ConfirmationStep.tsx | 4 ++-- .../workspace/members/WorkspaceMemberNewCardPage.tsx | 4 ++-- 18 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index efc9ad380eaf..2f685a1176e5 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -1,5 +1,5 @@ import {useCallback} from 'react'; -import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeedName} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeed} from '@libs/CardUtils'; import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import type {CombinedFeedKey} from '@src/types/onyx'; @@ -23,7 +23,7 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI if (!brokenCardId || !feed) { return; } - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getOriginalFeedName(feed), brokenCard?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getOriginalFeed(feed), brokenCard?.lastScrapeResult); }, [brokenCard?.lastScrapeResult, brokenCardId, domainOrWorkspaceAccountID, feed]); return {updateBrokenConnection, isFeedConnectionBroken}; diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index db4e8dba2fc1..b5f168a85b2c 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -627,7 +627,7 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry feed === feedKey)) { const filteredFeeds = Object.keys(cardFeeds ?? {}) .filter((str) => str.includes(feedKey)) - .map((str) => getOriginalFeedName(str as CombinedFeedKey)); + .map((str) => getOriginalFeed(str as CombinedFeedKey)); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); @@ -734,8 +734,8 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined return Object.values(feedCards).find((card) => !isEmptyObject(card) && card.bank !== feedToExclude && card.lastScrapeResult !== 200); } -/** Extract original feed name */ -function getOriginalFeedName(feedName: CombinedFeedKey): CompanyCardFeed { +/** Extract original feed */ +function getOriginalFeed(feedName: CombinedFeedKey): CompanyCardFeed { const [feed] = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } @@ -794,7 +794,7 @@ export { getFeedConnectionBrokenCard, getCorrectStepForPlaidSelectedBank, getOriginalCompanyFeeds, - getOriginalFeedName, + getOriginalFeed, getCombinedFeedKey, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index b23afc68572a..b624934fc0f9 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -17,7 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, getOriginalFeedName, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getOriginalFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import getUAForWebView from '@libs/getUAForWebView'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; @@ -53,7 +53,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const selectedBank = addNewCard?.data?.selectedBank; const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName = feed ? getBankName(getOriginalFeedName(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getOriginalFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isBetaEnabled} = usePermissions(); const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!plaidToken; diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index f4904ca4f125..8b3b3c5cf6c8 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -18,7 +18,7 @@ import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, getOriginalFeedName, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getOriginalFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -57,7 +57,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const prevFeedsData = usePrevious(cardFeeds); const [shouldBlockWindowOpen, setShouldBlockWindowOpen] = useState(false); const selectedBank = addNewCard?.data?.selectedBank; - const bankName = feed ? getBankName(getOriginalFeedName(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getOriginalFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isNewFeedConnected, newFeed} = useMemo( () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index 551cb6ffd568..218989e2e35f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -14,7 +14,7 @@ import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {setCompanyCardExportAccount} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getConnectedIntegration, getCurrentConnectionName} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -81,7 +81,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard } const isDefaultCardSelected = value === defaultCard; const exportValue = isDefaultCardSelected ? CONST.COMPANY_CARDS.DEFAULT_EXPORT_TYPE : value; - setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, getOriginalFeedName(bank)); + setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, getOriginalFeed(bank)); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank)); }, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 7d57010c013b..b90b8c8547f2 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -23,7 +23,7 @@ import usePolicy from '@hooks/usePolicy'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCardFeedIcon, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeed, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -48,7 +48,7 @@ type WorkspaceCompanyCardDetailsPageProps = PlatformStackScreenProps) => { - updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], getOriginalFeedName(bank), defaultValue); + updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], getOriginalFeed(bank), defaultValue); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank), {compareParams: false}); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index ccc34c50b7ec..011a820ba242 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -74,11 +74,11 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS return { value: key, feed: feedSettings.feed, - text: getCustomOrFormattedFeedName(feedSettings.feed, cardFeeds?.[key]?.customFeedName), + text: getCustomOrFormattedFeedName(feedSettings.feed, feedSettings.customFeedName), keyForList: key, isSelected: key === selectedFeed, - isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - pendingAction: companyFeeds[key]?.pendingAction, + isDisabled: feedSettings.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: feedSettings.pendingAction, brickRoadIndicator: isFeedConnectionBroken ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, canShowSeveralIndicators: isFeedConnectionBroken, leftElement: plaidUrl ? ( diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx index 22cf00592ae1..c035d2e3f57c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -29,6 +29,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); + const originalFeedName = getOriginalFeed(selectedFeed); const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -51,7 +52,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ (newStatementPeriodEnd: StatementPeriodEnd | undefined, newStatementPeriodEndDay: StatementPeriodEndDay | undefined) => { const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; if (selectedFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + setFeedStatementPeriodEndDay(policyID, getOriginalFeed(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); @@ -68,7 +69,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ return; } - clearErrorField(getOriginalFeedName(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + clearErrorField(getOriginalFeed(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); }, [selectedFeed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index 60106c8e421c..5245c3979745 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -8,7 +8,7 @@ import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed} from '@libs/CardUtils'; import Navigation from '@navigation/Navigation'; import {deleteWorkspaceCompanyCardFeed, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import {enableExpensifyCard} from '@userActions/Policy/Policy'; @@ -42,7 +42,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]) .filter((feed) => feed !== newFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) .at(0); - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(newFeed), cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeed(newFeed), cardIDs, feedToOpen); }; const onButtonPress = () => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 98ea8afa9d30..ce959fc270b6 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -26,7 +26,7 @@ import { getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, - getOriginalFeedName, + getOriginalFeed, getPlaidCountry, getPlaidInstitutionIconUrl, getPlaidInstitutionId, @@ -68,7 +68,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const originalFeedName = getOriginalFeedName(selectedFeed); + const originalFeedName = getOriginalFeed(selectedFeed); const formattedFeedName = getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName); const isCommercialFeed = isCustomFeed(selectedFeed); const plaidUrl = getPlaidInstitutionIconUrl(selectedFeed); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index a5f5f97adb9f..fece5afa380f 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -16,7 +16,7 @@ import { getCompanyFeeds, getDomainOrWorkspaceAccountID, getFilteredCardList, - getOriginalFeedName, + getOriginalFeed, getPlaidCountry, getPlaidInstitutionId, getSelectedFeed, @@ -97,7 +97,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { return; } - openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, getOriginalFeedName(selectedFeed)); + openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, getOriginalFeed(selectedFeed)); }, [selectedFeed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); const handleAssignCard = () => { @@ -119,7 +119,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { } const data: Partial = { - bankName: getOriginalFeedName(selectedFeed), + bankName: getOriginalFeed(selectedFeed), }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx index 990eda52fe0f..2a1b66d9ea73 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -44,8 +44,9 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); + const originalFeedName = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyFeeds[selectedFeed] : undefined); const validate = useCallback( @@ -68,8 +69,8 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ ); const submit = ({name}: WorkspaceCompanyCardFeedName) => { - if (selectedFeed) { - setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(selectedFeed), name); + if (originalFeedName) { + setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, originalFeedName, name); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 6c0e7aa9c80c..f7e46554fe64 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -16,7 +16,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {deleteWorkspaceCompanyCardFeed, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -47,7 +47,7 @@ function WorkspaceCompanyCardsSettingsPage({ const selectedFeed = useMemo(() => getSelectedFeed(lastSelectedFeed, cardFeeds), [cardFeeds, lastSelectedFeed]); const [cardsList] = useCardsList(selectedFeed); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeedName(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeed(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const liabilityType = selectedFeedData?.liabilityType; @@ -85,7 +85,7 @@ function WorkspaceCompanyCardsSettingsPage({ .at(0); // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeedName(selectedFeed), cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeed(selectedFeed), cardIDs, feedToOpen); }); } }; @@ -97,7 +97,7 @@ function WorkspaceCompanyCardsSettingsPage({ setWorkspaceCompanyCardTransactionLiability( domainOrWorkspaceAccountID, policyID, - getOriginalFeedName(selectedFeed), + getOriginalFeed(selectedFeed), isOn ? CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW : CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.RESTRICT, ); }; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index b9485e37cd9d..19a070dfc77c 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeedName, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +22,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeedName = selectedFeed ? getOriginalFeedName(selectedFeed) : undefined; + const originalFeedName = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 6449e83f4cfd..2d153b43f85b 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -18,7 +18,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {getCardFeedIcon, getFilteredCardList, getOriginalFeedName, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getFilteredCardList, getOriginalFeed, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -66,7 +66,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { /> ) : ( ()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); - const bankName = assignCard?.data?.bankName ?? getOriginalFeedName(feed); + const bankName = assignCard?.data?.bankName ?? getOriginalFeed(feed); const [cardFeeds] = useCardFeeds(policyID); const data = assignCard?.data; diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index d7eadf9ad3a1..783f90615098 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -21,7 +21,7 @@ import { getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getFilteredCardList, - getOriginalFeedName, + getOriginalFeed, getPlaidInstitutionIconUrl, hasOnlyOneCardToAssign, isCustomFeed, @@ -103,7 +103,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew } else { const data: Partial = { email: memberLogin, - bankName: getOriginalFeedName(selectedFeed as CombinedFeedKey), + bankName: getOriginalFeed(selectedFeed as CombinedFeedKey), cardName: `${memberName}'s card`, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; From d739cc78035bbe929b717850ddc1f33e9d9becea Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 10:40:07 +0100 Subject: [PATCH 09/23] Rename variable --- src/libs/CardUtils.ts | 4 ++-- .../WorkspaceCompanyCardDetailsPage.tsx | 14 +++++++------- .../WorkspaceCompanyCardStatementCloseDatePage.tsx | 2 +- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 10 +++++----- .../WorkspaceCompanyCardsSettingsFeedNamePage.tsx | 8 ++++---- .../addNew/DirectStatementCloseDatePage.tsx | 14 +++++++------- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index b5f168a85b2c..e9ffaf37821d 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -735,8 +735,8 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined } /** Extract original feed */ -function getOriginalFeed(feedName: CombinedFeedKey): CompanyCardFeed { - const [feed] = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); +function getOriginalFeed(feedKey: CombinedFeedKey): CompanyCardFeed { + const [feed] = feedKey.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index b90b8c8547f2..0ea4ca6f5dc5 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -48,7 +48,7 @@ type WorkspaceCompanyCardDetailsPageProps = PlatformStackScreenProps { setIsUnassignModalVisible(false); if (card) { - unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, originalFeedName, card); + unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, originalFeed, card); } Navigation.goBack(); }; const updateCard = () => { - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, originalFeedName, card?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, originalFeed, card?.lastScrapeResult); }; const lastScrape = useMemo(() => { @@ -155,7 +155,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag @@ -163,7 +163,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag pendingAction={card?.nameValuePairs?.pendingFields?.cardTitle} errorRowStyles={[styles.ph5, styles.mb3]} errors={getLatestErrorField(card?.nameValuePairs ?? {}, 'cardTitle')} - onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeedName, 'cardTitle')} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeed, 'cardTitle')} > clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeedName, 'lastScrape', true)} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeed, 'lastScrape', true)} > ()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const originalFeedName = getOriginalFeed(selectedFeed); - const formattedFeedName = getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName); + const originalFeed = getOriginalFeed(selectedFeed); + const formattedFeedName = getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName); const isCommercialFeed = isCustomFeed(selectedFeed); const plaidUrl = getPlaidInstitutionIconUrl(selectedFeed); const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; - const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(originalFeedName); + const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(originalFeed); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeedData); const filteredFeedCards = filterInactiveCards(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]); const hasFeedError = !!cardFeeds?.[selectedFeed]?.errors; @@ -83,7 +83,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const openBankConnection = () => { const institutionId = !!getPlaidInstitutionId(selectedFeed); const data: Partial = { - bankName: originalFeedName, + bankName: originalFeed, }; if (institutionId) { const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); @@ -122,7 +122,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))} - cardIcon={getCardFeedIcon(originalFeedName, illustrations)} + cardIcon={getCardFeedIcon(originalFeed, illustrations)} shouldChangeLayout={shouldChangeLayout} feedName={formattedFeedName} supportingText={translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed')} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx index 2a1b66d9ea73..921e958b75ec 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx @@ -44,9 +44,9 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeedName = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeedName, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyFeeds[selectedFeed] : undefined); const validate = useCallback( @@ -69,8 +69,8 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ ); const submit = ({name}: WorkspaceCompanyCardFeedName) => { - if (originalFeedName) { - setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, originalFeedName, name); + if (originalFeed) { + setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, originalFeed, name); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index 19a070dfc77c..451de0124081 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -22,7 +22,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeedName = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -52,22 +52,22 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr return; } const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; - if (originalFeedName && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, originalFeedName, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + if (originalFeed && isChangedValue) { + setFeedStatementPeriodEndDay(policyID, originalFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } goBack(); }, - [policyID, statementPeriodEndDay, goBack, originalFeedName, domainOrWorkspaceAccountID], + [policyID, statementPeriodEndDay, goBack, originalFeed, domainOrWorkspaceAccountID], ); const clearError = useCallback(() => { - if (!originalFeedName) { + if (!originalFeed) { return; } - clearErrorField(originalFeedName, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); - }, [originalFeedName, domainOrWorkspaceAccountID]); + clearErrorField(originalFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + }, [originalFeed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { return ; From 79a5a894624ea70d8660e1d6653bb46e121c5fc0 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 10:50:59 +0100 Subject: [PATCH 10/23] Clean up pt2 --- .../WorkspaceCompanyCardStatementCloseDatePage.tsx | 14 +++++++------- .../WorkspaceCompanyCardsSettingsPage.tsx | 11 ++++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx index 780e976df892..84e904ad8cd8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx @@ -29,7 +29,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeed = getOriginalFeed(selectedFeed); + const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -51,13 +51,13 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const submit = useCallback( (newStatementPeriodEnd: StatementPeriodEnd | undefined, newStatementPeriodEndDay: StatementPeriodEndDay | undefined) => { const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; - if (selectedFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, getOriginalFeed(selectedFeed), domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + if (originalFeed && isChangedValue) { + setFeedStatementPeriodEndDay(policyID, originalFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }, - [policyID, selectedFeed, statementPeriodEndDay, domainOrWorkspaceAccountID], + [policyID, originalFeed, statementPeriodEndDay, domainOrWorkspaceAccountID], ); const goBack = useCallback(() => { @@ -65,12 +65,12 @@ function WorkspaceCompanyCardStatementCloseDatePage({ }, [policyID]); const clearError = useCallback(() => { - if (!selectedFeed) { + if (!originalFeed) { return; } - clearErrorField(getOriginalFeed(selectedFeed), domainOrWorkspaceAccountID, 'statementPeriodEndDay'); - }, [selectedFeed, domainOrWorkspaceAccountID]); + clearErrorField(originalFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + }, [originalFeed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { return ; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index f7e46554fe64..f1900c7f6be9 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -45,9 +45,10 @@ function WorkspaceCompanyCardsSettingsPage({ const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const selectedFeed = useMemo(() => getSelectedFeed(lastSelectedFeed, cardFeeds), [cardFeeds, lastSelectedFeed]); + const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(getOriginalFeed(selectedFeed), cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const liabilityType = selectedFeedData?.liabilityType; @@ -77,7 +78,7 @@ function WorkspaceCompanyCardsSettingsPage({ const deleteCompanyCardFeed = () => { setDeleteCompanyCardConfirmModalVisible(false); Navigation.goBack(); - if (selectedFeed) { + if (originalFeed) { const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]) @@ -85,19 +86,19 @@ function WorkspaceCompanyCardsSettingsPage({ .at(0); // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeed(selectedFeed), cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, originalFeed, cardIDs, feedToOpen); }); } }; const onToggleLiability = (isOn: boolean) => { - if (!selectedFeed) { + if (!originalFeed) { return; } setWorkspaceCompanyCardTransactionLiability( domainOrWorkspaceAccountID, policyID, - getOriginalFeed(selectedFeed), + originalFeed, isOn ? CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW : CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.RESTRICT, ); }; From 7401ba4ba76f1602247c75d5a47e1e7d06591010 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 11:41:00 +0100 Subject: [PATCH 11/23] Clean up pt3 --- src/hooks/useCardFeeds.tsx | 4 ++-- src/hooks/useIsBlockedToAddFeed.ts | 11 +++-------- .../companyCards/WorkspaceCompanyCardsPage.tsx | 13 +++++++------ .../members/WorkspaceMemberNewCardPage.tsx | 4 ++-- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx index b4f3f1024bb9..13effd7dfdc3 100644 --- a/src/hooks/useCardFeeds.tsx +++ b/src/hooks/useCardFeeds.tsx @@ -32,7 +32,7 @@ type CombinedCardFeeds = Record; * 2. The result metadata from the Onyx collection fetch. * 3. Card feeds specific to the given policyID (or `undefined` if unavailable). */ -const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata>, CardFeeds | undefined, boolean] => { +const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata>, CardFeeds | undefined] => { const workspaceAccountID = useWorkspaceAccountID(policyID); const [allFeeds, allFeedsResult] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true}); const defaultFeed = allFeeds?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]; @@ -74,7 +74,7 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi }, result); }, [allFeeds, policyID]); - return [workspaceFeeds, allFeedsResult, defaultFeed, !!defaultFeed?.isLoading]; + return [workspaceFeeds, allFeedsResult, defaultFeed]; }; export default useCardFeeds; diff --git a/src/hooks/useIsBlockedToAddFeed.ts b/src/hooks/useIsBlockedToAddFeed.ts index bc1b28a5ed22..672ecab92f11 100644 --- a/src/hooks/useIsBlockedToAddFeed.ts +++ b/src/hooks/useIsBlockedToAddFeed.ts @@ -1,28 +1,23 @@ import {useEffect, useState} from 'react'; -import {checkIfNewFeedConnected, getCompanyFeeds, getSelectedFeed} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getCompanyFeeds} from '@libs/CardUtils'; import {isCollectPolicy} from '@libs/PolicyUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import useCardFeeds from './useCardFeeds'; -import useCardsList from './useCardsList'; import useOnyx from './useOnyx'; import usePolicy from './usePolicy'; function useIsBlockedToAddFeed(policyID?: string) { const policy = usePolicy(policyID); - const [cardFeeds, allFeedsResult, defaultFeed, isFeedsLoading] = useCardFeeds(policyID); + const [cardFeeds, allFeedsResult, defaultFeed] = useCardFeeds(policyID); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); - const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const companyFeeds = getCompanyFeeds(cardFeeds, true); const isCollect = isCollectPolicy(policy); const isAllFeedsResultLoading = isLoadingOnyxValue(allFeedsResult); - const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const [cardsList] = useCardsList(selectedFeed); const [prevCardFeeds, setPrevCardFeeds] = useState(cardFeeds); const [isNewFeedConnected, setIsNewFeedConnected] = useState(false); - const isLoading = !cardFeeds || (isFeedsLoading && isEmptyObject(cardsList)) || !!defaultFeed?.isLoading; + const isLoading = !cardFeeds || !!defaultFeed?.isLoading; useEffect(() => { const plaidConnectedFeed = addNewCard?.data?.plaidConnectedFeed; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index fece5afa380f..7791a2c55265 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -56,8 +56,9 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [workspaceCardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: true}); - const [cardFeeds, , , isCardFeedsLoading] = useCardFeeds(policyID); + const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); + const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); @@ -84,7 +85,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { }, [policyID, domainOrWorkspaceAccountID]); const {isOffline} = useNetwork({onReconnect: fetchCompanyCards}); - const isLoading = !isOffline && (!cardFeeds || (isCardFeedsLoading && isEmptyObject(cardsList))); + const isLoading = !isOffline && (!cardFeeds || (!!defaultFeed?.isLoading && isEmptyObject(cardsList))); const isGB = countryByIp === CONST.COUNTRY.GB; const shouldShowGBDisclaimer = isGB && isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && (isNoFeed || hasNoAssignedCard); @@ -93,12 +94,12 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { }, [fetchCompanyCards]); useEffect(() => { - if (isLoading || !selectedFeed || isPending) { + if (isLoading || !originalFeed || isPending) { return; } - openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, getOriginalFeed(selectedFeed)); - }, [selectedFeed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); + openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, originalFeed); + }, [originalFeed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); const handleAssignCard = () => { if (isActingAsDelegate) { @@ -119,7 +120,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { } const data: Partial = { - bankName: getOriginalFeed(selectedFeed), + bankName: originalFeed, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 783f90615098..a1c524c1577d 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -66,7 +66,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const styles = useThemeStyles(); const lazyIllustrations = useMemoizedLazyIllustrations(['ExpensifyCardImage'] as const); const illustrations = useThemeIllustrations(); - const [cardFeeds, , , isLoading] = useCardFeeds(policyID); + const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); const [selectedFeed, setSelectedFeed] = useState(''); const [shouldShowError, setShouldShowError] = useState(false); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, {canBeMissing: true}); @@ -215,7 +215,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew onSubmit={handleSubmit} message={translate('common.error.pleaseSelectOne')} buttonText={translate('common.next')} - isLoading={isLoading} + isLoading={!!defaultFeed?.isLoading} /> From 71f5e2879e9e253db92a1242d5deeb6b3485515c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 12:47:31 +0100 Subject: [PATCH 12/23] Minor improvements --- src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index a1c524c1577d..298fa240aff6 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -76,11 +76,12 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const memberLogin = personalDetails?.[accountID]?.login ?? ''; const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; const companyFeeds = getCompanyFeeds(cardFeeds, false, true); - const isFeedExpired = isSelectedFeedExpired(cardFeeds?.[selectedFeed as CombinedFeedKey]); - const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[selectedFeed as CombinedFeedKey]); + const currentFeed = selectedFeed ? cardFeeds?.[selectedFeed as CombinedFeedKey] : undefined; + const isFeedExpired = isSelectedFeedExpired(currentFeed); + const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeed); const [list] = useCardsList(selectedFeed as CombinedFeedKey); - const filteredCardList = getFilteredCardList(list, cardFeeds?.[selectedFeed as CombinedFeedKey]?.accountList, workspaceCardFeeds); + const filteredCardList = getFilteredCardList(list, currentFeed?.accountList, workspaceCardFeeds); const shouldShowExpensifyCard = isExpensifyCardFullySetUp(policy, cardSettings); From 9d0fb3fe83155294a015a8a44ddc9b67b9da9782 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 6 Nov 2025 12:50:09 +0100 Subject: [PATCH 13/23] Minor improvements --- src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 298fa240aff6..aa36606c2e9b 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -144,10 +144,10 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew return { value: key, feed: value.feed, - text: getCustomOrFormattedFeedName(value.feed, cardFeeds?.[key]?.customFeedName), + text: getCustomOrFormattedFeedName(value.feed, value.customFeedName), keyForList: key, - isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - pendingAction: companyFeeds[key]?.pendingAction, + isDisabled: value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: value.pendingAction, isSelected: selectedFeed === key, leftElement: plaidUrl ? ( From cfb417b2d76b92857521a23aeaf769dd1f032a55 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 11:15:41 +0100 Subject: [PATCH 14/23] Remove extra file --- src/hooks/useIsBlockedToAddFeed.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/hooks/useIsBlockedToAddFeed.ts diff --git a/src/hooks/useIsBlockedToAddFeed.ts b/src/hooks/useIsBlockedToAddFeed.ts deleted file mode 100644 index e69de29bb2d1..000000000000 From 0420ba779fde2d36c151588454c76b97bb9cef82 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 11:52:22 +0100 Subject: [PATCH 15/23] Add getOriginalFeed, getCombinedFeedKey, getOriginalCompanyFeeds unit tests --- tests/unit/CardUtilsTest.ts | 92 +++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 08e3257e503c..ffa09804b59d 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -17,12 +17,14 @@ import { getCardDescription, getCardFeedIcon, getCardsByCardholderName, + getCombinedFeedKey, getCompanyCardDescription, - getCompanyFeeds, getCustomOrFormattedFeedName, getFeedType, getFilteredCardList, getMonthFromExpirationDateString, + getOriginalCompanyFeeds, + getOriginalFeed, getSelectedFeed, getYearFromExpirationDateString, hasIssuedExpensifyCard, @@ -33,7 +35,7 @@ import { maskCardNumber, sortCardsByCardholderName, } from '@src/libs/CardUtils'; -import type {Card, CardList, CombinedFeedKey, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CardFeeds, CardList, CombinedFeedKey, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; @@ -60,7 +62,7 @@ const directFeedBanks = [ const companyCardsCustomFeedSettings = { [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { - pending: true, + pending: false, }, [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { liabilityType: 'personal', @@ -86,6 +88,36 @@ const companyCardsCustomVisaFeedSettingsWithNumbers = { }, }; +const companyCardsDirectFeedSettings = { + [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: { + liabilityType: 'personal', + }, + [CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]: { + liabilityType: 'personal', + }, +}; +const companyCardsSettingsWithoutExpensifyBank = { + [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { + pending: false, + }, + [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { + liabilityType: 'personal', + }, + ...companyCardsDirectFeedSettings, +}; + +const companyCardsSettingsWithPendingRemovedFeeds = { + [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { + pending: true, + }, + [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: { + pending: false, + }, + [CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, +}; + const oAuthAccountDetails = { [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: { accountList: ['CREDIT CARD...6607', 'CREDIT CARD...5501'], @@ -241,6 +273,25 @@ const cardSettingsWithoutPaymentBankAccountID = { paymentBankAccountID: undefined, } as unknown as ExpensifyCardSettings; +const cardFeedsCollection: OnyxCollection = { + // Policy with both custom and direct feeds + FAKE_ID_1: { + settings: { + companyCardNicknames: { + [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: customFeedName, + }, + companyCards: {...companyCardsCustomFeedSettings, ...companyCardsDirectFeedSettings}, + oAuthAccountDetails, + }, + }, + // Policy with pending and removed feeds + FAKE_ID_2: { + settings: { + companyCards: companyCardsSettingsWithPendingRemovedFeeds, + }, + }, +}; + /* eslint-disable @typescript-eslint/naming-convention */ const allCardsList = { 'cards_11111111_oauth.capitalone.com': directFeedCardsMultipleList, @@ -375,21 +426,19 @@ describe('CardUtils', () => { }); }); - describe('getCompanyFeeds', () => { - it('Should return feeds with filtered out "Expensify Card" bank', () => { - // TBD + describe('getOriginalCompanyFeeds', () => { + it('Should return both custom and direct feeds with filtered out "Expensify Card" bank', () => { + const companyFeeds = getOriginalCompanyFeeds(cardFeedsCollection.FAKE_ID_1); + expect(companyFeeds).toStrictEqual(companyCardsSettingsWithoutExpensifyBank); }); - it('Should return only feeds that are not pending if shouldFilterOutPendingFeeds is true', () => { - // TBD - }); - - it('Should return only feeds that are not removed if shouldFilterOutRemovedFeeds is true', () => { - // TBD + it('Should return only feeds that are not pending/removed', () => { + const companyFeeds = getOriginalCompanyFeeds(cardFeedsCollection.FAKE_ID_2); + expect(Object.keys(companyFeeds).length).toStrictEqual(1); }); it('Should return empty object if undefined is passed', () => { - const companyFeeds = getCompanyFeeds(undefined); + const companyFeeds = getOriginalCompanyFeeds(undefined); expect(companyFeeds).toStrictEqual({}); }); }); @@ -1190,4 +1239,21 @@ describe('CardUtils', () => { expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]); }); }); + + describe('getOriginalFeed', () => { + it('should extract the original feed from a combined feed key', () => { + const combinedKey: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`; + const originalFeed = getOriginalFeed(combinedKey); + expect(originalFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); + }); + }); + + describe('getCombinedFeedKey', () => { + it('should combine feed name domain ID', () => { + const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; + const domainID = 11111111; + const combinedKey = getCombinedFeedKey(feedName, domainID); + expect(combinedKey).toBe(`${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`); + }); + }); }); From 38e8d6f44bee6a3211cebedbabb3fe6cee874a88 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 14:31:48 +0100 Subject: [PATCH 16/23] Add getCompanyFeeds unit tests --- tests/unit/CardUtilsTest.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index ffa09804b59d..ea9fa8cf71ab 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -19,6 +19,7 @@ import { getCardsByCardholderName, getCombinedFeedKey, getCompanyCardDescription, + getCompanyFeeds, getCustomOrFormattedFeedName, getFeedType, getFilteredCardList, @@ -244,6 +245,7 @@ const combinedCardFeeds: CombinedCardFeeds = { credentials: 'xxxxx', expiration: 1730998958, pending: false, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, feed: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, }, [`${CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE}#11111111`]: { @@ -257,6 +259,15 @@ const combinedCardFeeds: CombinedCardFeeds = { }, }; +const combinedCardFeedsWithExpensifyCard: CombinedCardFeeds = { + ...combinedCardFeeds, + [`${CONST.EXPENSIFY_CARD.BANK}#11111111`]: { + domainID: 11111111, + pending: false, + feed: CONST.EXPENSIFY_CARD.BANK, + }, +}; + const policyWithCardsEnabled = { areExpensifyCardsEnabled: true, } as unknown as Policy; @@ -443,6 +454,31 @@ describe('CardUtils', () => { }); }); + describe('getCompanyFeeds', () => { + it('Should filter out Expensify Card bank by default', () => { + const companyFeeds = getCompanyFeeds(combinedCardFeedsWithExpensifyCard); + const feedKeys = Object.keys(companyFeeds); + expect(feedKeys).not.toContain(`${CONST.EXPENSIFY_CARD.BANK}#11111111`); + }); + + it('Should filter out pending feeds when shouldFilterOutPendingFeeds is true', () => { + const companyFeeds = getCompanyFeeds(combinedCardFeeds, false, true); + const feedKeys = Object.keys(companyFeeds); + expect(feedKeys).not.toContain(`${CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD}#11111111`); + }); + + it('Should filter out removed feeds when shouldFilterOutRemovedFeeds is true', () => { + const companyFeeds = getCompanyFeeds(combinedCardFeeds, true, false); + const feedKeys = Object.keys(companyFeeds); + expect(feedKeys).not.toContain(`${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`); + }); + + it('Should return empty object if undefined is passed', () => { + const companyFeeds = getCompanyFeeds(undefined); + expect(companyFeeds).toStrictEqual({}); + }); + }); + describe('getSelectedFeed', () => { it('Should return last selected custom feed', () => { const lastSelectedCustomFeed: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#12345`; From 179b0328c47809ac84768ffd06451074e0653536 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 15:06:29 +0100 Subject: [PATCH 17/23] Fix card details overview --- .../companyCards/WorkspaceCompanyCardsList.tsx | 8 ++++---- .../members/WorkspaceMemberDetailsPage.tsx | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 978adbf95316..174ebf039293 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -11,13 +11,13 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; -import {filterCardsByPersonalDetails, getCardsByCardholderName, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils'; +import {filterCardsByPersonalDetails, getCardsByCardholderName, getCombinedFeedKey, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils'; import {getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Card, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CompanyCardFeed, WorkspaceCardsList} from '@src/types/onyx'; import WorkspaceCompanyCardsFeedAddedEmptyPage from './WorkspaceCompanyCardsFeedAddedEmptyPage'; import WorkspaceCompanyCardsListRow from './WorkspaceCompanyCardsListRow'; @@ -72,10 +72,10 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis hoverStyle={styles.hoveredComponentBG} disabled={isCardDeleted} onPress={() => { - if (!cardID || !item?.accountID) { + if (!cardID || !item?.accountID || !item.fundID) { return; } - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, item.bank)); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, getCombinedFeedKey(item.bank as CompanyCardFeed, item.fundID))); }} > {({hovered}) => ( diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index d8abb8371cfe..64e3bbe57d3a 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -27,7 +27,16 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy'; import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow'; -import {getAllCardsForWorkspace, getCardFeedIcon, getCompanyFeeds, getPlaidInstitutionIconUrl, isExpensifyCardFullySetUp, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import { + getAllCardsForWorkspace, + getCardFeedIcon, + getCombinedFeedKey, + getCompanyFeeds, + getPlaidInstitutionIconUrl, + isExpensifyCardFullySetUp, + lastFourNumbersFromCardName, + maskCardNumber, +} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -276,7 +285,12 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_DETAILS.getRoute(policyID, card.cardID.toString(), Navigation.getActiveRoute())); return; } - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, card.cardID.toString(), card.bank, Navigation.getActiveRoute())); + if (!card.fundID) { + return; + } + Navigation.navigate( + ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, card.cardID.toString(), getCombinedFeedKey(card.bank as CompanyCardFeed, card.fundID), Navigation.getActiveRoute()), + ); }, [policyID], ); From 4f450796b59289956e40d6b8c3766251fa134adf Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 15:36:06 +0100 Subject: [PATCH 18/23] Add domain name display --- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 5 +++++ .../WorkspaceCompanyCardsListHeaderButtons.tsx | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index e823d5576184..f5e88edd9596 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -1,3 +1,4 @@ +import {Str} from 'expensify-common'; import React from 'react'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; @@ -55,6 +56,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const {translate} = useLocalize(); + const [allDomains] = useOnyx(ONYXKEYS.COLLECTION.DOMAIN, {canBeMissing: false}); const styles = useThemeStyles(); const illustrations = useThemeIllustrations(); const [cardFeeds] = useCardFeeds(policyID); @@ -70,10 +72,13 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS ); const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards); const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed); + const domain = allDomains?.[`${ONYXKEYS.COLLECTION.DOMAIN}${feedSettings.domainID}`]; + const domainName = domain?.email ? Str.extractEmailDomain(domain.email) : undefined; return { value: key, feed: feedSettings.feed, + alternateText: domainName ?? policy?.name, text: getCustomOrFormattedFeedName(feedSettings.feed, feedSettings.customFeedName), keyForList: key, isSelected: key === selectedFeed, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index b44644e09965..675063b09bb1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -1,3 +1,4 @@ +import {Str} from 'expensify-common'; import React, {useMemo} from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; @@ -79,6 +80,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const filteredFeedCards = filterInactiveCards(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]); const hasFeedError = !!cardFeeds?.[selectedFeed]?.errors; const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError; + const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`); const openBankConnection = () => { const institutionId = !!getPlaidInstitutionId(selectedFeed); @@ -116,6 +118,13 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS [policyID, translate], ); + const supportingText = useMemo(() => { + const firstPart = translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed'); + const domainName = domain?.email ? Str.extractEmailDomain(domain.email) : undefined; + const secondPart = ` (${domainName ?? policy?.name})`; + return `${firstPart}${secondPart}`; + }, [domain?.email, isCommercialFeed, policy?.name, translate]); + return ( @@ -125,7 +134,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS cardIcon={getCardFeedIcon(originalFeed, illustrations)} shouldChangeLayout={shouldChangeLayout} feedName={formattedFeedName} - supportingText={translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed')} + supportingText={supportingText} shouldShowRBR={checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, domainOrWorkspaceAccountID), selectedFeed)} /> From 283c69f68c271a9e54af92be51566ce31af75625 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 7 Nov 2025 15:42:19 +0100 Subject: [PATCH 19/23] Lint fix --- .../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 675063b09bb1..4f78609fe24c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -80,7 +80,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const filteredFeedCards = filterInactiveCards(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]); const hasFeedError = !!cardFeeds?.[selectedFeed]?.errors; const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError; - const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`); + const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`, {canBeMissing: true}); const openBankConnection = () => { const institutionId = !!getPlaidInstitutionId(selectedFeed); From 39aae2bff1777a93e1b3e7c6e3b8d4e21f56d0ed Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 13 Nov 2025 09:28:31 +0100 Subject: [PATCH 20/23] Fix TS issues and tests --- src/hooks/useIsAllowedToIssueCompanyCard.ts | 6 +- tests/unit/CardUtilsTest.ts | 4 +- .../useIsAllowedToIssueCompanyCard.test.ts | 124 +++++------------- 3 files changed, 37 insertions(+), 97 deletions(-) diff --git a/src/hooks/useIsAllowedToIssueCompanyCard.ts b/src/hooks/useIsAllowedToIssueCompanyCard.ts index 148dc0433065..050506c12151 100644 --- a/src/hooks/useIsAllowedToIssueCompanyCard.ts +++ b/src/hooks/useIsAllowedToIssueCompanyCard.ts @@ -17,7 +17,11 @@ function useIsAllowedToIssueCompanyCard({policyID}: {policyID?: string}) { const selectedFeedData = selectedFeed && companyCards[selectedFeed]; const [adminAccess] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_ADMIN_ACCESS}${selectedFeedData?.domainID}`, {canBeMissing: true}); - return selectedFeedData?.domainID ? !!adminAccess : isPolicyAdmin; + if (selectedFeedData?.domainID === policy?.workspaceAccountID) { + return isPolicyAdmin; + } + + return !!adminAccess; } export default useIsAllowedToIssueCompanyCard; diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 90af551a9614..9b4fc5b5ed6c 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -540,8 +540,8 @@ describe('CardUtils', () => { }); it('Should return feed key name for unknown feed', () => { - const companyCardNicknames = cardFeedsCollection.FAKE_ID_7?.settings?.companyCardNicknames; - const feedName = getCustomOrFormattedFeedName(unknownFeed, companyCardNicknames); + const companyCardNickname = cardFeedsCollection.FAKE_ID_7?.settings?.companyCardNicknames?.[unknownFeed]; + const feedName = getCustomOrFormattedFeedName(unknownFeed, companyCardNickname); expect(feedName).toBe(unknownFeed); }); }); diff --git a/tests/unit/hooks/useIsAllowedToIssueCompanyCard.test.ts b/tests/unit/hooks/useIsAllowedToIssueCompanyCard.test.ts index b30901cc8754..e8d46bd98cd2 100644 --- a/tests/unit/hooks/useIsAllowedToIssueCompanyCard.test.ts +++ b/tests/unit/hooks/useIsAllowedToIssueCompanyCard.test.ts @@ -6,9 +6,30 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import createRandomPolicy from '../../utils/collections/policies'; +const domainID = 19475968; const mockPolicyID = '123456'; +const workspaceAccountID = 11111111; -const mockPolicy = {...createRandomPolicy(Number(mockPolicyID), CONST.POLICY.TYPE.TEAM, 'TestPolicy'), policyID: mockPolicyID}; +const mockPolicy = {...createRandomPolicy(Number(mockPolicyID), CONST.POLICY.TYPE.TEAM, 'TestPolicy'), policyID: mockPolicyID, workspaceAccountID}; + +const mockedFeeds = { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'vcf#19475968': { + liabilityType: 'personal', + pending: false, + domainID, + customFeedName: 'Custom feed name', + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'vcf#11111111': { + liabilityType: 'personal', + pending: false, + domainID: workspaceAccountID, + customFeedName: 'Custom feed name 1', + feed: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA, + }, +}; jest.mock('@hooks/useCardFeeds', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention @@ -18,122 +39,37 @@ jest.mock('@hooks/useCardFeeds', () => ({ describe('useIsAllowedToIssueCompanyCard', () => { beforeEach(async () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${mockPolicy?.policyID}`, mockPolicy); - await Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${mockPolicy?.policyID}`, 'vcf'); }); it('should return true if domain feed and access is granted', async () => { - const domainID = 19475968; + await Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${mockPolicy?.policyID}`, 'vcf#19475968'); await Onyx.merge(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_ADMIN_ACCESS}${domainID}`, true); - (useCardFeeds as jest.Mock).mockReturnValue([ - { - settings: { - companyCards: { - vcf: { - asrEnabled: false, - country: 'US', - domainID, - forceReimbursable: 'force_no', - liabilityType: 'corporate', - preferredPolicy: '135CA2196CD21C88', - reportTitleFormat: '', - shouldApplyCashbackToBill: true, - statementPeriodEndDay: 'LAST_DAY_OF_MONTH', - uploadLayoutSettings: [], - }, - }, - companyCardNicknames: {}, - oAuthAccountDetails: {}, - }, - }, - {status: 'loaded'}, - ]); + (useCardFeeds as jest.Mock).mockReturnValue([mockedFeeds, {status: 'loaded'}]); const {result} = renderHook(() => useIsAllowedToIssueCompanyCard({policyID: mockPolicyID})); expect(result?.current).toBe(true); }); it('should return false if domain feed and access is not granted', async () => { - const domainID = 19475968; + await Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${mockPolicy?.policyID}`, 'vcf#19475968'); await Onyx.merge(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_ADMIN_ACCESS}${domainID}`, false); - (useCardFeeds as jest.Mock).mockReturnValue([ - { - settings: { - companyCards: { - vcf: { - asrEnabled: false, - country: 'US', - domainID, - forceReimbursable: 'force_no', - liabilityType: 'corporate', - preferredPolicy: '135CA2196CD21C88', - reportTitleFormat: '', - shouldApplyCashbackToBill: true, - statementPeriodEndDay: 'LAST_DAY_OF_MONTH', - uploadLayoutSettings: [], - }, - }, - companyCardNicknames: {}, - oAuthAccountDetails: {}, - }, - }, - {status: 'loaded'}, - ]); + (useCardFeeds as jest.Mock).mockReturnValue([mockedFeeds, {status: 'loaded'}]); const {result} = renderHook(() => useIsAllowedToIssueCompanyCard({policyID: mockPolicyID})); expect(result?.current).toBe(false); }); it('should return true if workspace feed and user is admin', async () => { + await Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${mockPolicy?.policyID}`, 'vcf#11111111'); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${mockPolicy?.policyID}`, { role: CONST.POLICY.ROLE.ADMIN, }); - (useCardFeeds as jest.Mock).mockReturnValue([ - { - settings: { - companyCards: { - vcf: { - asrEnabled: false, - country: 'US', - forceReimbursable: 'force_no', - liabilityType: 'corporate', - preferredPolicy: '135CA2196CD21C88', - reportTitleFormat: '', - shouldApplyCashbackToBill: true, - statementPeriodEndDay: 'LAST_DAY_OF_MONTH', - uploadLayoutSettings: [], - }, - }, - companyCardNicknames: {}, - oAuthAccountDetails: {}, - }, - }, - {status: 'loaded'}, - ]); + (useCardFeeds as jest.Mock).mockReturnValue([mockedFeeds, {status: 'loaded'}]); const {result} = renderHook(() => useIsAllowedToIssueCompanyCard({policyID: mockPolicyID})); expect(result?.current).toBe(true); }); it('should return false if workspace feed and user is not an admin', async () => { + await Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${mockPolicy?.policyID}`, 'vcf#11111111'); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${mockPolicy?.policyID}`, { role: CONST.POLICY.ROLE.USER, }); - (useCardFeeds as jest.Mock).mockReturnValue([ - { - settings: { - companyCards: { - vcf: { - asrEnabled: false, - country: 'US', - forceReimbursable: 'force_no', - liabilityType: 'corporate', - preferredPolicy: '135CA2196CD21C88', - reportTitleFormat: '', - shouldApplyCashbackToBill: true, - statementPeriodEndDay: 'LAST_DAY_OF_MONTH', - uploadLayoutSettings: [], - }, - }, - companyCardNicknames: {}, - oAuthAccountDetails: {}, - }, - }, - {status: 'loaded'}, - ]); + (useCardFeeds as jest.Mock).mockReturnValue([mockedFeeds, {status: 'loaded'}]); const {result} = renderHook(() => useIsAllowedToIssueCompanyCard({policyID: mockPolicyID})); expect(result?.current).toBe(false); }); From 0c50c62b701301175b1cd8f151a76bfd393edd89 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 14 Nov 2025 09:45:50 +0100 Subject: [PATCH 21/23] Rename CombinedFeedKey type to CompanyCardFeedWithDomainID --- src/ONYXKEYS.ts | 2 +- src/hooks/useCardFeeds.tsx | 10 +++++----- src/hooks/useCardsList.tsx | 4 ++-- src/hooks/useUpdateFeedBrokenConnection.ts | 4 ++-- src/libs/CardUtils.ts | 18 +++++++++--------- src/libs/actions/Card.ts | 4 ++-- src/libs/actions/CompanyCards.ts | 6 +++--- .../BankConnection/index.native.tsx | 4 ++-- .../companyCards/BankConnection/index.tsx | 4 ++-- ...rkspaceCompanyCardAccountSelectCardPage.tsx | 4 ++-- .../WorkspaceCompanyCardDetailsPage.tsx | 4 ++-- .../WorkspaceCompanyCardEditCardNamePage.tsx | 4 ++-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 6 +++--- .../WorkspaceCompanyCardsErrorConfirmation.tsx | 6 +++--- .../companyCards/WorkspaceCompanyCardsList.tsx | 4 ++-- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 4 ++-- .../WorkspaceCompanyCardsSettingsPage.tsx | 4 ++-- .../addNew/CardInstructionsStep.tsx | 4 ++-- .../addNew/PlaidConnectionStep.tsx | 4 ++-- .../assignCard/AssignCardFeedPage.tsx | 4 ++-- .../companyCards/assignCard/AssigneeStep.tsx | 2 +- .../assignCard/CardSelectionStep.tsx | 4 ++-- .../assignCard/ConfirmationStep.tsx | 4 ++-- .../members/WorkspaceMemberDetailsPage.tsx | 9 +++++++-- .../members/WorkspaceMemberNewCardPage.tsx | 14 +++++++------- src/types/onyx/CardFeeds.ts | 8 ++++---- src/types/onyx/index.ts | 4 ++-- tests/unit/CardUtilsTest.ts | 14 +++++++------- 28 files changed, 84 insertions(+), 79 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 47bb6037eae5..22af17fee262 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -1090,7 +1090,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; - [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CombinedFeedKey; + [ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CompanyCardFeedWithDomainID; [ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED]: OnyxTypes.FundID; [ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST]: OnyxTypes.CardOnWaitlist; [ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD]: OnyxTypes.IssueNewCard; diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx index 13effd7dfdc3..46282e29dae8 100644 --- a/src/hooks/useCardFeeds.tsx +++ b/src/hooks/useCardFeeds.tsx @@ -1,8 +1,8 @@ import {useMemo} from 'react'; import type {OnyxCollection, ResultMetadata} from 'react-native-onyx'; -import {getCombinedFeedKey} from '@libs/CardUtils'; +import {getCompanyCardFeedWithDomainID} from '@libs/CardUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CardFeeds, CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; +import type {CardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import type {CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds'; import useOnyx from './useOnyx'; import useWorkspaceAccountID from './useWorkspaceAccountID'; @@ -16,7 +16,7 @@ type CombinedCardFeed = CustomCardFeedData & feed: CompanyCardFeed; }; -type CombinedCardFeeds = Record; +type CombinedCardFeeds = Record; /** * This is a custom hook that combines workspace and domain card feeds for a given policy. @@ -59,7 +59,7 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi return; } - const combinedFeedKey = getCombinedFeedKey(feedName, domainID); + const combinedFeedKey = getCompanyCardFeedWithDomainID(feedName, domainID); acc[combinedFeedKey] = { ...feedSettings, @@ -78,4 +78,4 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi }; export default useCardFeeds; -export type {CombinedCardFeeds, CombinedFeedKey, CombinedCardFeed}; +export type {CombinedCardFeeds, CompanyCardFeedWithDomainID, CombinedCardFeed}; diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx index de804b76c1be..934d1cb63b6d 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, CombinedFeedKey} from '@src/types/onyx'; +import type {CardList, CompanyCardFeedWithDomainID} 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: CombinedFeedKey | undefined): [CardList | undefined, ResultMetadata] => { +const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [CardList | 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/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index 2f685a1176e5..1cebdb4b84c1 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -2,12 +2,12 @@ import {useCallback} from 'react'; import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeed} from '@libs/CardUtils'; import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import useCardFeeds from './useCardFeeds'; import useCardsList from './useCardsList'; import usePolicy from './usePolicy'; -export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CombinedFeedKey}) { +export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CompanyCardFeedWithDomainID}) { const [cardsList] = useCardsList(feed); const policy = usePolicy(policyID); const [cardFeeds] = useCardFeeds(policyID); diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 49591aebc0a6..32ec276418a4 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -12,7 +12,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, CurrencyList, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {FilteredCardList} from '@src/types/onyx/Card'; -import type {CardFeedData, CombinedFeedKey, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds'; +import type {CardFeedData, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -524,15 +524,15 @@ function getCorrectStepForPlaidSelectedBank(selectedBank: ValueOf, cardFeeds: OnyxEntry): CombinedFeedKey | undefined { - const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CombinedFeedKey | undefined; +function getSelectedFeed(lastSelectedFeed: OnyxEntry, cardFeeds: OnyxEntry): CompanyCardFeedWithDomainID | undefined { + const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CompanyCardFeedWithDomainID | undefined; if (!lastSelectedFeed?.includes(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR)) { return defaultFeed; } return lastSelectedFeed; } -function getCombinedFeedKey(feedName: CompanyCardFeed, domainID: number | string): CombinedFeedKey { +function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeed, domainID: number | string): CompanyCardFeedWithDomainID { return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`; } @@ -585,7 +585,7 @@ function checkIfNewFeedConnected(prevFeedsData: CompanyFeeds, currentFeedsData: return { isNewFeedConnected: currentFeeds.length > prevFeeds.length || (plaidBank && currentFeeds.includes(`${CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID}.${plaidBank}`)), - newFeed: currentFeeds.find((feed) => !prevFeeds.includes(feed)) as CombinedFeedKey | undefined, + newFeed: currentFeeds.find((feed) => !prevFeeds.includes(feed)) as CompanyCardFeedWithDomainID | undefined, }; } @@ -628,7 +628,7 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry feed === feedKey)) { const filteredFeeds = Object.keys(cardFeeds ?? {}) .filter((str) => str.includes(feedKey)) - .map((str) => getOriginalFeed(str as CombinedFeedKey)); + .map((str) => getOriginalFeed(str as CompanyCardFeedWithDomainID)); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); @@ -735,8 +735,8 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined return Object.values(feedCards).find((card) => !isEmptyObject(card) && card.bank !== feedToExclude && card.lastScrapeResult !== 200); } -/** Extract original feed */ -function getOriginalFeed(feedKey: CombinedFeedKey): CompanyCardFeed { +/** Extract feed from feed with domainID */ +function getOriginalFeed(feedKey: CompanyCardFeedWithDomainID): CompanyCardFeed { const [feed] = feedKey.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } @@ -796,6 +796,6 @@ export { getCorrectStepForPlaidSelectedBank, getOriginalCompanyFeeds, getOriginalFeed, - getCombinedFeedKey, + getCompanyCardFeedWithDomainID, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index c6ad1935fc8d..bde8d1f86abc 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -22,7 +22,7 @@ import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PolicyUtils from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Card, CombinedFeedKey} from '@src/types/onyx'; +import type {Card, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import type {CardLimitType, ExpensifyCardDetails, IssueNewCardData, IssueNewCardStep} from '@src/types/onyx/Card'; import type {ConnectionName} from '@src/types/onyx/Policy'; @@ -945,7 +945,7 @@ function toggleContinuousReconciliation(workspaceAccountID: number, shouldUseCon }); } -function updateSelectedFeed(feed: CombinedFeedKey, policyID: string | undefined) { +function updateSelectedFeed(feed: CompanyCardFeedWithDomainID, policyID: string | undefined) { if (!policyID) { return; } diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index cdc3258408d1..3d5c9d1c2c25 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -28,8 +28,8 @@ import type { AddNewCardFeedStep, CardFeedData, CardFeedDetails, - CombinedFeedKey, CompanyCardFeed, + CompanyCardFeedWithDomainID, StatementPeriodEnd, StatementPeriodEndDay, } from '@src/types/onyx/CardFeeds'; @@ -76,7 +76,7 @@ function addNewCompanyCardsFeed( cardFeeds: OnyxEntry, statementPeriodEnd: StatementPeriodEnd | undefined, statementPeriodEndDay: StatementPeriodEndDay | undefined, - lastSelectedFeed?: CombinedFeedKey, + lastSelectedFeed?: CompanyCardFeedWithDomainID, ) { const authToken = NetworkStore.getAuthToken(); const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); @@ -220,7 +220,7 @@ function setWorkspaceCompanyCardTransactionLiability(domainOrWorkspaceAccountID: API.write(WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY, parameters, onyxData); } -function deleteWorkspaceCompanyCardFeed(policyID: string, domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, cardIDs: string[], feedToOpen?: CombinedFeedKey) { +function deleteWorkspaceCompanyCardFeed(policyID: string, domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, cardIDs: string[], feedToOpen?: CompanyCardFeedWithDomainID) { const authToken = NetworkStore.getAuthToken(); const isCustomFeed = CardUtils.isCustomFeed(bankName); const optimisticFeedUpdates = {[bankName]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}}; diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index 628da4f80784..b445f023938a 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -28,14 +28,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; type BankConnectionProps = { /** ID of the policy */ policyID?: string; /** Selected feed for assign card flow */ - feed?: CombinedFeedKey; + feed?: CompanyCardFeedWithDomainID; /** Route params for add new card flow */ route?: PlatformStackRouteProp; diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index 1bbeef7efc9f..f7b8bdf5a049 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -29,7 +29,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import openBankConnection from './openBankConnection'; let customWindow: Window | null = null; @@ -39,7 +39,7 @@ type BankConnectionProps = { policyID?: string; /** Selected feed for assign card flow */ - feed?: CombinedFeedKey; + feed?: CompanyCardFeedWithDomainID; /** Route params for add new card flow */ route?: PlatformStackRouteProp; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index 218989e2e35f..4228fb7fd50c 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -24,7 +24,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import {getExportMenuItem} from './utils'; type WorkspaceCompanyCardAccountSelectCardProps = PlatformStackScreenProps; @@ -34,7 +34,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard const styles = useThemeStyles(); const {environmentURL} = useEnvironment(); const {policyID, cardID, backTo} = route.params; - const bank = decodeURIComponent(route.params.bank) as CombinedFeedKey; + const bank = decodeURIComponent(route.params.bank) as CompanyCardFeedWithDomainID; const policy = usePolicy(policyID); const workspaceAccountID = useWorkspaceAccountID(policyID); const [searchText, setSearchText] = useState(''); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 0ea4ca6f5dc5..d02efe64ec70 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -39,7 +39,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; +import type {CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import {getExportMenuItem} from './utils'; @@ -47,7 +47,7 @@ type WorkspaceCompanyCardDetailsPageProps = PlatformStackScreenProps; @@ -33,7 +33,7 @@ type WorkspaceCompanyCardEditCardNamePageProps = PlatformStackScreenProps).map(([key, feedSettings]) => { + const feeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([key, feedSettings]) => { const filteredFeedCards = filterInactiveCards( allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${getDomainOrWorkspaceAccountID(workspaceAccountID, feedSettings)}_${feedSettings.feed}`], ); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index f8f7c0d552f7..91ab5a7f57f5 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -14,11 +14,11 @@ import {deleteWorkspaceCompanyCardFeed, setAddNewCompanyCardStepAndData} from '@ import {enableExpensifyCard} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; type WorkspaceCompanyCardsErrorConfirmationProps = { policyID?: string; - newFeed?: CombinedFeedKey; + newFeed?: CompanyCardFeedWithDomainID; }; function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCompanyCardsErrorConfirmationProps) { @@ -39,7 +39,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo } const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); - const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]).find( + const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeedWithDomainID[]).find( (feed) => feed !== newFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeed(newFeed), cardIDs, feedToOpen); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 174ebf039293..5f1c5cda7520 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -11,7 +11,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; -import {filterCardsByPersonalDetails, getCardsByCardholderName, getCombinedFeedKey, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils'; +import {filterCardsByPersonalDetails, getCardsByCardholderName, getCompanyCardFeedWithDomainID, getDefaultCardName, sortCardsByCardholderName} from '@libs/CardUtils'; import {getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; @@ -75,7 +75,7 @@ function WorkspaceCompanyCardsList({cardsList, policyID, handleAssignCard, isDis if (!cardID || !item?.accountID || !item.fundID) { return; } - Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, getCombinedFeedKey(item.bank as CompanyCardFeed, item.fundID))); + Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, getCompanyCardFeedWithDomainID(item.bank as CompanyCardFeed, item.fundID))); }} > {({hovered}) => ( diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index d47a14b5e997..d4a7134501dc 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -8,7 +8,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; -import type {CombinedFeedKey} from '@hooks/useCardFeeds'; +import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds'; import useCardFeeds from '@hooks/useCardFeeds'; import useIsAllowedToIssueCompanyCard from '@hooks/useIsAllowedToIssueCompanyCard'; import useLocalize from '@hooks/useLocalize'; @@ -48,7 +48,7 @@ type WorkspaceCompanyCardsListHeaderButtonsProps = { policyID: string; /** Currently selected feed */ - selectedFeed: CombinedFeedKey; + selectedFeed: CompanyCardFeedWithDomainID; /** Whether to show assign card button */ shouldShowAssignCardButton?: boolean; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 043e16b5496c..492577b9db57 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; type WorkspaceCompanyCardsSettingsPageProps = PlatformStackScreenProps; @@ -81,7 +81,7 @@ function WorkspaceCompanyCardsSettingsPage({ if (originalFeed) { const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); - const feedToOpen = (Object.keys(companyFeeds) as CombinedFeedKey[]).find( + const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeedWithDomainID[]).find( (feed) => feed !== selectedFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); // eslint-disable-next-line @typescript-eslint/no-deprecated diff --git a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx index 4d4e8f3932cb..92f7bce11d76 100644 --- a/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardInstructionsStep.tsx @@ -14,7 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAddNewCompanyCardStepAndData} from '@libs/actions/CompanyCards'; -import {getBankName, getCombinedFeedKey} from '@libs/CardUtils'; +import {getBankName, getCompanyCardFeedWithDomainID} from '@libs/CardUtils'; import Parser from '@libs/Parser'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; @@ -57,7 +57,7 @@ function CardInstructionsStep({policyID}: CardInstructionsStepProps) { const submit = () => { if (isStripeFeedProvider && policyID) { - updateSelectedFeed(getCombinedFeedKey(feedProvider, workspaceAccountID), policyID); + updateSelectedFeed(getCompanyCardFeedWithDomainID(feedProvider, workspaceAccountID), policyID); Navigation.goBack(); return; } diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index d9a5b8f78f8d..2846ddf95777 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -22,10 +22,10 @@ import {setPlaidEvent} from '@userActions/BankAccounts'; import {importPlaidAccounts, openPlaidCompanyCardLogin} from '@userActions/Plaid'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CombinedFeedKey; policyID?: string; onExit?: () => void}) { +function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeedWithDomainID; policyID?: string; onExit?: () => void}) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx index 7df53ac9b8b9..b8b240dcfb85 100644 --- a/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssignCardFeedPage.tsx @@ -14,7 +14,7 @@ import {clearAssignCardStepAndData} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; import AssigneeStep from './AssigneeStep'; import CardNameStep from './CardNameStep'; import CardSelectionStep from './CardSelectionStep'; @@ -27,7 +27,7 @@ function AssignCardFeedPage({route, policy}: AssignCardFeedPageProps) { const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true}); const currentStep = assignCard?.currentStep; - const feed = decodeURIComponent(route.params?.feed) as CombinedFeedKey; + const feed = decodeURIComponent(route.params?.feed) as CompanyCardFeedWithDomainID; const backTo = route.params?.backTo; const policyID = policy?.id; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true}); diff --git a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx index 3c1f8ed18ee0..c165629db143 100644 --- a/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx @@ -34,7 +34,7 @@ type AssigneeStepProps = { policy: OnyxEntry; /** Selected feed */ - feed: OnyxTypes.CombinedFeedKey; + feed: OnyxTypes.CompanyCardFeedWithDomainID; }; function AssigneeStep({policy, feed}: AssigneeStepProps) { diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 2d153b43f85b..6fa6f04290f0 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -25,11 +25,11 @@ import tokenizedSearch from '@libs/tokenizedSearch'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {CombinedFeedKey} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; type CardSelectionStepProps = { /** Selected feed */ - feed: CombinedFeedKey; + feed: CompanyCardFeedWithDomainID; /** Current policy id */ policyID: string | undefined; diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index 29954c926413..dd79c5eccc5e 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -22,7 +22,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey, CurrencyList} from '@src/types/onyx'; +import type {CompanyCardFeedWithDomainID, CurrencyList} from '@src/types/onyx'; import type {AssignCardStep} from '@src/types/onyx/AssignCard'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; @@ -34,7 +34,7 @@ type ConfirmationStepProps = { backTo?: Route; /** Selected feed */ - feed: CombinedFeedKey; + feed: CompanyCardFeedWithDomainID; }; function ConfirmationStep({policyID, feed, backTo}: ConfirmationStepProps) { diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index a1ef6472a163..c7a18cf6f41f 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -30,7 +30,7 @@ import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWo import { getAllCardsForWorkspace, getCardFeedIcon, - getCombinedFeedKey, + getCompanyCardFeedWithDomainID, getCompanyFeeds, getPlaidInstitutionIconUrl, isExpensifyCardFullySetUp, @@ -289,7 +289,12 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM return; } Navigation.navigate( - ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, card.cardID.toString(), getCombinedFeedKey(card.bank as CompanyCardFeed, card.fundID), Navigation.getActiveRoute()), + ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute( + policyID, + card.cardID.toString(), + getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeed, card.fundID), + Navigation.getActiveRoute(), + ), ); }, [policyID], diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index aa36606c2e9b..78fd8c77f98d 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -42,12 +42,12 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CombinedFeedKey, CompanyCardFeed} from '@src/types/onyx'; +import type {CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx'; import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard'; type CardFeedListItem = ListItem & { /** Combined feed key */ - value: CombinedFeedKey; + value: CompanyCardFeedWithDomainID; /** Card feed value */ feed: CompanyCardFeed; @@ -76,11 +76,11 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const memberLogin = personalDetails?.[accountID]?.login ?? ''; const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; const companyFeeds = getCompanyFeeds(cardFeeds, false, true); - const currentFeed = selectedFeed ? cardFeeds?.[selectedFeed as CombinedFeedKey] : undefined; + const currentFeed = selectedFeed ? cardFeeds?.[selectedFeed as CompanyCardFeedWithDomainID] : undefined; const isFeedExpired = isSelectedFeedExpired(currentFeed); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeed); - const [list] = useCardsList(selectedFeed as CombinedFeedKey); + const [list] = useCardsList(selectedFeed as CompanyCardFeedWithDomainID); const filteredCardList = getFilteredCardList(list, currentFeed?.accountList, workspaceCardFeeds); const shouldShowExpensifyCard = isExpensifyCardFullySetUp(policy, cardSettings); @@ -104,7 +104,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew } else { const data: Partial = { email: memberLogin, - bankName: getOriginalFeed(selectedFeed as CombinedFeedKey), + bankName: getOriginalFeed(selectedFeed as CompanyCardFeedWithDomainID), cardName: `${memberName}'s card`, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; @@ -138,7 +138,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew setShouldShowError(false); }; - const companyCardFeeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CombinedFeedKey, CombinedCardFeed]>).map(([key, value]) => { + const companyCardFeeds: CardFeedListItem[] = (Object.entries(companyFeeds) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([key, value]) => { const plaidUrl = getPlaidInstitutionIconUrl(value.feed); return { @@ -170,7 +170,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew ? [ ...companyCardFeeds, { - value: CONST.EXPENSIFY_CARD.NAME as CombinedFeedKey, + value: CONST.EXPENSIFY_CARD.NAME as CompanyCardFeedWithDomainID, feed: CONST.EXPENSIFY_CARD.NAME as CompanyCardFeed, text: translate('workspace.common.expensifyCard'), keyForList: CONST.EXPENSIFY_CARD.NAME, diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts index 94561d9872ad..31ee7d1ac767 100644 --- a/src/types/onyx/CardFeeds.ts +++ b/src/types/onyx/CardFeeds.ts @@ -7,11 +7,11 @@ import type * as OnyxCommon from './OnyxCommon'; /** Card feed */ type CompanyCardFeed = ValueOf; -/** Combined feed key */ -type CombinedFeedKey = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`; +/** Company card feed with domain ID */ +type CompanyCardFeedWithDomainID = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`; /** Custom card feed with a number */ -type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CombinedFeedKey; +type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CompanyCardFeedWithDomainID; /** Statement period end */ type StatementPeriodEnd = Exclude, typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE.CUSTOM_DAY_OF_MONTH>; @@ -228,7 +228,7 @@ export type { CardFeedProvider, CardFeedData, CompanyFeeds, - CombinedFeedKey, + CompanyCardFeedWithDomainID, CustomCardFeedData, CompanyCardNicknames, CompanyCardFeedWithNumber, diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 7722bd883915..4fd8c653057d 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -16,7 +16,7 @@ import type CancellationDetails from './CancellationDetails'; import type Card from './Card'; import type {CardList, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card'; import type CardFeeds from './CardFeeds'; -import type {AddNewCompanyCardFeed, CombinedFeedKey, CompanyCardFeed, FundID} from './CardFeeds'; +import type {AddNewCompanyCardFeed, CompanyCardFeed, CompanyCardFeedWithDomainID, FundID} from './CardFeeds'; import type CardOnWaitlist from './CardOnWaitlist'; import type {CapturedLogs, Log} from './Console'; import type {CorpayFields, CorpayFormField} from './CorpayFields'; @@ -165,7 +165,7 @@ export type { IssueNewCard, AddNewCompanyCardFeed, CompanyCardFeed, - CombinedFeedKey, + CompanyCardFeedWithDomainID, LastExportMethod, Locale, LockAccountDetails, diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 9b4fc5b5ed6c..cfb4fed5833f 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -17,8 +17,8 @@ import { getCardDescription, getCardFeedIcon, getCardsByCardholderName, - getCombinedFeedKey, getCompanyCardDescription, + getCompanyCardFeedWithDomainID, getCompanyFeeds, getCustomOrFormattedFeedName, getFeedType, @@ -36,7 +36,7 @@ import { maskCardNumber, sortCardsByCardholderName, } from '@src/libs/CardUtils'; -import type {Card, CardFeeds, CardList, CombinedFeedKey, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; +import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx'; import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; @@ -490,13 +490,13 @@ describe('CardUtils', () => { describe('getSelectedFeed', () => { it('Should return last selected custom feed', () => { - const lastSelectedCustomFeed: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#12345`; + const lastSelectedCustomFeed: CompanyCardFeedWithDomainID = `${CONST.COMPANY_CARD.FEED_BANK_NAME.VISA}#12345`; const selectedFeed = getSelectedFeed(lastSelectedCustomFeed, combinedCardFeeds); expect(selectedFeed).toBe(lastSelectedCustomFeed); }); it('Should return last selected direct feed', () => { - const lastSelectedDirectFeed: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#12345`; + const lastSelectedDirectFeed: CompanyCardFeedWithDomainID = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#12345`; const selectedFeed = getSelectedFeed(lastSelectedDirectFeed, combinedCardFeeds); expect(selectedFeed).toBe(lastSelectedDirectFeed); }); @@ -1293,17 +1293,17 @@ describe('CardUtils', () => { describe('getOriginalFeed', () => { it('should extract the original feed from a combined feed key', () => { - const combinedKey: CombinedFeedKey = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`; + const combinedKey: CompanyCardFeedWithDomainID = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`; const originalFeed = getOriginalFeed(combinedKey); expect(originalFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); }); }); - describe('getCombinedFeedKey', () => { + describe('getCompanyCardFeedWithDomainID', () => { it('should combine feed name domain ID', () => { const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; const domainID = 11111111; - const combinedKey = getCombinedFeedKey(feedName, domainID); + const combinedKey = getCompanyCardFeedWithDomainID(feedName, domainID); expect(combinedKey).toBe(`${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`); }); }); From 020e930ce1dea8004682068b1e8bb41b16df878f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 14 Nov 2025 13:30:16 +0100 Subject: [PATCH 22/23] Rename getOriginalFeed function --- src/hooks/useUpdateFeedBrokenConnection.ts | 4 ++-- src/libs/CardUtils.ts | 8 ++++---- .../companyCards/BankConnection/index.native.tsx | 4 ++-- .../companyCards/BankConnection/index.tsx | 4 ++-- ...WorkspaceCompanyCardAccountSelectCardPage.tsx | 4 ++-- .../WorkspaceCompanyCardDetailsPage.tsx | 16 ++++++++-------- .../WorkspaceCompanyCardEditCardNamePage.tsx | 4 ++-- ...orkspaceCompanyCardStatementCloseDatePage.tsx | 16 ++++++++-------- .../WorkspaceCompanyCardsErrorConfirmation.tsx | 4 ++-- .../WorkspaceCompanyCardsListHeaderButtons.tsx | 12 ++++++------ .../companyCards/WorkspaceCompanyCardsPage.tsx | 12 ++++++------ ...WorkspaceCompanyCardsSettingsFeedNamePage.tsx | 10 +++++----- .../WorkspaceCompanyCardsSettingsPage.tsx | 16 ++++++++-------- .../addNew/DirectStatementCloseDatePage.tsx | 16 ++++++++-------- .../assignCard/CardSelectionStep.tsx | 4 ++-- .../companyCards/assignCard/ConfirmationStep.tsx | 4 ++-- .../members/WorkspaceMemberNewCardPage.tsx | 4 ++-- tests/unit/CardUtilsTest.ts | 8 ++++---- 18 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts index 1cebdb4b84c1..77fdc8ffe815 100644 --- a/src/hooks/useUpdateFeedBrokenConnection.ts +++ b/src/hooks/useUpdateFeedBrokenConnection.ts @@ -1,5 +1,5 @@ import {useCallback} from 'react'; -import {checkIfFeedConnectionIsBroken, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard, getOriginalFeed} from '@libs/CardUtils'; +import {checkIfFeedConnectionIsBroken, getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard} from '@libs/CardUtils'; import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import type {CompanyCardFeedWithDomainID} from '@src/types/onyx'; @@ -23,7 +23,7 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI if (!brokenCardId || !feed) { return; } - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getOriginalFeed(feed), brokenCard?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getCompanyCardFeed(feed), brokenCard?.lastScrapeResult); }, [brokenCard?.lastScrapeResult, brokenCardId, domainOrWorkspaceAccountID, feed]); return {updateBrokenConnection, isFeedConnectionBroken}; diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 32ec276418a4..9a5ea851e745 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -628,7 +628,7 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry feed === feedKey)) { const filteredFeeds = Object.keys(cardFeeds ?? {}) .filter((str) => str.includes(feedKey)) - .map((str) => getOriginalFeed(str as CompanyCardFeedWithDomainID)); + .map((str) => getCompanyCardFeed(str as CompanyCardFeedWithDomainID)); const feedNumbers = filteredFeeds.map((str) => parseInt(str.replace(feedKey, ''), 10)).filter(Boolean); feedNumbers.sort((a, b) => a - b); @@ -736,8 +736,8 @@ function getFeedConnectionBrokenCard(feedCards: Record | undefined } /** Extract feed from feed with domainID */ -function getOriginalFeed(feedKey: CompanyCardFeedWithDomainID): CompanyCardFeed { - const [feed] = feedKey.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); +function getCompanyCardFeed(feedWithDomainID: CompanyCardFeedWithDomainID): CompanyCardFeed { + const [feed] = feedWithDomainID.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR); return feed as CompanyCardFeed; } @@ -795,7 +795,7 @@ export { getFeedConnectionBrokenCard, getCorrectStepForPlaidSelectedBank, getOriginalCompanyFeeds, - getOriginalFeed, + getCompanyCardFeed, getCompanyCardFeedWithDomainID, getEligibleBankAccountsForUkEuCard, }; diff --git a/src/pages/workspace/companyCards/BankConnection/index.native.tsx b/src/pages/workspace/companyCards/BankConnection/index.native.tsx index b445f023938a..8fd8f73021dd 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.native.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.native.tsx @@ -16,7 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {updateSelectedFeed} from '@libs/actions/Card'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, getOriginalFeed, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import getUAForWebView from '@libs/getUAForWebView'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; @@ -52,7 +52,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const selectedBank = addNewCard?.data?.selectedBank; const {bankName: bankNameFromRoute, backTo, policyID: policyIDFromRoute} = route?.params ?? {}; const policyID = policyIDFromProps ?? policyIDFromRoute; - const bankName = feed ? getBankName(getOriginalFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isBetaEnabled} = usePermissions(); const plaidToken = addNewCard?.data?.publicToken ?? assignCard?.data?.plaidAccessToken; const isPlaid = isBetaEnabled(CONST.BETAS.PLAID_COMPANY_CARDS) && !!plaidToken; diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index f7b8bdf5a049..d956ec2edc02 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -17,7 +17,7 @@ import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {checkIfNewFeedConnected, getBankName, getOriginalFeed, isSelectedFeedExpired} from '@libs/CardUtils'; +import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -56,7 +56,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti const prevFeedsData = usePrevious(cardFeeds); const [shouldBlockWindowOpen, setShouldBlockWindowOpen] = useState(false); const selectedBank = addNewCard?.data?.selectedBank; - const bankName = feed ? getBankName(getOriginalFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); + const bankName = feed ? getBankName(getCompanyCardFeed(feed)) : (bankNameFromRoute ?? addNewCard?.data?.plaidConnectedFeed ?? selectedBank); const {isNewFeedConnected, newFeed} = useMemo( () => checkIfNewFeedConnected(prevFeedsData ?? {}, cardFeeds ?? {}, addNewCard?.data?.plaidConnectedFeed), [addNewCard?.data?.plaidConnectedFeed, cardFeeds, prevFeedsData], diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx index 4228fb7fd50c..b0691d963b23 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage.tsx @@ -14,7 +14,7 @@ import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {setCompanyCardExportAccount} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getConnectedIntegration, getCurrentConnectionName} from '@libs/PolicyUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -81,7 +81,7 @@ function WorkspaceCompanyCardAccountSelectCardPage({route}: WorkspaceCompanyCard } const isDefaultCardSelected = value === defaultCard; const exportValue = isDefaultCardSelected ? CONST.COMPANY_CARDS.DEFAULT_EXPORT_TYPE : value; - setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, getOriginalFeed(bank)); + setCompanyCardExportAccount(policyID, domainOrWorkspaceAccountID, cardID, exportMenuItem.exportType, exportValue, getCompanyCardFeed(bank)); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank)); }, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index d02efe64ec70..41b5799e4fd0 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -23,7 +23,7 @@ import usePolicy from '@hooks/usePolicy'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCardFeedIcon, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getOriginalFeed, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getCompanyCardFeed, getCompanyFeeds, getDefaultCardName, getDomainOrWorkspaceAccountID, getPlaidInstitutionIconUrl, maskCardNumber} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -48,7 +48,7 @@ type WorkspaceCompanyCardDetailsPageProps = PlatformStackScreenProps { setIsUnassignModalVisible(false); if (card) { - unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, originalFeed, card); + unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID, feed, card); } Navigation.goBack(); }; const updateCard = () => { - updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, originalFeed, card?.lastScrapeResult); + updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, cardID, feed, card?.lastScrapeResult); }; const lastScrape = useMemo(() => { @@ -155,7 +155,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag @@ -163,7 +163,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag pendingAction={card?.nameValuePairs?.pendingFields?.cardTitle} errorRowStyles={[styles.ph5, styles.mb3]} errors={getLatestErrorField(card?.nameValuePairs ?? {}, 'cardTitle')} - onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeed, 'cardTitle')} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, feed, 'cardTitle')} > clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, originalFeed, 'lastScrape', true)} + onClose={() => clearCompanyCardErrorField(domainOrWorkspaceAccountID, cardID, feed, 'lastScrape', true)} > ) => { - updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], getOriginalFeed(bank), defaultValue); + updateCompanyCardName(domainOrWorkspaceAccountID, cardID, values[INPUT_IDS.NAME], getCompanyCardFeed(bank), defaultValue); Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.getRoute(policyID, cardID, bank), {compareParams: false}); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx index 84e904ad8cd8..ff7d58f45e4b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -29,7 +29,7 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -51,13 +51,13 @@ function WorkspaceCompanyCardStatementCloseDatePage({ const submit = useCallback( (newStatementPeriodEnd: StatementPeriodEnd | undefined, newStatementPeriodEndDay: StatementPeriodEndDay | undefined) => { const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; - if (originalFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, originalFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + if (feed && isChangedValue) { + setFeedStatementPeriodEndDay(policyID, feed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }, - [policyID, originalFeed, statementPeriodEndDay, domainOrWorkspaceAccountID], + [policyID, feed, statementPeriodEndDay, domainOrWorkspaceAccountID], ); const goBack = useCallback(() => { @@ -65,12 +65,12 @@ function WorkspaceCompanyCardStatementCloseDatePage({ }, [policyID]); const clearError = useCallback(() => { - if (!originalFeed) { + if (!feed) { return; } - clearErrorField(originalFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); - }, [originalFeed, domainOrWorkspaceAccountID]); + clearErrorField(feed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + }, [feed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { return ; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx index 91ab5a7f57f5..bc3d851a72d4 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation.tsx @@ -8,7 +8,7 @@ import useCardsList from '@hooks/useCardsList'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils'; import Navigation from '@navigation/Navigation'; import {deleteWorkspaceCompanyCardFeed, setAddNewCompanyCardStepAndData} from '@userActions/CompanyCards'; import {enableExpensifyCard} from '@userActions/Policy/Policy'; @@ -42,7 +42,7 @@ function WorkspaceCompanyCardsErrorConfirmation({policyID, newFeed}: WorkspaceCo const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeedWithDomainID[]).find( (feed) => feed !== newFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getOriginalFeed(newFeed), cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, getCompanyCardFeed(newFeed), cardIDs, feedToOpen); }; const onButtonPress = () => { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index d4a7134501dc..474f7ce79ff8 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -25,10 +25,10 @@ import { flatAllCardsList, getBankName, getCardFeedIcon, + getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, - getOriginalFeed, getPlaidCountry, getPlaidInstitutionIconUrl, getPlaidInstitutionId, @@ -70,13 +70,13 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const originalFeed = getOriginalFeed(selectedFeed); - const formattedFeedName = getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName); + const feed = getCompanyCardFeed(selectedFeed); + const formattedFeedName = getCustomOrFormattedFeedName(feed, cardFeeds?.[selectedFeed]?.customFeedName); const isCommercialFeed = isCustomFeed(selectedFeed); const plaidUrl = getPlaidInstitutionIconUrl(selectedFeed); const companyFeeds = getCompanyFeeds(cardFeeds); const currentFeedData = companyFeeds?.[selectedFeed]; - const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(originalFeed); + const bankName = plaidUrl && formattedFeedName ? formattedFeedName : getBankName(feed); const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, currentFeedData); const filteredFeedCards = filterInactiveCards(allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${selectedFeed}`]); const hasFeedError = !!cardFeeds?.[selectedFeed]?.errors; @@ -87,7 +87,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS const openBankConnection = () => { const institutionId = !!getPlaidInstitutionId(selectedFeed); const data: Partial = { - bankName: originalFeed, + bankName: feed, }; if (institutionId) { const country = getPlaidCountry(policy?.outputCurrency, currencyList, countryByIp); @@ -133,7 +133,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))} - cardIcon={getCardFeedIcon(originalFeed, illustrations)} + cardIcon={getCardFeedIcon(feed, illustrations)} shouldChangeLayout={shouldChangeLayout} feedName={formattedFeedName} supportingText={supportingText} diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index d4641c609e49..86dc3915da99 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -14,10 +14,10 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import { checkIfFeedConnectionIsBroken, + getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFilteredCardList, - getOriginalFeed, getPlaidCountry, getPlaidInstitutionId, getSelectedFeed, @@ -59,7 +59,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { const [workspaceCardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: true}); const [cardFeeds, , defaultFeed] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false}); const [currencyList = getEmptyObject()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); @@ -96,12 +96,12 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { }, [fetchCompanyCards]); useEffect(() => { - if (isLoading || !originalFeed || isPending) { + if (isLoading || !feed || isPending) { return; } - openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, originalFeed); - }, [originalFeed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); + openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, feed); + }, [feed, isLoading, policyID, isPending, domainOrWorkspaceAccountID]); const handleAssignCard = () => { if (isActingAsDelegate) { @@ -122,7 +122,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) { } const data: Partial = { - bankName: originalFeed, + bankName: feed, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.ASSIGNEE; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx index 921e958b75ec..1e2cd87bd7de 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsFeedNamePage.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -44,9 +44,9 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(feed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed ? companyFeeds[selectedFeed] : undefined); const validate = useCallback( @@ -69,8 +69,8 @@ function WorkspaceCompanyCardsSettingsFeedNamePage({ ); const submit = ({name}: WorkspaceCompanyCardFeedName) => { - if (originalFeed) { - setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, originalFeed, name); + if (feed) { + setWorkspaceCompanyCardFeedName(policyID, domainOrWorkspaceAccountID, feed, name); } Navigation.goBack(ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 492577b9db57..4a638ecb1ff5 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -16,7 +16,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {deleteWorkspaceCompanyCardFeed, setWorkspaceCompanyCardTransactionLiability} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; @@ -45,10 +45,10 @@ function WorkspaceCompanyCardsSettingsPage({ const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const selectedFeed = useMemo(() => getSelectedFeed(lastSelectedFeed, cardFeeds), [cardFeeds, lastSelectedFeed]); - const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const [cardsList] = useCardsList(selectedFeed); - const feedName = selectedFeed ? getCustomOrFormattedFeedName(originalFeed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; + const feedName = selectedFeed ? getCustomOrFormattedFeedName(feed, cardFeeds?.[selectedFeed]?.customFeedName) : undefined; const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; const liabilityType = selectedFeedData?.liabilityType; @@ -78,27 +78,27 @@ function WorkspaceCompanyCardsSettingsPage({ const deleteCompanyCardFeed = () => { setDeleteCompanyCardConfirmModalVisible(false); Navigation.goBack(); - if (originalFeed) { + if (feed) { const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeedWithDomainID[]).find( - (feed) => feed !== selectedFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + (feedWithDomainID) => feedWithDomainID !== selectedFeed && companyFeeds[feedWithDomainID]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { - deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, originalFeed, cardIDs, feedToOpen); + deleteWorkspaceCompanyCardFeed(policyID, domainOrWorkspaceAccountID, feed, cardIDs, feedToOpen); }); } }; const onToggleLiability = (isOn: boolean) => { - if (!originalFeed) { + if (!feed) { return; } setWorkspaceCompanyCardTransactionLiability( domainOrWorkspaceAccountID, policyID, - originalFeed, + feed, isOn ? CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.ALLOW : CONST.COMPANY_CARDS.DELETE_TRANSACTIONS.RESTRICT, ); }; diff --git a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx index 451de0124081..79a36cc0203c 100644 --- a/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx +++ b/src/pages/workspace/companyCards/addNew/DirectStatementCloseDatePage.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID'; import {clearErrorField, setFeedStatementPeriodEndDay} from '@libs/actions/CompanyCards'; -import {getCompanyFeeds, getDomainOrWorkspaceAccountID, getOriginalFeed, getSelectedFeed} from '@libs/CardUtils'; +import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getSelectedFeed} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import WorkspaceCompanyCardStatementCloseDateSelectionList from '@pages/workspace/companyCards/WorkspaceCompanyCardStatementCloseDateSelectionList'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +22,7 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr const [lastSelectedFeed, lastSelectedFeedResult] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true}); const [cardFeeds, cardFeedsResult] = useCardFeeds(policyID); const selectedFeed = getSelectedFeed(lastSelectedFeed, cardFeeds); - const originalFeed = selectedFeed ? getOriginalFeed(selectedFeed) : undefined; + const feed = selectedFeed ? getCompanyCardFeed(selectedFeed) : undefined; const workspaceAccountID = useWorkspaceAccountID(policyID); const companyFeeds = getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed ? companyFeeds[selectedFeed] : undefined; @@ -52,22 +52,22 @@ function DirectStatementCloseDateStep({policyID}: DirectStatementCloseDateStepPr return; } const isChangedValue = (newStatementPeriodEndDay ?? newStatementPeriodEnd) !== statementPeriodEndDay; - if (originalFeed && isChangedValue) { - setFeedStatementPeriodEndDay(policyID, originalFeed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); + if (feed && isChangedValue) { + setFeedStatementPeriodEndDay(policyID, feed, domainOrWorkspaceAccountID, newStatementPeriodEnd, newStatementPeriodEndDay, statementPeriodEndDay); } goBack(); }, - [policyID, statementPeriodEndDay, goBack, originalFeed, domainOrWorkspaceAccountID], + [policyID, statementPeriodEndDay, goBack, feed, domainOrWorkspaceAccountID], ); const clearError = useCallback(() => { - if (!originalFeed) { + if (!feed) { return; } - clearErrorField(originalFeed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); - }, [originalFeed, domainOrWorkspaceAccountID]); + clearErrorField(feed, domainOrWorkspaceAccountID, 'statementPeriodEndDay'); + }, [feed, domainOrWorkspaceAccountID]); if (isLoadingOnyxValue(cardFeedsResult) || isLoadingOnyxValue(lastSelectedFeedResult)) { return ; diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index 6fa6f04290f0..89d8fbc4dd28 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -18,7 +18,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; -import {getCardFeedIcon, getFilteredCardList, getOriginalFeed, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import {getCardFeedIcon, getCompanyCardFeed, getFilteredCardList, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import tokenizedSearch from '@libs/tokenizedSearch'; @@ -66,7 +66,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { /> ) : ( ()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); - const bankName = assignCard?.data?.bankName ?? getOriginalFeed(feed); + const bankName = assignCard?.data?.bankName ?? getCompanyCardFeed(feed); const [cardFeeds] = useCardFeeds(policyID); const data = assignCard?.data; diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 78fd8c77f98d..b4cdc4796946 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -17,11 +17,11 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import { getCardFeedIcon, + getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getDomainOrWorkspaceAccountID, getFilteredCardList, - getOriginalFeed, getPlaidInstitutionIconUrl, hasOnlyOneCardToAssign, isCustomFeed, @@ -104,7 +104,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew } else { const data: Partial = { email: memberLogin, - bankName: getOriginalFeed(selectedFeed as CompanyCardFeedWithDomainID), + bankName: getCompanyCardFeed(selectedFeed as CompanyCardFeedWithDomainID), cardName: `${memberName}'s card`, }; let currentStep: AssignCardStep = CONST.COMPANY_CARD.STEP.CARD; diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index cfb4fed5833f..938506e1b1fa 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -25,7 +25,7 @@ import { getFilteredCardList, getMonthFromExpirationDateString, getOriginalCompanyFeeds, - getOriginalFeed, + getCompanyCardFeed, getSelectedFeed, getYearFromExpirationDateString, hasIssuedExpensifyCard, @@ -1291,11 +1291,11 @@ describe('CardUtils', () => { }); }); - describe('getOriginalFeed', () => { + describe('getCompanyCardFeed', () => { it('should extract the original feed from a combined feed key', () => { const combinedKey: CompanyCardFeedWithDomainID = `${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}#22222222`; - const originalFeed = getOriginalFeed(combinedKey); - expect(originalFeed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); + const feed = getCompanyCardFeed(combinedKey); + expect(feed).toBe(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); }); }); From 5b6c22e7f894f7cf34936e5e9b5aacf0a83d69db Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 14 Nov 2025 13:39:24 +0100 Subject: [PATCH 23/23] Fix prettier check --- tests/unit/CardUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 938506e1b1fa..0461e7945543 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -18,6 +18,7 @@ import { getCardFeedIcon, getCardsByCardholderName, getCompanyCardDescription, + getCompanyCardFeed, getCompanyCardFeedWithDomainID, getCompanyFeeds, getCustomOrFormattedFeedName, @@ -25,7 +26,6 @@ import { getFilteredCardList, getMonthFromExpirationDateString, getOriginalCompanyFeeds, - getCompanyCardFeed, getSelectedFeed, getYearFromExpirationDateString, hasIssuedExpensifyCard,