diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 2e31ffa808b9..d56477c3f148 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -9,7 +9,7 @@ import type {TranslationPaths} from '@src/languages/types'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx'; -import type {CardFeedData} from '@src/types/onyx/CardFeeds'; +import type {CompanyCardNicknames, CompanyFeeds} from '@src/types/onyx/CardFeeds'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import localeCompare from './LocaleCompare'; @@ -243,10 +243,20 @@ function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD return Illustrations.AmexCompanyCards; } -function removeExpensifyCardFromCompanyCards(companyCards?: Record) { - if (!companyCards) { +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 { + return {...cardFeeds?.settings?.companyCards, ...cardFeeds?.settings?.oAuthAccountDetails}; +} + +function removeExpensifyCardFromCompanyCards(cardFeeds: OnyxEntry): CompanyFeeds { + if (!cardFeeds) { return {}; } + + const companyCards = getCompanyFeeds(cardFeeds); return Object.fromEntries(Object.entries(companyCards).filter(([key]) => key !== CONST.EXPENSIFY_CARD.BANK)); } @@ -283,6 +293,16 @@ const getBankCardDetailsImage = (bank: ValueOf return iconMap[bank]; }; +function getCustomOrFormattedFeedName(feed?: CompanyCardFeed, companyCardNicknames?: CompanyCardNicknames): string | undefined { + if (!feed) { + return; + } + + const customFeedName = companyCardNicknames?.[feed]; + const formattedFeedName = Localize.translateLocal('workspace.companyCards.feedName', {feedName: getCardFeedName(feed)}); + return customFeedName ?? formattedFeedName; +} + // We will simplify the logic below once we have #50450 #50451 implemented const getCorrectStepForSelectedBank = (selectedBank: ValueOf) => { const banksWithFeedType = [ @@ -316,8 +336,8 @@ const getCorrectStepForSelectedBank = (selectedBank: ValueOf, cardFeeds: OnyxEntry): CompanyCardFeed { - const defaultFeed = Object.keys(removeExpensifyCardFromCompanyCards(cardFeeds?.settings?.companyCards)).at(0) as CompanyCardFeed; +function getSelectedFeed(lastSelectedFeed: OnyxEntry, cardFeeds: OnyxEntry): CompanyCardFeed | undefined { + const defaultFeed = Object.keys(removeExpensifyCardFromCompanyCards(cardFeeds)).at(0) as CompanyCardFeed | undefined; return lastSelectedFeed ?? defaultFeed; } @@ -340,8 +360,11 @@ export { getCompanyCardNumber, getCardFeedIcon, getCardFeedName, + getCompanyFeeds, + isCustomFeed, getBankCardDetailsImage, getSelectedFeed, getCorrectStepForSelectedBank, + getCustomOrFormattedFeedName, removeExpensifyCardFromCompanyCards, }; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index fc9601424080..1c0895d2fbc6 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -814,7 +814,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { policyID: string; - bank: string; + bank: CompanyCardFeed; cardID: string; backTo?: Routes; }; diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 0c0ea1ada08a..4a102ab9bb72 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -10,6 +10,7 @@ import type { UpdateCompanyCardNameParams, } from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import * as CardUtils from '@libs/CardUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as NetworkStore from '@libs/Network/NetworkStore'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -121,19 +122,20 @@ function setWorkspaceCompanyCardFeedName(policyID: string, workspaceAccountID: n API.write(WRITE_COMMANDS.SET_COMPANY_CARD_FEED_NAME, parameters, onyxData); } -function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, policyID: string, bankName: string, liabilityType: string) { +function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, policyID: string, bankName: CompanyCardFeed, liabilityType: string) { const authToken = NetworkStore.getAuthToken(); + const isCustomFeed = CardUtils.isCustomFeed(bankName); + const feedUpdates = { + [bankName]: {liabilityType}, + }; + const onyxData: OnyxData = { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: { - companyCards: { - [bankName]: {liabilityType}, - }, - }, + settings: isCustomFeed ? {companyCards: feedUpdates} : {oAuthAccountDetails: feedUpdates}, }, }, ], @@ -149,8 +151,10 @@ function setWorkspaceCompanyCardTransactionLiability(workspaceAccountID: number, API.write(WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY, parameters, onyxData); } -function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: number, bankName: string) { +function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: number, bankName: CompanyCardFeed) { const authToken = NetworkStore.getAuthToken(); + const isCustomFeed = CardUtils.isCustomFeed(bankName); + const feedUpdates = {[bankName]: null}; const onyxData: OnyxData = { optimisticData: [ @@ -159,9 +163,7 @@ function deleteWorkspaceCompanyCardFeed(policyID: string, workspaceAccountID: nu key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { settings: { - companyCards: { - [bankName]: null, - }, + ...(isCustomFeed ? {companyCards: feedUpdates} : {oAuthAccountDetails: feedUpdates}), companyCardNicknames: { [bankName]: null, }, @@ -309,8 +311,12 @@ function unassignWorkspaceCompanyCard(workspaceAccountID: number, bankName: stri API.write(WRITE_COMMANDS.UNASSIGN_COMPANY_CARD, parameters, onyxData); } -function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, bankName: string) { +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}}}; + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -346,13 +352,7 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: { - companyCards: { - [bankName]: { - errors: null, - }, - }, - }, + settings: isCustomFeed ? {companyCards: optimisticFeedUpdates} : {oAuthAccountDetails: optimisticFeedUpdates}, }, }, ]; @@ -419,13 +419,7 @@ function updateWorkspaceCompanyCard(workspaceAccountID: number, cardID: string, onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`, value: { - settings: { - companyCards: { - [bankName]: { - errors: {error: CONST.COMPANY_CARDS.CONNECTION_ERROR}, - }, - }, - }, + settings: isCustomFeed ? {companyCards: failureFeedUpdates} : {oAuthAccountDetails: failureFeedUpdates}, }, }, ]; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 8d7acf236967..c5098ed08be7 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -20,6 +20,7 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {isConnectionInProgress} from '@libs/actions/connections'; +import * as CardUtils from '@libs/CardUtils'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; @@ -220,12 +221,14 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac } if (featureStates?.[CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]) { + const hasPolicyFeedsError = PolicyUtils.hasPolicyFeedsError(CardUtils.getCompanyFeeds(cardFeeds)); + protectedCollectPolicyMenuItems.push({ translationKey: 'workspace.common.companyCards', icon: Expensicons.CreditCard, action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.COMPANY_CARDS, - brickRoadIndicator: PolicyUtils.hasPolicyFeedsError(cardFeeds?.settings?.companyCards ?? {}) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: hasPolicyFeedsError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }); } diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 4eb1f752d176..805cda9a2eba 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -129,7 +129,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro subtitleTranslationKey: 'workspace.moreFeatures.companyCards.subtitle', isActive: policy?.areCompanyCardsEnabled ?? false, pendingAction: policy?.pendingFields?.areCompanyCardsEnabled, - disabled: !isEmptyObject(CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds?.settings?.companyCards)), + disabled: !isEmptyObject(CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds)), action: (isEnabled: boolean) => { if (!policyID) { return; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index 684ee0660d28..50eb27df790b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -41,15 +41,16 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const selectedFeed = CardUtils.getSelectedFeed(lastSelectedFeed, cardFeeds); - const availableCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds?.settings?.companyCards); + const companyFeeds = CardUtils.getCompanyFeeds(cardFeeds); + const availableCards = CardUtils.removeExpensifyCardFromCompanyCards(cardFeeds); const feeds: CardFeedListItem[] = (Object.keys(availableCards) as CompanyCardFeed[]).map((feed) => ({ value: feed, text: cardFeeds?.settings?.companyCardNicknames?.[feed] ?? CardUtils.getCardFeedName(feed), keyForList: feed, isSelected: feed === selectedFeed, - brickRoadIndicator: cardFeeds?.settings?.companyCards?.[feed]?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - canShowSeveralIndicators: !!cardFeeds?.settings?.companyCards?.[feed]?.errors, + brickRoadIndicator: companyFeeds[feed]?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + canShowSeveralIndicators: !!companyFeeds[feed]?.errors, leftElement: ( @@ -65,7 +64,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp {formattedFeedName} - {PolicyUtils.hasPolicyFeedsError(cardFeeds?.settings?.companyCards ?? {}, selectedFeed) && ( + {PolicyUtils.hasPolicyFeedsError(companyFeeds, selectedFeed) && (