diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 10da4ca095a1..df38084fc2a2 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -590,6 +590,9 @@ const ONYXKEYS = {
/** Currently displaying feed */
LAST_SELECTED_FEED: 'lastSelectedFeed_',
+ /** Currently displaying Expensify Card feed */
+ LAST_SELECTED_EXPENSIFY_CARD_FEED: 'lastSelectedExpensifyCardFeed_',
+
/** Whether the bank account chosen for Expensify Card in on verification waitlist */
NVP_EXPENSIFY_ON_CARD_WAITLIST: 'nvp_expensify_onCardWaitlist_',
@@ -946,6 +949,7 @@ type OnyxCollectionValuesMapping = {
[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_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/ROUTES.ts b/src/ROUTES.ts
index d8cd860f6a27..c5fc1db57e30 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -1645,6 +1645,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card/settings/account',
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/settings/account`, backTo),
},
+ WORKSPACE_EXPENSIFY_CARD_SELECT_FEED: {
+ route: 'settings/workspaces/:policyID/expensify-card/select-feed',
+ getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/select-feed`, backTo),
+ },
WORKSPACE_EXPENSIFY_CARD_SETTINGS_FREQUENCY: {
route: 'settings/workspaces/:policyID/expensify-card/settings/frequency',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings/frequency` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 093233e52bc2..d9d572fd8411 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -505,6 +505,7 @@ const SCREENS = {
EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit',
EXPENSIFY_CARD_ISSUE_NEW: 'Workspace_ExpensifyCard_New',
EXPENSIFY_CARD_NAME: 'Workspace_ExpensifyCard_Name',
+ EXPENSIFY_CARD_SELECT_FEED: 'Workspace_ExpensifyCard_Select_Feed',
EXPENSIFY_CARD_LIMIT_TYPE: 'Workspace_ExpensifyCard_LimitType',
EXPENSIFY_CARD_BANK_ACCOUNT: 'Workspace_ExpensifyCard_BankAccount',
EXPENSIFY_CARD_SETTINGS: 'Workspace_ExpensifyCard_Settings',
diff --git a/src/components/FeedSelector.tsx b/src/components/FeedSelector.tsx
new file mode 100644
index 000000000000..0caa235739e0
--- /dev/null
+++ b/src/components/FeedSelector.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import {View} from 'react-native';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
+import type IconAsset from '@src/types/utils/IconAsset';
+import CaretWrapper from './CaretWrapper';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+import {PressableWithFeedback} from './Pressable';
+import Text from './Text';
+
+type Props = {
+ /** Function to call when the feed is selected */
+ onFeedSelect: () => void;
+
+ /** Icon for the card */
+ cardIcon: IconAsset;
+
+ /** Whether to show assign card button */
+ shouldChangeLayout?: boolean;
+
+ /** Feed name */
+ feedName?: string;
+
+ /** Supporting text */
+ supportingText?: string;
+
+ /** Whether the RBR indicator should be shown */
+ shouldShowRBR?: boolean;
+};
+
+function FeedSelector({onFeedSelect, cardIcon, shouldChangeLayout, feedName, supportingText, shouldShowRBR = false}: Props) {
+ const styles = useThemeStyles();
+ const theme = useTheme();
+
+ return (
+
+
+
+
+
+ {feedName}
+
+ {shouldShowRBR && (
+
+ )}
+
+ {supportingText}
+
+
+ );
+}
+
+export default FeedSelector;
diff --git a/src/hooks/useDefaultFundID.ts b/src/hooks/useDefaultFundID.ts
new file mode 100644
index 000000000000..1515a473bfa9
--- /dev/null
+++ b/src/hooks/useDefaultFundID.ts
@@ -0,0 +1,37 @@
+import {useOnyx} from 'react-native-onyx';
+import {getFundIdFromSettingsKey} from '@libs/CardUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import useWorkspaceAccountID from './useWorkspaceAccountID';
+
+/**
+ * Hook to get the default fundID for a given policyID. This is used to get the settings and cards for each of the feeds.
+ * It will always return lastSelectedExpensifyCardFeed if it exists or fallback to the workspaceAccountID or domainFundID.
+ */
+function useDefaultFundID(policyID: string | undefined) {
+ const workspaceAccountID = useWorkspaceAccountID(policyID);
+ const [lastSelectedExpensifyCardFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED}${policyID}`);
+
+ const [domainFundID] = useOnyx(ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS, {
+ selector: (cardSettings) => {
+ const matchingKey = Object.entries(cardSettings ?? {}).find(
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ([key, settings]) => settings?.preferredPolicy && settings.preferredPolicy === policyID && !key.includes(workspaceAccountID.toString()),
+ );
+
+ return getFundIdFromSettingsKey(matchingKey?.[0] ?? '');
+ },
+ });
+
+ if (lastSelectedExpensifyCardFeed) {
+ return lastSelectedExpensifyCardFeed;
+ }
+
+ if (workspaceAccountID) {
+ return workspaceAccountID;
+ }
+
+ return domainFundID ?? CONST.DEFAULT_NUMBER_ID;
+}
+
+export default useDefaultFundID;
diff --git a/src/hooks/useDomainFundID.ts b/src/hooks/useDomainFundID.ts
deleted file mode 100644
index 29fe4fb6b3fa..000000000000
--- a/src/hooks/useDomainFundID.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import {useOnyx} from 'react-native-onyx';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-
-function useDomainFundID(policyID: string | undefined) {
- const [domainFundID] = useOnyx(ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS, {
- selector: (cardSettings) => {
- const matchingEntry = Object.entries(cardSettings ?? {}).find(
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- ([_, settings]) => settings?.preferredPolicy && settings.preferredPolicy === policyID,
- );
-
- if (!matchingEntry) {
- return CONST.DEFAULT_NUMBER_ID;
- }
-
- const key = matchingEntry[0];
- const prefix = ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS;
-
- if (!key.startsWith(prefix)) {
- return CONST.DEFAULT_NUMBER_ID;
- }
-
- const accountIDStr = key.substring(prefix.length);
-
- const accountID = Number(accountIDStr);
- return Number.isNaN(accountID) ? CONST.DEFAULT_NUMBER_ID : accountID;
- },
- });
-
- return domainFundID;
-}
-
-export default useDomainFundID;
diff --git a/src/hooks/useExpensifyCardFeeds.ts b/src/hooks/useExpensifyCardFeeds.ts
new file mode 100644
index 000000000000..bc3b113f344a
--- /dev/null
+++ b/src/hooks/useExpensifyCardFeeds.ts
@@ -0,0 +1,19 @@
+import {useOnyx} from 'react-native-onyx';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+function useExpensifyCardFeeds(policyID: string | undefined) {
+ const [allExpensifyCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS, {
+ selector: (cardSettings) => {
+ const matchingEntries = Object.entries(cardSettings ?? {}).filter(
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ([_, settings]) => settings?.preferredPolicy && settings.preferredPolicy === policyID,
+ );
+
+ return Object.fromEntries(matchingEntries);
+ },
+ });
+
+ return allExpensifyCardFeeds;
+}
+
+export default useExpensifyCardFeeds;
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index e5d3d9e9ddb6..faddf805f0f1 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -597,6 +597,17 @@ function isExpensifyCardFullySetUp(policy?: OnyxEntry, cardSettings?: On
return !!(policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID);
}
+function getFundIdFromSettingsKey(key: string) {
+ const prefix = ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS;
+ if (!key?.startsWith(prefix)) {
+ return CONST.DEFAULT_NUMBER_ID;
+ }
+ const fundIDStr = key.substring(prefix.length);
+
+ const fundID = Number(fundIDStr);
+ return Number.isNaN(fundID) ? CONST.DEFAULT_NUMBER_ID : fundID;
+}
+
export {
isExpensifyCard,
isCorporateCard,
@@ -641,4 +652,5 @@ export {
hasCardListObject,
isExpensifyCardFullySetUp,
filterInactiveCards,
+ getFundIdFromSettingsKey,
};
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 38d332677858..8f6b9948e13e 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -564,6 +564,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_FREQUENCY]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage').default,
+ [SCREENS.WORKSPACE.EXPENSIFY_CARD_SELECT_FEED]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardSelectorPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_NAME]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceEditCardNamePage').default,
diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
index 433e4dfa923b..c296848e03e5 100755
--- a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
+++ b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
@@ -237,6 +237,7 @@ const WORKSPACE_TO_RHP: Partial['config'] = {
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT.route,
},
+ [SCREENS.WORKSPACE.EXPENSIFY_CARD_SELECT_FEED]: {
+ path: ROUTES.WORKSPACE_EXPENSIFY_CARD_SELECT_FEED.route,
+ },
[SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS]: {
path: ROUTES.WORKSPACE_COMPANY_CARDS_SETTINGS.route,
},
@@ -1283,7 +1286,10 @@ const config: LinkingOptions['config'] = {
[SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: ROUTES.MONEY_REQUEST_STEP_WAYPOINT.route,
[SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route,
[SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route,
- [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: {path: ROUTES.MONEY_REQUEST_STATE_SELECTOR.route, exact: true},
+ [SCREENS.MONEY_REQUEST.STATE_SELECTOR]: {
+ path: ROUTES.MONEY_REQUEST_STATE_SELECTOR.route,
+ exact: true,
+ },
[SCREENS.MONEY_REQUEST.STEP_SPLIT_PAYER]: ROUTES.MONEY_REQUEST_STEP_SPLIT_PAYER.route,
[SCREENS.MONEY_REQUEST.STEP_ATTENDEES]: ROUTES.MONEY_REQUEST_ATTENDEE.route,
[SCREENS.MONEY_REQUEST.STEP_UPGRADE]: ROUTES.MONEY_REQUEST_UPGRADE.route,
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index 4cb585bf6fa0..a674306eb20d 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -334,11 +334,10 @@ function updateSettlementFrequency(workspaceAccountID: number, settlementFrequen
API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY, parameters, {optimisticData, successData, failureData});
}
-function updateSettlementAccount(workspaceAccountID: number, policyID: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) {
+function updateSettlementAccount(domainName: string, workspaceAccountID: number, policyID: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) {
if (!settlementBankAccountID) {
return;
}
- const domainName = PolicyUtils.getDomainNameForPolicy(policyID);
const optimisticData: OnyxUpdate[] = [
{
@@ -390,7 +389,12 @@ function getCardDefaultName(userName?: string) {
}
function setIssueNewCardStepAndData({data, isEditing, step, policyID}: IssueNewCardFlowData) {
- Onyx.merge(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {data, isEditing, currentStep: step, errors: null});
+ Onyx.merge(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {
+ data,
+ isEditing,
+ currentStep: step,
+ errors: null,
+ });
}
function clearIssueNewCardFlow(policyID: string | undefined) {
@@ -918,6 +922,20 @@ function updateSelectedFeed(feed: CompanyCardFeed, policyID: string | undefined)
]);
}
+function updateSelectedExpensifyCardFeed(feed: number, policyID: string | undefined) {
+ if (!policyID) {
+ return;
+ }
+
+ Onyx.update([
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED}${policyID}`,
+ value: feed,
+ },
+ ]);
+}
+
function queueExpensifyCardForBilling(feedCountry: string, domainAccountID: number) {
const parameters = {
feedCountry,
@@ -948,6 +966,7 @@ export {
toggleContinuousReconciliation,
updateExpensifyCardLimitType,
updateSelectedFeed,
+ updateSelectedExpensifyCardFeed,
deactivateCard,
getCardDefaultName,
queueExpensifyCardForBilling,
diff --git a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx
index acce8ea75bca..42da603378f6 100644
--- a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx
+++ b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx
@@ -5,13 +5,14 @@ import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
+import useDefaultFundID from '@hooks/useDefaultFundID';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
import {getConnectionNameFromRouteParam} from '@libs/AccountingUtils';
import {getLastFourDigits} from '@libs/BankAccountUtils';
import {getEligibleBankAccountsForCard} from '@libs/CardUtils';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
+import {getDomainNameForPolicy} from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
import {updateSettlementAccount} from '@userActions/Card';
@@ -29,17 +30,19 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting
const styles = useThemeStyles();
const {translate} = useLocalize();
- const workspaceAccountID = useWorkspaceAccountID(policyID);
const connectionName = getConnectionNameFromRouteParam(connection);
+ const defaultFundID = useDefaultFundID(policyID);
const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
- const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);
+ const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`);
const paymentBankAccountID = cardSettings?.paymentBankAccountID;
const selectedBankAccount = useMemo(() => bankAccountList?.[paymentBankAccountID?.toString() ?? ''], [paymentBankAccountID, bankAccountList]);
const bankAccountNumber = useMemo(() => selectedBankAccount?.accountData?.accountNumber ?? '', [selectedBankAccount]);
const settlementAccountEnding = getLastFourDigits(bankAccountNumber);
+ const domainName = cardSettings?.domainName ?? getDomainNameForPolicy(policyID);
+
const sections = useMemo(() => {
if (!bankAccountList || isEmptyObject(bankAccountList)) {
return [];
@@ -56,7 +59,7 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting
}, [bankAccountList, paymentBankAccountID]);
const selectBankAccount = (newBankAccountID?: number) => {
- updateSettlementAccount(workspaceAccountID, policyID, newBankAccountID, paymentBankAccountID);
+ updateSettlementAccount(domainName, defaultFundID, policyID, newBankAccountID, paymentBankAccountID);
Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING_CARD_RECONCILIATION.getRoute(policyID, connection));
};
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx
index 37786c6f578c..808b2ad67303 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx
@@ -2,10 +2,9 @@ import React from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
-import CaretWrapper from '@components/CaretWrapper';
+import FeedSelector from '@components/FeedSelector';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
-import {PressableWithFeedback} from '@components/Pressable';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
@@ -26,7 +25,6 @@ import {
isCustomFeed,
} from '@libs/CardUtils';
import Navigation from '@navigation/Navigation';
-import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {CompanyCardFeed} from '@src/types/onyx';
@@ -67,33 +65,14 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed, shouldS
return (
- Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))}
- style={[styles.flexRow, styles.alignItemsCenter, styles.gap3, shouldChangeLayout && styles.mb3]}
- accessibilityLabel={formattedFeedName ?? ''}
- >
-
-
-
-
- {formattedFeedName}
-
- {checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, workspaceAccountID), selectedFeed) && (
-
- )}
-
- {translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed')}
-
-
-
+ Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.getRoute(policyID))}
+ cardIcon={getCardFeedIcon(selectedFeed, illustrations)}
+ shouldChangeLayout={shouldChangeLayout}
+ feedName={formattedFeedName}
+ supportingText={translate(isCommercialFeed ? 'workspace.companyCards.commercialFeed' : 'workspace.companyCards.directFeed')}
+ shouldShowRBR={checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, workspaceAccountID), selectedFeed)}
+ />
{!!shouldShowAssignCardButton && (