From daf0d1bb5467b909f137cb0556c839c1476f69d9 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 20 Dec 2024 17:40:52 +0100 Subject: [PATCH 1/8] Improve feed removal offline --- src/libs/CardUtils.ts | 17 +++-- src/libs/actions/CompanyCards.ts | 66 +++++++++++++++++-- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 2 + .../WorkspaceCompanyCardsPage.tsx | 2 +- .../WorkspaceCompanyCardsSettingsPage.tsx | 5 +- .../members/WorkspaceMemberNewCardPage.tsx | 3 + src/types/onyx/CardFeeds.ts | 8 +-- 7 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 9a71480019a6..2785bb294405 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -252,16 +252,25 @@ function isCustomFeed(feed: CompanyCardFeed): boolean { } function getCompanyFeeds(cardFeeds: OnyxEntry): CompanyFeeds { - return {...cardFeeds?.settings?.companyCards, ...cardFeeds?.settings?.oAuthAccountDetails}; + const allFeeds = {...cardFeeds?.settings?.companyCards, ...cardFeeds?.settings?.oAuthAccountDetails}; + const {[CONST.EXPENSIFY_CARD.BANK as CompanyCardFeed]: expensifyFeed, ...companyFeeds} = allFeeds; + return companyFeeds; } -function removeExpensifyCardFromCompanyCards(cardFeeds: OnyxEntry): CompanyFeeds { +function removeExpensifyCardFromCompanyCards(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false): CompanyFeeds { if (!cardFeeds) { return {}; } const companyCards = getCompanyFeeds(cardFeeds); - return Object.fromEntries(Object.entries(companyCards).filter(([key]) => key !== CONST.EXPENSIFY_CARD.BANK)); + return Object.fromEntries( + Object.entries(companyCards).filter(([key, value]) => { + if (shouldFilterOutRemovedFeeds && value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + return false; + } + return key !== CONST.EXPENSIFY_CARD.BANK; + }), + ); } function getCardFeedName(feedType: CompanyCardFeed): string { @@ -348,7 +357,7 @@ const getCorrectStepForSelectedBank = (selectedBank: ValueOf, cardFeeds: OnyxEntry): CompanyCardFeed | undefined { - const defaultFeed = Object.keys(removeExpensifyCardFromCompanyCards(cardFeeds)).at(0) as CompanyCardFeed | undefined; + const defaultFeed = Object.keys(removeExpensifyCardFromCompanyCards(cardFeeds, true)).at(0) as CompanyCardFeed | undefined; return lastSelectedFeed ?? defaultFeed; } diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 8e83b9192a71..ee059086adde 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -151,10 +151,14 @@ function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, API.write(WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY, parameters, onyxData); } -function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: number, bankName: CompanyCardFeed, feedToOpen?: CompanyCardFeed) { +function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: number, bankName: CompanyCardFeed, cardIDs: string[], feedToOpen?: CompanyCardFeed) { const authToken = NetworkStore.getAuthToken(); const isCustomFeed = CardUtils.isCustomFeed(bankName); - const feedUpdates = {[bankName]: null}; + const optimisticFeedUpdates = {[bankName]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}}; + const successFeedUpdates = {[bankName]: null}; + const failureFeedUpdates = {[bankName]: {pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}}; + const optimisticCardUpdates = Object.fromEntries(cardIDs.map((cardID) => [cardID, {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}])); + const successAndFailureCardUpdates = Object.fromEntries(cardIDs.map((cardID) => [cardID, {pendingAction: null}])); const optimisticData: OnyxUpdate[] = [ { @@ -162,13 +166,67 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { settings: { - ...(isCustomFeed ? {companyCards: feedUpdates} : {oAuthAccountDetails: feedUpdates}), + ...(isCustomFeed ? {companyCards: optimisticFeedUpdates} : {oAuthAccountDetails: optimisticFeedUpdates}), + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`, + value: optimisticCardUpdates, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CARD_LIST, + value: optimisticCardUpdates, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, + value: { + settings: { + ...(isCustomFeed ? {companyCards: successFeedUpdates} : {oAuthAccountDetails: successFeedUpdates, companyCards: successFeedUpdates}), companyCardNicknames: { [bankName]: null, }, }, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`, + value: successAndFailureCardUpdates, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CARD_LIST, + value: successAndFailureCardUpdates, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, + value: { + settings: { + ...(isCustomFeed ? {companyCards: failureFeedUpdates} : {oAuthAccountDetails: failureFeedUpdates}), + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`, + value: successAndFailureCardUpdates, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CARD_LIST, + value: successAndFailureCardUpdates, + }, ]; if (feedToOpen) { @@ -185,7 +243,7 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu bankName, }; - API.write(WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED, parameters, {optimisticData}); + API.write(WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED, parameters, {optimisticData, successData, failureData}); } function assignWorkspaceCompanyCard(policyID: string, data?: Partial) { diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 723242c55494..d8109d7b7caf 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -49,6 +49,8 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS text: CardUtils.getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), keyForList: feed, isSelected: feed === selectedFeed, + isDisabled: companyFeeds[feed]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: companyFeeds[feed]?.pendingAction, brickRoadIndicator: companyFeeds[feed]?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, canShowSeveralIndicators: !!companyFeeds[feed]?.errors, leftElement: ( diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index 392138a2d8d1..b424c69a9d41 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -51,7 +51,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const companyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); const selectedFeedData = selectedFeed && companyCards[selectedFeed]; - const isNoFeed = isEmptyObject(companyCards) && !selectedFeedData; + const isNoFeed = !selectedFeedData; const isPending = !!selectedFeedData?.pending; const isFeedAdded = !isPending && !isNoFeed; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 58c79d41d3c9..13df27f84f83 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -42,6 +42,7 @@ function WorkspaceCompanyCardsSettingsPage({ const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we want to run the hook only once to escape unexpected feed change const selectedFeed = useMemo(() => CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds), []); + const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); const feedName = CardUtils.getCustomOrFormattedFeedName(selectedFeed, cardFeeds?.settings?.companyCardNicknames); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); const liabilityType = selectedFeed && companyFeeds[selectedFeed]?.liabilityType; @@ -53,8 +54,10 @@ function WorkspaceCompanyCardsSettingsPage({ const deleteCompanyCardFeed = () => { if (selectedFeed) { + const {cardList, ...cards} = cardsList ?? {}; + const cardIDs = Object.keys(cards); const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeed[]).filter((feed) => feed !== selectedFeed).at(0); - CompanyCards.deleteWorkspaceCompanyCardFeed(policyID, workspaceAccountID, selectedFeed, feedToOpen); + CompanyCards.deleteWorkspaceCompanyCardFeed(policyID, workspaceAccountID, selectedFeed, cardIDs, feedToOpen); } setDeleteCompanyCardConfirmModalVisible(false); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index d53d8a558276..3600963b455e 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -50,6 +50,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew const memberLogin = personalDetails?.[accountID]?.login ?? ''; const memberName = personalDetails?.[accountID]?.firstName ? personalDetails?.[accountID]?.firstName : personalDetails?.[accountID]?.login; const availableCompanyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); + const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); const filteredCardList = CardUtils.getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed as CompanyCardFeed]); @@ -101,6 +102,8 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew value: key, text: CardUtils.getCustomOrFormattedFeedName(key, cardFeeds?.settings?.companyCardNicknames), keyForList: key, + isDisabled: companyFeeds[key]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + pendingAction: companyFeeds[key]?.pendingAction, isSelected: selectedFeed === key, leftElement: ( ; /** Direct card feed data */ -type DirectCardFeedData = { +type DirectCardFeedData = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** List of accounts */ accountList: string[]; @@ -58,7 +58,7 @@ type DirectCardFeedData = { /** Broken connection errors */ errors?: OnyxCommon.Errors; -}; +}>; /** Card feed data */ type CardFeedData = CustomCardFeedData | DirectCardFeedData; From c3accb3f17732be79046f2df625ef5c616341102 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 10:38:49 +0100 Subject: [PATCH 2/8] Fix lint errors --- src/libs/CardUtils.ts | 4 +- src/libs/actions/CompanyCards.ts | 134 +++++++++--------- .../WorkspaceCompanyCardsSettingsPage.tsx | 2 +- 3 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 2785bb294405..c2e8744e0d5d 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -203,8 +203,8 @@ function getEligibleBankAccountsForCard(bankAccountsList: OnyxEntry, personalDetails: OnyxEntry): Card[] { const {cardList, ...cards} = cardsList ?? {}; return Object.values(cards).sort((cardA: Card, cardB: Card) => { - const userA = personalDetails?.[cardA.accountID ?? '-1'] ?? {}; - const userB = personalDetails?.[cardB.accountID ?? '-1'] ?? {}; + const userA = cardA.accountID ? personalDetails?.[cardA.accountID] ?? {} : {}; + const userB = cardB.accountID ? personalDetails?.[cardB.accountID] ?? {} : {}; const aName = PersonalDetailsUtils.getDisplayNameOrDefault(userA); const bName = PersonalDetailsUtils.getDisplayNameOrDefault(userB); diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index ee059086adde..f145bfd54afa 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -252,7 +252,7 @@ function assignWorkspaceCompanyCard(policyID: string, data?: Partial Date: Mon, 23 Dec 2024 10:48:37 +0100 Subject: [PATCH 3/8] Fix tests --- tests/unit/CardUtilsTest.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index c294b068a62d..4bd3ba28a88d 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -2,7 +2,6 @@ import type {OnyxCollection} from 'react-native-onyx'; import CONST from '@src/CONST'; import * as CardUtils from '@src/libs/CardUtils'; import type * as OnyxTypes from '@src/types/onyx'; -import type {CompanyFeeds} from '@src/types/onyx/CardFeeds'; const shortDate = '0924'; const shortDateSlashed = '09/24'; @@ -115,7 +114,6 @@ const customFeedCardsList = { '480801XXXXXX2566': 'ENCRYPTED_CARD_NUMBER', }, } as unknown as OnyxTypes.WorkspaceCardsList; -const allFeeds: CompanyFeeds = {...customFeeds, ...directFeeds}; const customFeedName = 'Custom feed name'; const cardFeedsCollection: OnyxCollection = { @@ -200,7 +198,7 @@ describe('CardUtils', () => { describe('getCompanyFeeds', () => { it('Should return both custom and direct feeds if exists', () => { const companyFeeds = CardUtils.getCompanyFeeds(cardFeedsCollection.FAKE_ID_1); - expect(companyFeeds).toStrictEqual(allFeeds); + expect(companyFeeds).toStrictEqual({...directFeeds, ...customFeedsWithoutExpensifyBank}); }); it('Should return direct feeds only since custom feeds are not exist', () => { @@ -210,7 +208,7 @@ describe('CardUtils', () => { it('Should return custom feeds only since direct feeds are not exist', () => { const companyFeeds = CardUtils.getCompanyFeeds(cardFeedsCollection.FAKE_ID_3); - expect(companyFeeds).toStrictEqual(customFeeds); + expect(companyFeeds).toStrictEqual(customFeedsWithoutExpensifyBank); }); it('Should return empty object if undefined is passed', () => { From 470b8350d3590ba28168c693e1d004af68db4b24 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 11:41:59 +0100 Subject: [PATCH 4/8] Code improvement --- src/libs/actions/CompanyCards.ts | 132 +++++++++--------- .../WorkspaceCompanyCardDetailsPage.tsx | 4 +- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index f145bfd54afa..6d25d0135a83 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -300,74 +300,72 @@ function assignWorkspaceCompanyCard(policyID: string, data?: Partial { setIsUnassignModalVisible(false); - CompanyCards.unassignWorkspaceCompanyCard(workspaceAccountID, bank, card); + if (card) { + CompanyCards.unassignWorkspaceCompanyCard(workspaceAccountID, bank, card); + } Navigation.goBack(); }; From b75a4cfe2e1ffc3311b4fbdefe7399d4c1b00d2f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 13:33:31 +0100 Subject: [PATCH 5/8] Fix lint --- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 85a5d2372ee9..55cc6e5e2e16 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -81,7 +81,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const isSelectedMemberCurrentUser = accountID === currentUserPersonalDetails?.accountID; const isCurrentUserAdmin = policy?.employeeList?.[personalDetails?.[currentUserPersonalDetails?.accountID]?.login ?? '']?.role === CONST.POLICY.ROLE.ADMIN; const isCurrentUserOwner = policy?.owner === currentUserPersonalDetails?.login; - const ownerDetails = personalDetails?.[policy?.ownerAccountID ?? -1] ?? ({} as PersonalDetails); + const ownerDetails = personalDetails?.[policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? ({} as PersonalDetails); const policyOwnerDisplayName = formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(ownerDetails)) ?? policy?.owner ?? ''; const hasMultipleFeeds = Object.values(CardUtils.getCompanyFeeds(cardFeeds)).filter((feed) => !feed.pending).length > 0; const paymentAccountID = cardSettings?.paymentBankAccountID ?? 0; From 785eccd15268f0fbefb3638fb216b2e29061aca9 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 14:01:09 +0100 Subject: [PATCH 6/8] Improve getCompanyFeeds function and get rid of removeExpensifyCardFromCompanyCards --- src/libs/CardUtils.ts | 18 +---- src/libs/actions/CompanyCards.ts | 12 ++-- .../workspace/WorkspaceMoreFeaturesPage.tsx | 2 +- .../WorkspaceCompanyCardFeedSelectorPage.tsx | 3 +- .../WorkspaceCompanyCardsPage.tsx | 2 +- .../members/WorkspaceMemberNewCardPage.tsx | 3 +- tests/unit/CardUtilsTest.ts | 67 ++++++++++--------- 7 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index c2e8744e0d5d..e26618fd3a6d 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -251,20 +251,9 @@ function isCustomFeed(feed: CompanyCardFeed): 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) => value === feed); } -function getCompanyFeeds(cardFeeds: OnyxEntry): CompanyFeeds { - const allFeeds = {...cardFeeds?.settings?.companyCards, ...cardFeeds?.settings?.oAuthAccountDetails}; - const {[CONST.EXPENSIFY_CARD.BANK as CompanyCardFeed]: expensifyFeed, ...companyFeeds} = allFeeds; - return companyFeeds; -} - -function removeExpensifyCardFromCompanyCards(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false): CompanyFeeds { - if (!cardFeeds) { - return {}; - } - - const companyCards = getCompanyFeeds(cardFeeds); +function getCompanyFeeds(cardFeeds: OnyxEntry, shouldFilterOutRemovedFeeds = false): CompanyFeeds { return Object.fromEntries( - Object.entries(companyCards).filter(([key, value]) => { + Object.entries(cardFeeds?.settings?.companyCards ?? {}).filter(([key, value]) => { if (shouldFilterOutRemovedFeeds && value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return false; } @@ -357,7 +346,7 @@ const getCorrectStepForSelectedBank = (selectedBank: ValueOf, cardFeeds: OnyxEntry): CompanyCardFeed | undefined { - const defaultFeed = Object.keys(removeExpensifyCardFromCompanyCards(cardFeeds, true)).at(0) as CompanyCardFeed | undefined; + const defaultFeed = Object.keys(getCompanyFeeds(cardFeeds, true)).at(0) as CompanyCardFeed | undefined; return lastSelectedFeed ?? defaultFeed; } @@ -419,7 +408,6 @@ export { getSelectedFeed, getCorrectStepForSelectedBank, getCustomOrFormattedFeedName, - removeExpensifyCardFromCompanyCards, getFilteredCardList, hasOnlyOneCardToAssign, checkIfNewFeedConnected, diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 6d25d0135a83..4fba1e89be2f 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -124,7 +124,6 @@ function setWorkspaceCompanyCardFeedName(policyID: string, workspaceAccountID: n function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, policyID: string, bankName: CompanyCardFeed, liabilityType: string) { const authToken = NetworkStore.getAuthToken(); - const isCustomFeed = CardUtils.isCustomFeed(bankName); const feedUpdates = { [bankName]: {liabilityType}, }; @@ -135,7 +134,7 @@ function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: isCustomFeed ? {companyCards: feedUpdates} : {oAuthAccountDetails: feedUpdates}, + settings: {companyCards: feedUpdates}, }, }, ], @@ -166,7 +165,7 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { settings: { - ...(isCustomFeed ? {companyCards: optimisticFeedUpdates} : {oAuthAccountDetails: optimisticFeedUpdates}), + companyCards: optimisticFeedUpdates, }, }, }, @@ -213,7 +212,7 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { settings: { - ...(isCustomFeed ? {companyCards: failureFeedUpdates} : {oAuthAccountDetails: failureFeedUpdates}), + companyCards: failureFeedUpdates, }, }, }, @@ -377,7 +376,6 @@ function unassignWorkspaceCompanyCard(workspaceAccountID: number, bankName: stri function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, bankName: CompanyCardFeed) { const authToken = NetworkStore.getAuthToken(); - const isCustomFeed = CardUtils.isCustomFeed(bankName); const optimisticFeedUpdates = {[bankName]: {errors: null}}; const failureFeedUpdates = {[bankName]: {errors: {error: CONST.COMPANY_CARDS.CONNECTION_ERROR}}}; @@ -416,7 +414,7 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: isCustomFeed ? {companyCards: optimisticFeedUpdates} : {oAuthAccountDetails: optimisticFeedUpdates}, + settings: {companyCards: optimisticFeedUpdates}, }, }, ]; @@ -483,7 +481,7 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: isCustomFeed ? {companyCards: failureFeedUpdates} : {oAuthAccountDetails: failureFeedUpdates}, + settings: {companyCards: failureFeedUpdates}, }, }, ]; diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index ddcb89064c7e..53ef127193ed 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -131,7 +131,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle', isActive: policy?.areCompanyCardsEnabled ?? false, pendingAction: policy?.pendingFields?.areCompanyCardsEnabled, - disabled: !isEmptyObject(CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds)), + disabled: !isEmptyObject(CardUtils.getCompanyFeeds(cardFeeds)), action: (isEnabled: boolean) => { if (!policyID) { return; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index d8109d7b7caf..29d0dfb0c6af 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -42,9 +42,8 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const selectedFeed = CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); - const availableCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); - const feeds: CardFeedListItem[] = (Object.keys(availableCards) as CompanyCardFeed[]).map((feed) => ({ + const feeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((feed) => ({ value: feed, text: CardUtils.getCustomOrFormattedFeedName(feed, cardFeeds?.settings?.companyCardNicknames), keyForList: feed, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index b424c69a9d41..ea3a0e0f7071 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -49,7 +49,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const filteredCardList = CardUtils.getFilteredCardList(cardsList, selectedFeed ? cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed] : undefined); - const companyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); + const companyCards = CardUtils.getCompanyFeeds(cardFeeds); const selectedFeedData = selectedFeed && companyCards[selectedFeed]; const isNoFeed = !selectedFeedData; const isPending = !!selectedFeedData?.pending; diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index 3600963b455e..afa76e63a815 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -49,7 +49,6 @@ 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 availableCompanyCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`); @@ -98,7 +97,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew setShouldShowError(false); }; - const companyCardFeeds: CardFeedListItem[] = (Object.keys(availableCompanyCards) as CompanyCardFeed[]).map((key) => ({ + const companyCardFeeds: CardFeedListItem[] = (Object.keys(companyFeeds) as CompanyCardFeed[]).map((key) => ({ value: key, text: CardUtils.getCustomOrFormattedFeedName(key, cardFeeds?.settings?.companyCardNicknames), keyForList: key, diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 4bd3ba28a88d..2685a77836b3 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -12,7 +12,7 @@ const longDateHyphen = '09-2024'; const expectedMonth = '09'; const expectedYear = '2024'; -const customFeeds = { +const companyCardsCustomFeedSettings = { [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { pending: true, }, @@ -23,7 +23,7 @@ const customFeeds = { liabilityType: 'personal', }, }; -const customFeedsWithoutExpensifyBank = { +const companyCardsCustomFeedSettingsWithoutExpensifyBank = { [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: { pending: true, }, @@ -31,7 +31,25 @@ const customFeedsWithoutExpensifyBank = { liabilityType: 'personal', }, }; -const directFeeds = { +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 oAuthAccountDetails = { [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: { accountList: ['CREDIT CARD...6607', 'CREDIT CARD...5501'], credentials: 'xxxxx', @@ -117,23 +135,27 @@ const customFeedCardsList = { const customFeedName = 'Custom feed name'; const cardFeedsCollection: OnyxCollection = { + // Policy with both custom and direct feeds FAKE_ID_1: { settings: { companyCardNicknames: { [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: customFeedName, }, - companyCards: customFeeds, - oAuthAccountDetails: directFeeds, + companyCards: {...companyCardsCustomFeedSettings, ...companyCardsDirectFeedSettings}, + oAuthAccountDetails, }, }, + // Policy with direct feeds only FAKE_ID_2: { settings: { - oAuthAccountDetails: directFeeds, + companyCards: companyCardsDirectFeedSettings, + oAuthAccountDetails, }, }, + // Policy with custom feeds only FAKE_ID_3: { settings: { - companyCards: customFeeds, + companyCards: companyCardsCustomFeedSettings, }, }, }; @@ -196,19 +218,19 @@ describe('CardUtils', () => { }); describe('getCompanyFeeds', () => { - it('Should return both custom and direct feeds if exists', () => { + it('Should return both custom and direct feeds with filtered out "Expensify Card" bank', () => { const companyFeeds = CardUtils.getCompanyFeeds(cardFeedsCollection.FAKE_ID_1); - expect(companyFeeds).toStrictEqual({...directFeeds, ...customFeedsWithoutExpensifyBank}); + expect(companyFeeds).toStrictEqual(companyCardsSettingsWithoutExpensifyBank); }); it('Should return direct feeds only since custom feeds are not exist', () => { const companyFeeds = CardUtils.getCompanyFeeds(cardFeedsCollection.FAKE_ID_2); - expect(companyFeeds).toStrictEqual(directFeeds); + expect(companyFeeds).toStrictEqual(companyCardsDirectFeedSettings); }); - it('Should return custom feeds only since direct feeds are not exist', () => { + it('Should return custom feeds only with filtered out "Expensify Card" bank since direct feeds are not exist', () => { const companyFeeds = CardUtils.getCompanyFeeds(cardFeedsCollection.FAKE_ID_3); - expect(companyFeeds).toStrictEqual(customFeedsWithoutExpensifyBank); + expect(companyFeeds).toStrictEqual(companyCardsCustomFeedSettingsWithoutExpensifyBank); }); it('Should return empty object if undefined is passed', () => { @@ -217,23 +239,6 @@ describe('CardUtils', () => { }); }); - describe('removeExpensifyCardFromCompanyCards', () => { - it('Should return custom feeds without filtered out "Expensify Card" bank', () => { - const companyFeeds = CardUtils.removeExpensifyCardFromCompanyCards(cardFeedsCollection.FAKE_ID_3); - expect(companyFeeds).toStrictEqual(customFeedsWithoutExpensifyBank); - }); - - it('Should return direct feeds without any updates, since there were no "Expensify Card" bank', () => { - const companyFeeds = CardUtils.removeExpensifyCardFromCompanyCards(cardFeedsCollection.FAKE_ID_2); - expect(companyFeeds).toStrictEqual(directFeeds); - }); - - it('Should return empty object if undefined is passed', () => { - const companyFeeds = CardUtils.removeExpensifyCardFromCompanyCards(undefined); - expect(companyFeeds).toStrictEqual({}); - }); - }); - describe('getSelectedFeed', () => { it('Should return last selected custom feed', () => { const lastSelectedCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA; @@ -355,13 +360,13 @@ describe('CardUtils', () => { }); it('Should return filtered direct feed cards list with a single card', () => { - const cardsList = CardUtils.getFilteredCardList(directFeedCardsSingleList, directFeeds[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]); + const cardsList = CardUtils.getFilteredCardList(directFeedCardsSingleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]); // 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 = CardUtils.getFilteredCardList(directFeedCardsMultipleList, directFeeds[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]); + const cardsList = CardUtils.getFilteredCardList(directFeedCardsMultipleList, oAuthAccountDetails[CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE]); // eslint-disable-next-line @typescript-eslint/naming-convention expect(cardsList).toStrictEqual({'CREDIT CARD...1233': 'CREDIT CARD...1233', 'CREDIT CARD...3333': 'CREDIT CARD...3333', 'CREDIT CARD...7788': 'CREDIT CARD...7788'}); }); From 75299dcacb2bc92385df15b24a1066165fa2488c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 14:03:57 +0100 Subject: [PATCH 7/8] Lint fixes --- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 2 +- .../workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx | 2 +- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 53ef127193ed..6c6e70b8cc28 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -74,7 +74,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro !!policy?.connections?.xero?.config?.importTaxRates || !!policy?.connections?.netsuite?.options?.config?.syncOptions?.syncTax; const policyID = policy?.id; - const workspaceAccountID = policy?.workspaceAccountID ?? -1; + const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID; const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID.toString()}_${CONST.EXPENSIFY_CARD.BANK}`); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID.toString()}`); const [isOrganizeWarningModalOpen, setIsOrganizeWarningModalOpen] = useState(false); diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index b016c86c112a..fb49ece0feb0 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -58,7 +58,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag const card = allBankCards?.[cardID]; const cardBank = card?.bank ?? ''; - const cardholder = personalDetails?.[card?.accountID ?? -1]; + const cardholder = personalDetails?.[card?.accountID ?? CONST.DEFAULT_NUMBER_ID]; const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(cardholder); const exportMenuItem = getExportMenuItem(connectedIntegration, policyID, translate, policy, card); diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 55cc6e5e2e16..c53fe1fb2e7f 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -84,7 +84,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const ownerDetails = personalDetails?.[policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? ({} as PersonalDetails); const policyOwnerDisplayName = formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(ownerDetails)) ?? policy?.owner ?? ''; const hasMultipleFeeds = Object.values(CardUtils.getCompanyFeeds(cardFeeds)).filter((feed) => !feed.pending).length > 0; - const paymentAccountID = cardSettings?.paymentBankAccountID ?? 0; + const paymentAccountID = cardSettings?.paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID; useEffect(() => { CompanyCards.openPolicyCompanyCardsPage(policyID, workspaceAccountID); From d11c081836b588e49d98f67a1405d2d0a110aa81 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 23 Dec 2024 14:37:44 +0100 Subject: [PATCH 8/8] Buf fix --- src/libs/actions/CompanyCards.ts | 12 +++++------- .../WorkspaceCompanyCardsSettingsPage.tsx | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 4fba1e89be2f..d0e19398c257 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -228,13 +228,11 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu }, ]; - if (feedToOpen) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, - value: feedToOpen, - }); - } + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, + value: feedToOpen ?? null, + }); const parameters = { authToken, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx index 5abd95f7ad9c..60774c448546 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsSettingsPage.tsx @@ -56,7 +56,9 @@ function WorkspaceCompanyCardsSettingsPage({ if (selectedFeed) { const {cardList, ...cards} = cardsList ?? {}; const cardIDs = Object.keys(cards); - const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeed[]).filter((feed) => feed !== selectedFeed).at(0); + const feedToOpen = (Object.keys(companyFeeds) as CompanyCardFeed[]) + .filter((feed) => feed !== selectedFeed && companyFeeds[feed]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) + .at(0); CompanyCards.deleteWorkspaceCompanyCardFeed(policyID, workspaceAccountID, selectedFeed, cardIDs, feedToOpen); } setDeleteCompanyCardConfirmModalVisible(false);