From b3cf916a71ab23009bf6eea7ba1d027577db54a4 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 22 Dec 2025 13:57:04 -0500
Subject: [PATCH 001/122] refactor: Company Cards page and table

---
 src/hooks/useCompanyCards.ts                  | 35 ++++++++++++++--
 .../OpenPolicyCompanyCardsFeedParams.ts       |  4 +-
 src/libs/actions/CompanyCards.ts              | 38 ++++++++++-------
 .../WorkspaceCompanyCardPageEmptyState.tsx    | 10 +++--
 .../WorkspaceCompanyCardsPage.tsx             | 21 +++++-----
 .../WorkspaceCompanyCardsTable/index.tsx      | 41 ++++++++++---------
 6 files changed, 94 insertions(+), 55 deletions(-)

diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index d09fac5ef5b2..3defda99b8b9 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -6,6 +6,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
 import type {CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed, CombinedCardFeeds} from './useCardFeeds';
 import useCardsList from './useCardsList';
@@ -18,7 +19,7 @@ type UseCompanyCardsProps = {
     feedName?: CompanyCardFeedWithDomainID;
 };
 
-type UsCompanyCardsResult = Partial<{
+type UseCompanyCardsResult = Partial<{
     cardFeedType: CardFeedType;
     bankName: CompanyCardFeed;
     feedName: CompanyCardFeedWithDomainID;
@@ -29,6 +30,11 @@ type UsCompanyCardsResult = Partial<{
     companyCardFeeds: CompanyFeeds;
     selectedFeed: CombinedCardFeed;
 }> & {
+    isInitiallyLoadingFeeds: boolean;
+    isNoFeed: boolean;
+    isFeedPending: boolean;
+    isFeedAdded: boolean;
+
     onyxMetadata: {
         cardListMetadata: ResultMetadata<WorkspaceCardsList>;
         allCardFeedsMetadata: ResultMetadata<OnyxCollection<CardFeeds>>;
@@ -36,7 +42,7 @@ type UsCompanyCardsResult = Partial<{
     };
 };
 
-function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProps): UsCompanyCardsResult {
+function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProps): UseCompanyCardsResult {
     const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true});
     const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID);
 
@@ -63,11 +69,32 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
         lastSelectedFeedMetadata,
     };
 
+    const isInitiallyLoadingFeeds = isLoadingOnyxValue(allCardFeedsMetadata);
+    const isNoFeed = !selectedFeed && !isInitiallyLoadingFeeds;
+    const isFeedPending = !!selectedFeed?.pending;
+    const isFeedAdded = !isLoadingOnyxValue(allCardFeedsMetadata) && !isFeedPending && !isNoFeed;
+
     if (!policyID) {
-        return {onyxMetadata};
+        return {onyxMetadata, isInitiallyLoadingFeeds, isNoFeed, isFeedPending, isFeedAdded};
     }
 
-    return {allCardFeeds, feedName, companyCardFeeds, cardList, assignedCards, cardNames, selectedFeed, bankName, cardFeedType, onyxMetadata};
+    return {
+        allCardFeeds,
+        feedName,
+        companyCardFeeds,
+        cardList,
+        assignedCards,
+        cardNames,
+        selectedFeed,
+        bankName,
+        cardFeedType,
+        onyxMetadata,
+        isInitiallyLoadingFeeds,
+        isNoFeed,
+        isFeedPending,
+        isFeedAdded,
+    };
 }
 
 export default useCompanyCards;
+export type {UseCompanyCardsResult};
diff --git a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
index 85c881baf55b..739045ae61c2 100644
--- a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
+++ b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
@@ -1,7 +1,9 @@
+import type {CompanyCardFeed} from '@src/types/onyx';
+
 type OpenPolicyCompanyCardsFeedParams = {
     domainAccountID?: number;
     policyID: string;
-    feed: string;
+    feed: CompanyCardFeed;
 };
 
 export default OpenPolicyCompanyCardsFeedParams;
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 898230b9ab19..72a37717f57a 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -774,17 +774,7 @@ function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID
         },
     ];
 
-    const successData: OnyxUpdate[] = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
-            value: {
-                isLoading: false,
-            },
-        },
-    ];
-
-    const failureData: OnyxUpdate[] = [
+    const finallyData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
@@ -799,17 +789,35 @@ function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID
         authToken,
     };
 
-    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, successData, failureData});
+    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, finallyData});
 }
 
-function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, feed: CompanyCardFeed) {
+function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, bankName: CompanyCardFeed) {
     const parameters: OpenPolicyCompanyCardsFeedParams = {
         domainAccountID,
         policyID,
-        feed,
+        feed: bankName,
     };
 
-    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
+    const failureData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
+            value: {
+                settings: {
+                    companyCards: {
+                        [bankName]: {
+                            errors: {
+                                'Card feed fetch error': 'Could not fetch card feed due to an error',
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    ];
+
+    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {failureData});
 }
 
 function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx
index 059ea878a81f..d8ddcd6e0813 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx
@@ -7,6 +7,7 @@ import Text from '@components/Text';
 import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
+import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {hasIssuedExpensifyCard} from '@libs/CardUtils';
@@ -16,22 +17,23 @@ import {clearAddNewCardFlow} from '@userActions/CompanyCards';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
-import type {Policy} from '@src/types/onyx';
 import WorkspaceCompanyCardExpensifyCardPromotionBanner from './WorkspaceCompanyCardExpensifyCardPromotionBanner';
 
 type WorkspaceCompanyCardPageEmptyStateProps = {
-    policy: Policy | undefined;
+    policyID: string;
     shouldShowGBDisclaimer?: boolean;
 };
 
-function WorkspaceCompanyCardPageEmptyState({policy, shouldShowGBDisclaimer}: WorkspaceCompanyCardPageEmptyStateProps) {
+function WorkspaceCompanyCardPageEmptyState({policyID, shouldShowGBDisclaimer}: WorkspaceCompanyCardPageEmptyStateProps) {
     const {translate} = useLocalize();
     const styles = useThemeStyles();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
     const {isActingAsDelegate, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
     const [allWorkspaceCards] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true});
-    const shouldShowExpensifyCardPromotionBanner = !hasIssuedExpensifyCard(policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID, allWorkspaceCards);
+
+    const policy = usePolicy(policyID);
     const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
+    const shouldShowExpensifyCardPromotionBanner = !hasIssuedExpensifyCard(workspaceAccountID, allWorkspaceCards);
 
     const illustrations = useMemoizedLazyIllustrations(['CreditCardsNew', 'HandCard', 'MagnifyingGlassMoney', 'CompanyCardsEmptyState']);
 
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
index 2902df3e3741..8970fa1d1b50 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
@@ -1,7 +1,6 @@
 import React, {useEffect, useState} from 'react';
 import DecisionModal from '@components/DecisionModal';
 import useAssignCard from '@hooks/useAssignCard';
-import useCardsList from '@hooks/useCardsList';
 import useCompanyCards from '@hooks/useCompanyCards';
 import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
@@ -33,30 +32,27 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
     const policy = usePolicy(policyID);
     const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
 
+    const companyCards = useCompanyCards({policyID});
     const {
         allCardFeeds,
         feedName,
         selectedFeed,
         bankName,
-        onyxMetadata: {allCardFeedsMetadata},
-    } = useCompanyCards({policyID});
-    const [, cardsListMetadata] = useCardsList(feedName);
+        isFeedPending,
+        isFeedAdded,
+        onyxMetadata: {cardListMetadata},
+    } = companyCards;
 
-    const isInitiallyLoadingFeeds = isLoadingOnyxValue(allCardFeedsMetadata);
-    const isNoFeed = !selectedFeed && !isInitiallyLoadingFeeds;
-    const isFeedPending = !!selectedFeed?.pending;
-    const isFeedAdded = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed;
-    const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
     const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed);
     const {isOffline} = useNetwork({
         onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID),
     });
 
+    const isLoading = !isOffline && (!allCardFeeds || (isFeedAdded && isLoadingOnyxValue(cardListMetadata)));
     useEffect(() => {
         openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID);
     }, [policyID, domainOrWorkspaceAccountID]);
 
-    const isLoading = !isOffline && (!allCardFeeds || (isFeedAdded && isLoadingOnyxValue(cardsListMetadata)));
     useEffect(() => {
         if (isLoading || !bankName || isFeedPending) {
             return;
@@ -67,6 +63,7 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
         openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, bankName);
     }, [bankName, isLoading, policyID, isFeedPending, domainOrWorkspaceAccountID, policy?.employeeList]);
 
+    const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
     const {assignCard, isAssigningCardDisabled} = useAssignCard({feedName, policyID, setShouldShowOfflineModal});
 
     return (
@@ -83,7 +80,9 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
                 addBottomSafeAreaPadding
             >
                 <WorkspaceCompanyCardsTable
-                    policy={policy}
+                    policyID={policyID}
+                    domainOrWorkspaceAccountID={domainOrWorkspaceAccountID}
+                    companyCards={companyCards}
                     onAssignCard={assignCard}
                     isAssigningCardDisabled={isAssigningCardDisabled}
                 />
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index 5b5e2a4a1b23..b54ecb7203ea 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -6,21 +6,21 @@ import ScrollView from '@components/ScrollView';
 import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton';
 import Table from '@components/Table';
 import type {ActiveSorting, CompareItemsCallback, FilterConfig, IsItemInFilterCallback, IsItemInSearchCallback, TableColumn, TableHandle} from '@components/Table';
-import useCompanyCards from '@hooks/useCompanyCards';
+import type {UseCompanyCardsResult} from '@hooks/useCompanyCards';
 import useLocalize from '@hooks/useLocalize';
 import useNetwork from '@hooks/useNetwork';
 import useOnyx from '@hooks/useOnyx';
 import usePermissions from '@hooks/usePermissions';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {getDomainOrWorkspaceAccountID, isMaskedCardNumberEqual} from '@libs/CardUtils';
+import {isMaskedCardNumberEqual} from '@libs/CardUtils';
 import WorkspaceCompanyCardPageEmptyState from '@pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState';
 import WorkspaceCompanyCardsFeedAddedEmptyPage from '@pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage';
 import WorkspaceCompanyCardsFeedPendingPage from '@pages/workspace/companyCards/WorkspaceCompanyCardsFeedPendingPage';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, Policy} from '@src/types/onyx';
+import type {Card} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import WorkspaceCompanyCardsTableHeaderButtons from './WorkspaceCompanyCardsTableHeaderButtons';
 import WorkspaceCompanyCardTableItem from './WorkspaceCompanyCardsTableItem';
@@ -29,8 +29,14 @@ import type {WorkspaceCompanyCardTableItemData} from './WorkspaceCompanyCardsTab
 type CompanyCardsTableColumnKey = 'member' | 'card' | 'customCardName';
 
 type WorkspaceCompanyCardsTableProps = {
-    /** Current policy */
-    policy: Policy | undefined;
+    /** Policy ID */
+    policyID: string;
+
+    /** Domain or workspace account ID */
+    domainOrWorkspaceAccountID: number;
+
+    /** Company cards */
+    companyCards: UseCompanyCardsResult;
 
     /** On assign card callback */
     onAssignCard: (cardID: string) => void;
@@ -39,7 +45,7 @@ type WorkspaceCompanyCardsTableProps = {
     isAssigningCardDisabled: boolean;
 };
 
-function WorkspaceCompanyCardsTable({policy, onAssignCard, isAssigningCardDisabled}: WorkspaceCompanyCardsTableProps) {
+function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, companyCards, onAssignCard, isAssigningCardDisabled}: WorkspaceCompanyCardsTableProps) {
     const styles = useThemeStyles();
     const {isOffline} = useNetwork();
     const {translate, localeCompare} = useLocalize();
@@ -53,27 +59,22 @@ function WorkspaceCompanyCardsTable({policy, onAssignCard, isAssigningCardDisabl
         cardNames,
         cardFeedType,
         selectedFeed,
-        onyxMetadata: {cardListMetadata, lastSelectedFeedMetadata, allCardFeedsMetadata},
-    } = useCompanyCards({policyID: policy?.id});
+        isInitiallyLoadingFeeds,
+        isNoFeed,
+        isFeedPending,
+        onyxMetadata: {cardListMetadata, lastSelectedFeedMetadata},
+    } = companyCards;
     const isDirectCardFeed = cardFeedType === 'directFeed';
 
-    const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
-    const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed);
     const [countryByIp] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: false});
     const [personalDetails, personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
     const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true});
     const [failedCompanyCardAssignments] = useOnyx(`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feedName ?? ''}`, {canBeMissing: true});
 
     const hasNoAssignedCard = Object.keys(assignedCards ?? {}).length === 0;
-    const isInitiallyLoadingFeeds = isLoadingOnyxValue(allCardFeedsMetadata);
-
-    const isNoFeed = !selectedFeed && !isInitiallyLoadingFeeds;
-    const isFeedPending = !!selectedFeed?.pending;
-    const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || policy?.id === undefined || isLoadingOnyxValue(lastSelectedFeedMetadata);
-
+    const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || isLoadingOnyxValue(lastSelectedFeedMetadata);
     const isLoadingCards = cardFeedType === 'directFeed' ? selectedFeed?.accountList === undefined : isLoadingOnyxValue(cardListMetadata) || cardList === undefined;
     const isLoadingPage = !isOffline && (isLoadingFeed || isLoadingOnyxValue(personalDetailsMetadata));
-
     const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed;
     const showTableControls = showCards && !!selectedFeed && !isFeedPending;
 
@@ -224,7 +225,7 @@ function WorkspaceCompanyCardsTable({policy, onAssignCard, isAssigningCardDisabl
         <WorkspaceCompanyCardTableItem
             key={`${item.cardName}_${index}`}
             item={item}
-            policyID={policy?.id ?? String(CONST.DEFAULT_NUMBER_ID)}
+            policyID={policyID ?? String(CONST.DEFAULT_NUMBER_ID)}
             feed={feedName}
             domainOrWorkspaceAccountID={domainOrWorkspaceAccountID}
             CardFeedIcon={cardFeedIcon}
@@ -279,7 +280,7 @@ function WorkspaceCompanyCardsTable({policy, onAssignCard, isAssigningCardDisabl
                 <View style={shouldUseNarrowTableLayout && styles.mb5}>
                     <WorkspaceCompanyCardsTableHeaderButtons
                         isLoading={isLoadingPage}
-                        policyID={policy?.id}
+                        policyID={policyID}
                         feedName={feedName}
                         showTableControls={showTableControls}
                         CardFeedIcon={cardFeedIcon}
@@ -300,7 +301,7 @@ function WorkspaceCompanyCardsTable({policy, onAssignCard, isAssigningCardDisabl
                     {isNoFeed && (
                         <View style={styles.flex1}>
                             <WorkspaceCompanyCardPageEmptyState
-                                policy={policy}
+                                policyID={policyID}
                                 shouldShowGBDisclaimer={shouldShowGBDisclaimer}
                             />
                         </View>

From 8be9d254dc6951c9c1b9c911be440917375f9865 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 17:11:28 +0000
Subject: [PATCH 002/122] fix: error getting `lastSelectedFeed` with empty
 policyID

---
 src/CONST/index.ts           | 2 ++
 src/hooks/useCompanyCards.ts | 8 ++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 9780d4cb607a..c9582b4971a7 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -24,6 +24,7 @@ const EMPTY_OBJECT = Object.freeze({});
 const MONTH_DAYS = Object.freeze([...Array(28).keys()].map((i) => i + 1));
 
 const DEFAULT_NUMBER_ID = 0;
+const DEFAULT_MISSING_ID = -1;
 const DEFAULT_COUNTRY_CODE = 1;
 const CLOUDFRONT_DOMAIN = 'cloudfront.net';
 const CLOUDFRONT_URL = `https://d2k5nsl2zxldvw.${CLOUDFRONT_DOMAIN}`;
@@ -971,6 +972,7 @@ const CONST = {
     EMPTY_ARRAY,
     EMPTY_OBJECT,
     DEFAULT_NUMBER_ID,
+    DEFAULT_MISSING_ID,
     DEFAULT_COUNTRY_CODE,
     FAKE_REPORT_ID: 'FAKE_REPORT_ID',
     USE_EXPENSIFY_URL,
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 3defda99b8b9..4c9b11d16696 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -1,7 +1,7 @@
 import type {OnyxCollection, OnyxEntry, ResultMetadata} from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import {getCompanyCardFeed, getCompanyFeeds, getPlaidInstitutionId, getSelectedFeed} from '@libs/CardUtils';
-import type CONST from '@src/CONST';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
@@ -43,7 +43,11 @@ type UseCompanyCardsResult = Partial<{
 };
 
 function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProps): UseCompanyCardsResult {
-    const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, {canBeMissing: true});
+    // If an empty string is passed, we need to use an invalid key to avoid fetching the whole collection.
+    // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+    const policyIDKey = policyID || CONST.DEFAULT_MISSING_ID;
+
+    const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyIDKey}`, {canBeMissing: true});
     const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID);
 
     const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);

From 3d400a489801e5bef91a08b1fe0c0f2e58e7ed98 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 17:11:46 +0000
Subject: [PATCH 003/122] refactor: extract broken card feed connection util
 function

---
 src/libs/CardUtils.ts | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index ab13eca190c3..904cd2132509 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -733,6 +733,10 @@ function flatAllCardsList(allCardsList: OnyxCollection<WorkspaceCardsList>, work
     }, {});
 }
 
+function isCardWithBrokenConnection(card: Card): boolean {
+    return !!card.lastScrapeResult && !CONST.COMPANY_CARDS.BROKEN_CONNECTION_IGNORED_STATUSES.includes(card.lastScrapeResult);
+}
+
 /**
  * Check if any card from the provided feed(s) has a broken connection
  *
@@ -744,9 +748,7 @@ function checkIfFeedConnectionIsBroken(feedCards: Record<string, Card> | undefin
         return false;
     }
 
-    return Object.values(feedCards).some(
-        (card) => !isEmptyObject(card) && card.bank !== feedToExclude && card.lastScrapeResult && !CONST.COMPANY_CARDS.BROKEN_CONNECTION_IGNORED_STATUSES.includes(card.lastScrapeResult),
-    );
+    return Object.values(feedCards).some((card) => !isEmptyObject(card) && card.bank !== feedToExclude && isCardWithBrokenConnection(card));
 }
 
 /**
@@ -935,6 +937,7 @@ export {
     isCardHiddenFromSearch,
     getFeedType,
     flatAllCardsList,
+    isCardWithBrokenConnection,
     checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,

From 67220e1282d9b853088b5b03d97a1a28c28cd68b Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 17:12:41 +0000
Subject: [PATCH 004/122] fix: missing error message in WalletPage

Partially based on changes by @narefyev91 in https://github.com/Expensify/App/pull/76620
---
 .../settings/Wallet/PaymentMethodList.tsx     | 43 +++++++++++--------
 .../settings/Wallet/PaymentMethodListItem.tsx |  2 +-
 2 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 49b69e5161c0..3ff51994b6d7 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -31,11 +31,12 @@ import {
 import Navigation from '@libs/Navigation/Navigation';
 import {formatPaymentMethods} from '@libs/PaymentUtils';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
+import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
-import type {BankAccount, BankAccountList, Card, CardList, CompanyCardFeed} from '@src/types/onyx';
+import type {BankAccount, BankAccountList, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
 import type PaymentMethod from '@src/types/onyx/PaymentMethod';
 import {getEmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject';
 import type IconAsset from '@src/types/utils/IconAsset';
@@ -179,18 +180,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const getCardBrickRoadIndicator = useCallback(
-        (card: Card) => {
-            if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
-                return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
-            }
-            if (isExpensifyCardPendingAction(card, privatePersonalDetails)) {
-                return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
-            }
-            return undefined;
-        },
-        [privatePersonalDetails],
-    );
+    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
@@ -205,6 +195,17 @@ function PaymentMethodList({
                 const isDisabled = card.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
                 const icon = getCardFeedIcon(card.bank as CompanyCardFeed, illustrations, companyCardFeedIcons);
 
+                let brickRoadIndicator: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;
+
+                const cardFeedErrors = getCardFeedErrors(card.bank as CompanyCardFeedWithDomainID);
+                if (cardFeedErrors.shouldShowRBR) {
+                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                } else if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
+                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                } else if (isExpensifyCardPendingAction(card, privatePersonalDetails)) {
+                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+                }
+
                 if (!isExpensifyCard(card)) {
                     const pressHandler = onPress as CardPressHandler;
                     const lastFourPAN = lastFourNumbersFromCardName(card.cardName);
@@ -222,10 +223,7 @@ function PaymentMethodList({
                         shouldShowRightIcon,
                         errors: card.errors,
                         pendingAction: card.pendingAction,
-                        brickRoadIndicator:
-                            card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors
-                                ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR
-                                : undefined,
+                        brickRoadIndicator,
                         icon,
                         iconStyles: [styles.cardIcon],
                         iconWidth: variables.cardIconWidth,
@@ -270,6 +268,12 @@ function PaymentMethodList({
 
                 const pressHandler = onPress as CardPressHandler;
 
+                if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
+                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                } else if (isExpensifyCardPendingAction(card, privatePersonalDetails)) {
+                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+                }
+
                 // The card shouldn't be grouped or it's domain group doesn't exist yet
                 const cardDescription =
                     card?.nameValuePairs?.issuedBy && card?.lastFourPAN
@@ -301,7 +305,7 @@ function PaymentMethodList({
                     canDismissError: true,
                     errors: card.errors,
                     pendingAction: card.pendingAction,
-                    brickRoadIndicator: getCardBrickRoadIndicator(card),
+                    brickRoadIndicator,
                     icon,
                     iconStyles: [styles.cardIcon],
                     iconWidth: variables.cardIconWidth,
@@ -386,8 +390,9 @@ function PaymentMethodList({
         cardList,
         illustrations,
         companyCardFeedIcons,
+        getCardFeedErrors,
+        privatePersonalDetails,
         onPress,
-        getCardBrickRoadIndicator,
         shouldShowRightIcon,
         itemIconRight,
         expensifyIcons.ThreeDots,
diff --git a/src/pages/settings/Wallet/PaymentMethodListItem.tsx b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
index bca259a45b00..e2c5c38884c1 100644
--- a/src/pages/settings/Wallet/PaymentMethodListItem.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
@@ -117,7 +117,7 @@ function PaymentMethodListItem({item, shouldShowDefaultBadge, threeDotsMenuItems
             pendingAction={item.pendingAction}
             errors={item.errors}
             errorRowStyles={styles.ph6}
-            shouldShowErrorMessages={false}
+            shouldShowErrorMessages={!!item.errors}
         >
             <MenuItem
                 onPress={handleRowPress}

From acd33b61c70f2ab26de4b9425b496ba92846f4aa Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 18:47:08 +0000
Subject: [PATCH 005/122] refactor: re-structure card utils and
 `useCardFeedErrors`

---
 src/libs/CardUtils.ts                         | 58 ++++++++++++++-----
 .../settings/Wallet/PaymentMethodList.tsx     |  4 +-
 .../WorkspaceCompanyCardFeedSelectorPage.tsx  |  4 +-
 ...orkspaceCompanyCardsTableHeaderButtons.tsx |  4 +-
 .../companyCards/hooks/useCardFeedErrors.ts   | 26 +++++----
 .../hooks/useHasWorkspaceCompanyCardErrors.ts |  4 +-
 tests/unit/CardUtilsTest.ts                   |  6 +-
 7 files changed, 71 insertions(+), 35 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 904cd2132509..3f72f9e18026 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -642,11 +642,20 @@ function checkIfNewFeedConnected(prevFeedsData: CompanyFeeds, currentFeedsData:
     };
 }
 
-function filterInactiveCards(cardsList: WorkspaceCardsList | undefined) {
-    const {cardList, ...assignedCards} = cardsList ?? {};
+function filterInactiveCards(cards: CardList | undefined) {
+    if (!cards) {
+        return {};
+    }
 
     const closedStates = new Set<number>([CONST.EXPENSIFY_CARD.STATE.CLOSED, CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED, CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED]);
-    const filteredAssignedCards = filterObject(assignedCards, (_key, card) => !closedStates.has(card.state));
+    const filteredCards = filterObject(cards, (_key, card) => !closedStates.has(card.state));
+
+    return filteredCards;
+}
+
+function filterInactiveWorkspaceCards(cardsList: WorkspaceCardsList | undefined) {
+    const {cardList, ...assignedCards} = cardsList ?? {};
+    const filteredAssignedCards = filterInactiveCards(assignedCards);
 
     return {
         ...(cardList ? {cardList} : {}),
@@ -671,7 +680,7 @@ function getAllCardsForWorkspace(
         const isExpensifyDomainCards = expensifyCardsDomainIDs.some((domainID) => key.includes(domainID.toString()) && key.includes(CONST.EXPENSIFY_CARD.BANK));
         if ((isWorkspaceAccountCards || isCompanyDomainCards || isExpensifyDomainCards) && values) {
             const {cardList: assignableCards, ...assignedCards} = values ?? {};
-            const filteredCards = filterInactiveCards(assignedCards);
+            const filteredCards = filterInactiveWorkspaceCards(assignedCards);
             Object.assign(cards, filteredCards);
         }
     }
@@ -708,6 +717,20 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry<CombinedCard
     return feedKey;
 }
 
+/**
+ * Filter out the Expensify cards from the list of cards
+ *
+ * @param cards the list of cards to filter
+ * @returns the list of cards without Expensify cards
+ */
+function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
+    if (!cards) {
+        return {};
+    }
+
+    return Object.fromEntries(Object.entries(cards).filter(([key]) => !key.includes(CONST.EXPENSIFY_CARD.BANK)));
+}
+
 /**
  * Takes the list of cards divided by workspaces and feeds and returns the flattened non-Expensify cards related to the provided workspace
  *
@@ -715,24 +738,29 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry<CombinedCard
  * @param workspaceAccountID the workspace account id we want to get cards for
  * @param domainIDs the domain ids we want to get cards for
  */
-function flatAllCardsList(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number, domainIDs?: number[]): Record<string, Card> | undefined {
+function flatCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
     if (!allCardsList) {
-        return;
+        return {} as CardList;
     }
 
     return Object.entries(allCardsList).reduce((acc, [key, cards]) => {
-        const isWorkspaceAccountCards = key.includes(workspaceAccountID.toString());
-        const isDomainCards = domainIDs?.some((domainID) => key.includes(domainID.toString()));
-        if ((!isWorkspaceAccountCards && !isDomainCards) || key.includes(CONST.EXPENSIFY_CARD.BANK)) {
+        const isWorkspaceAccountCard = key.includes(workspaceAccountID.toString());
+        if (!isWorkspaceAccountCard || key.includes(CONST.EXPENSIFY_CARD.BANK)) {
             return acc;
         }
         const {cardList, ...feedCards} = cards ?? {};
-        const filteredCards = filterInactiveCards(feedCards);
+        const filteredCards = filterInactiveWorkspaceCards(feedCards);
         Object.assign(acc, filteredCards);
         return acc;
     }, {});
 }
 
+/**
+ * Check if the card has a broken connection
+ *
+ * @param card the card to check
+ * @returns true if the card has a broken connection, false otherwise
+ */
 function isCardWithBrokenConnection(card: Card): boolean {
     return !!card.lastScrapeResult && !CONST.COMPANY_CARDS.BROKEN_CONNECTION_IGNORED_STATUSES.includes(card.lastScrapeResult);
 }
@@ -743,7 +771,7 @@ function isCardWithBrokenConnection(card: Card): boolean {
  * @param feedCards the list of the cards, related to one or several feeds
  * @param [feedToExclude] the feed to ignore during the check, it's useful for checking broken connection error only in the feeds other than the selected one
  */
-function checkIfFeedConnectionIsBroken(feedCards: Record<string, Card> | undefined, feedToExclude?: string): boolean {
+function checkIfFeedConnectionIsBroken(feedCards: CardList | undefined, feedToExclude?: string): boolean {
     if (!feedCards || isEmptyObject(feedCards)) {
         return false;
     }
@@ -820,7 +848,7 @@ function getFundIdFromSettingsKey(key: string) {
  * @param feedCards the list of the cards, related to one or several feeds
  * @param [feedToExclude] the feed to ignore during the check, it's useful for checking broken connection error only in the feeds other than the selected one
  */
-function getFeedConnectionBrokenCard(feedCards: Record<string, Card> | undefined, feedToExclude?: string): Card | undefined {
+function getFeedConnectionBrokenCard(feedCards: CardList | undefined, feedToExclude?: string): Card | undefined {
     if (!feedCards || isEmptyObject(feedCards)) {
         return undefined;
     }
@@ -933,17 +961,19 @@ export {
     getDomainOrWorkspaceAccountID,
     mergeCardListWithWorkspaceFeeds,
     isCard,
+    filterCardsByNonExpensify,
     getAllCardsForWorkspace,
     isCardHiddenFromSearch,
     getFeedType,
-    flatAllCardsList,
+    flatCompanyCards,
     isCardWithBrokenConnection,
     checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
     hasIssuedExpensifyCard,
     isExpensifyCardFullySetUp,
-    filterInactiveCards,
+    filterInactiveCards as filterAllInactiveCards,
+    filterInactiveWorkspaceCards as filterInactiveCards,
     isCardPendingIssue,
     isCardPendingActivate,
     hasPendingExpensifyCardAction,
diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 3ff51994b6d7..cba3927243b5 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -31,7 +31,7 @@ import {
 import Navigation from '@libs/Navigation/Navigation';
 import {formatPaymentMethods} from '@libs/PaymentUtils';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
-import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
+import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -180,7 +180,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
+    const {getCardFeedErrors} = useCardFeedErrors({policyID});
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 242a49502ee5..89491e72de89 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -31,7 +31,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {CompanyCardFeed} from '@src/types/onyx';
-import useCompanyCardFeedErrors from './hooks/useCardFeedErrors';
+import useCardFeedErrors from './hooks/useCardFeedErrors';
 
 type CardFeedListItem = ListItem & {
     /** Combined feed key */
@@ -56,7 +56,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
     const icons = useMemoizedLazyExpensifyIcons(['Plus']);
 
     const {companyCardFeeds, feedName: selectedFeedName} = useCompanyCards({policyID});
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID, feedName: selectedFeedName});
+    const {getCardFeedErrors} = useCardFeedErrors({policyID, feedName: selectedFeedName});
 
     const feeds: CardFeedListItem[] = (Object.entries(companyCardFeeds ?? {}) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([feedName, feedSettings]) => {
         const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed);
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 7730add244a5..c58f147eebd2 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -19,7 +19,7 @@ import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
-import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
+import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
 import useHasWorkspaceCompanyCardErrors from '@pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -63,7 +63,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
     const currentFeedData = feedName ? companyFeeds?.[feedName] : undefined;
     const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`, {canBeMissing: true});
 
-    const {hasFeedError, isFeedConnectionBroken} = useCompanyCardFeedErrors({policyID, feedName});
+    const {hasFeedError, isFeedConnectionBroken} = useCardFeedErrors({policyID, feedName});
     const hasCompanyCardFeedError = useHasWorkspaceCompanyCardErrors({policyID});
 
     const openBankConnection = () => {
diff --git a/src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts b/src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts
index 24cdcb317575..46a4f93147d9 100644
--- a/src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts
+++ b/src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts
@@ -2,34 +2,38 @@ import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
 import useCompanyCards from '@hooks/useCompanyCards';
 import useOnyx from '@hooks/useOnyx';
 import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
-import {checkIfFeedConnectionIsBroken, flatAllCardsList, getCompanyCardFeed, getDomainOrWorkspaceAccountID} from '@libs/CardUtils';
+import {checkIfFeedConnectionIsBroken, filterAllInactiveCards, filterCardsByNonExpensify, getCompanyCardFeed, getDomainOrWorkspaceAccountID} from '@libs/CardUtils';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
-type UseCompanyCardFeedErrorsProps = {
+type UseCardFeedErrorsProps = {
     policyID: string | undefined;
     feedName?: CompanyCardFeedWithDomainID;
+    shouldExcludeExpensifyCards?: boolean;
 };
 
-type CompanyCardFeedErrors = {
+type CardFeedErrors = {
     shouldShowRBR: boolean;
     hasFailedCardAssignments: boolean;
     hasFeedError: boolean;
     isFeedConnectionBroken: boolean;
 };
 
-type UseCompanyCardFeedErrorsResult = CompanyCardFeedErrors & {
-    getCardFeedErrors: (feedName: CompanyCardFeedWithDomainID) => CompanyCardFeedErrors;
+type UseCompanyCardFeedErrorsResult = CardFeedErrors & {
+    getCardFeedErrors: (feedName: CompanyCardFeedWithDomainID) => CardFeedErrors;
 };
 
-function useCompanyCardFeedErrors({policyID, feedName}: UseCompanyCardFeedErrorsProps): UseCompanyCardFeedErrorsResult {
+function useCardFeedErrors({policyID, feedName, shouldExcludeExpensifyCards = false}: UseCardFeedErrorsProps): UseCompanyCardFeedErrorsResult {
     const workspaceAccountID = useWorkspaceAccountID(policyID);
 
     const {companyCardFeeds} = useCompanyCards({policyID, feedName});
-    const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: false});
+    const [allCards] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
+    const filteredCards = filterAllInactiveCards(shouldExcludeExpensifyCards ? filterCardsByNonExpensify(allCards) : allCards);
+
     const [failedCompanyCardAssignmentsPerFeed] = useOnyx(ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, {canBeMissing: true});
 
-    const getCardFeedErrors = (feedNameFn: CompanyCardFeedWithDomainID | undefined): CompanyCardFeedErrors => {
+    const getCardFeedErrors = (feedNameFn: CompanyCardFeedWithDomainID | undefined): CardFeedErrors => {
         const bankName = getCompanyCardFeed(feedNameFn);
         const selectedFeed = bankName && companyCardFeeds?.[bankName];
         const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed);
@@ -38,7 +42,9 @@ function useCompanyCardFeedErrors({policyID, feedName}: UseCompanyCardFeedErrors
             failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feedNameFn ?? ''}`],
         );
         const hasFeedError = feedNameFn ? !!selectedFeed?.errors : false;
-        const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(flatAllCardsList(allFeedsCards, domainOrWorkspaceAccountID), feedNameFn);
+
+        const feedToExclude = shouldExcludeExpensifyCards ? CONST.EXPENSIFY_CARD.BANK : undefined;
+        const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredCards, feedToExclude);
 
         const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 
@@ -56,4 +62,4 @@ function useCompanyCardFeedErrors({policyID, feedName}: UseCompanyCardFeedErrors
     };
 }
 
-export default useCompanyCardFeedErrors;
+export default useCardFeedErrors;
diff --git a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
index e1f5546ea9ed..408e4bb7859c 100644
--- a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
+++ b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
@@ -1,6 +1,6 @@
 import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
 import useCompanyCards from '@hooks/useCompanyCards';
-import useCompanyCardFeedErrors from './useCardFeedErrors';
+import useCardFeedErrors from './useCardFeedErrors';
 
 type UseHasWorkspaceCompanyCardErrorsProps = {
     policyID: string | undefined;
@@ -9,7 +9,7 @@ type UseHasWorkspaceCompanyCardErrorsProps = {
 function useHasWorkspaceCompanyCardErrors({policyID}: UseHasWorkspaceCompanyCardErrorsProps): boolean {
     const {companyCardFeeds} = useCompanyCards({policyID});
     const companyCardFeedNames = Object.keys(companyCardFeeds ?? {}) as CompanyCardFeedWithDomainID[];
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
+    const {getCardFeedErrors} = useCardFeedErrors({policyID});
     const hasCompanyCardFeedError = companyCardFeedNames.some((feed) => getCardFeedErrors(feed).shouldShowRBR);
     return hasCompanyCardFeedError;
 }
diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 120d123ad42f..650c5790b2f6 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -9,7 +9,7 @@ import IntlStore from '@src/languages/IntlStore';
 import {
     checkIfFeedConnectionIsBroken,
     filterInactiveCards,
-    flatAllCardsList,
+    flatCompanyCards,
     formatCardExpiration,
     getAllCardsForWorkspace,
     getAssignedCardSortKey,
@@ -872,7 +872,7 @@ describe('CardUtils', () => {
     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 flattenedCardsList = flatCompanyCards(allCardsList, workspaceAccountID);
             const {cardList, ...customCards} = customFeedCardsList;
             expect(flattenedCardsList).toStrictEqual({
                 ...directFeedCardsMultipleList,
@@ -882,7 +882,7 @@ describe('CardUtils', () => {
 
         it('should return undefined if not defined cards list was provided', () => {
             const workspaceAccountID = 11111111;
-            const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID);
+            const flattenedCardsList = flatCompanyCards(undefined, workspaceAccountID);
             expect(flattenedCardsList).toBeUndefined();
         });
     });

From b62479bc32ecef0b8b0bc5075c7ee58f1a271600 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 18:47:21 +0000
Subject: [PATCH 006/122] refactor: remove duplicate type definition

---
 src/libs/CardFeedUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 6aa9a7d1fad7..0f86467f4e25 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -130,7 +130,7 @@ function buildCardsData(
     const allWorkspaceCards: CardFilterItem[] = Object.values(workspaceCardFeeds)
         .filter((cardFeed) => !isEmptyObject(cardFeed))
         .flatMap((cardFeed) => {
-            return Object.values(cardFeed as Record<string, Card>)
+            return Object.values(cardFeed as CardList)
                 .filter((card) => card && isCard(card) && !userCardList?.[card.cardID] && filterCondition(card))
                 .map((card) => createCardFilterItem(card, personalDetailsList, selectedCards, illustrations, companyCardIcons));
         });

From afe25e28d231cd888053fbd7875f5de50b379254 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 18:47:29 +0000
Subject: [PATCH 007/122] refactor: simplify RBR condition

---
 src/pages/settings/InitialSettingsPage.tsx | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index 5d2a18e01618..dbccc1c121e7 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -148,10 +148,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
         if (hasPaymentMethodError(bankAccountList, fundList, allCards) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) || hasBrokenFeedConnection) {
             return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
         }
-        if (hasPartiallySetupBankAccount(bankAccountList)) {
-            return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
-        }
-        if (hasPendingCardAction) {
+        if (hasPartiallySetupBankAccount(bankAccountList) || hasPendingCardAction) {
             return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
         }
         return undefined;

From 296646a70173d51cd9e6fecb07ad3af03789aa4a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 23:00:14 +0000
Subject: [PATCH 008/122] feat: implement global `CardFeedErrorsContext`

---
 src/App.tsx                                   |   2 +
 .../CardFeedErrorsContext/index.tsx           | 121 ++++++++++++++++++
 src/components/CardFeedErrorsContext/types.ts |  20 +++
 3 files changed, 143 insertions(+)
 create mode 100644 src/components/CardFeedErrorsContext/index.tsx
 create mode 100644 src/components/CardFeedErrorsContext/types.ts

diff --git a/src/App.tsx b/src/App.tsx
index d8120f363143..4ca3f193f465 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,6 +8,7 @@ import {SafeAreaProvider} from 'react-native-safe-area-context';
 import '../wdyr';
 import {ActionSheetAwareScrollViewProvider} from './components/ActionSheetAwareScrollView';
 import ActiveElementRoleProvider from './components/ActiveElementRoleProvider';
+import {CardFeedErrorsContextProvider} from './components/CardFeedErrorsContext';
 import ColorSchemeWrapper from './components/ColorSchemeWrapper';
 import ComposeProviders from './components/ComposeProviders';
 import {CurrentUserPersonalDetailsProvider} from './components/CurrentUserPersonalDetailsProvider';
@@ -132,6 +133,7 @@ function App() {
                                         ExpensifyCardContextProvider,
                                         KYCWallContextProvider,
                                         WideRHPContextProvider,
+                                        CardFeedErrorsContextProvider,
                                     ]}
                                 >
                                     <CustomStatusBarAndBackground />
diff --git a/src/components/CardFeedErrorsContext/index.tsx b/src/components/CardFeedErrorsContext/index.tsx
new file mode 100644
index 000000000000..58e5079d9cf7
--- /dev/null
+++ b/src/components/CardFeedErrorsContext/index.tsx
@@ -0,0 +1,121 @@
+import React, {createContext, useContext} from 'react';
+import type {PropsWithChildren} from 'react';
+import useOnyx from '@hooks/useOnyx';
+import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
+import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import type {AllCardFeedErrors, CardErrors, CardFeedErrors} from './types';
+
+type CardFeedErrorsContextValue = {
+    cardFeedErrors: AllCardFeedErrors;
+};
+
+const defaultCardFeedErrorsContextValue: CardFeedErrorsContextValue = {
+    cardFeedErrors: new Map<number, Map<CompanyCardFeedWithDomainID, CardFeedErrors>>(),
+};
+
+const CardFeedErrorsContext = createContext<CardFeedErrorsContextValue>(defaultCardFeedErrorsContextValue);
+
+// function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
+//     return Object.entries(policyCollection ?? {}).reduce<Record<string, number>>((acc, [key, value]) => {
+//         acc[key] = value?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
+//         return acc;
+//     }, {});
+// }
+
+function CardFeedErrorsContextProvider({children}: PropsWithChildren) {
+    const [globalCardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
+
+    // const [policyWorkspaceAccountIDMapping] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true, selector: getPolicyWorkspaceAccountIDMapping});
+
+    const [allWorkspaceCards] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true});
+
+    const [companyCardFeedsPerWorkspaceAccountID] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true});
+
+    const [failedCompanyCardAssignmentsPerFeed] = useOnyx(ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, {canBeMissing: true});
+
+    // const companyCardFeeds = new Map<{workspaceAccountID: number, feedName: CardFeed}, CardFeedData>();
+
+    // for (const [key, value] of Object.entries(allWorkspaceCards ?? {})) {
+    //     const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST)
+    //     const workspaceCardParamParts = keyParts.at(1)?.split('_');
+
+    //     if (!workspaceCardParamParts) {
+    //         continue;
+    //     }
+
+    //     const [workspaceAccountID, feedName] = workspaceCardParamParts as [string, CardFeed];
+
+    //     companyCardFeeds.set({workspaceAccountID: Number(workspaceAccountID), feedName}, value);
+    // }
+
+    const cardFeedErrors: AllCardFeedErrors = new Map();
+
+    function addErrorsForCard(card: Card) {
+        const bankName = card.bank as CompanyCardFeed;
+        const workspaceAccountID = Number(card.fundID);
+        const feedName = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
+
+        // const cardFeedCards = allWorkspaceCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`];
+
+        // const companyCardFeeds = companyCardFeedsPerWorkspaceAccountID?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
+
+        const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(companyCardFeedsPerWorkspaceAccountID);
+        const selectedFeed = combinedCompanyCardFeeds?.[feedName];
+
+        const hasFailedCardAssignments = !isEmptyObject(failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedName}`]);
+
+        const hasFeedError = feedName ? !!selectedFeed?.errors : false;
+        const isFeedConnectionBroken = isCardConnectionBroken(card);
+
+        const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
+
+        const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CompanyCardFeedWithDomainID, CardFeedErrors>();
+
+        const feedErrors = allFeedsErrors.get(feedName);
+
+        const cardErrors = feedErrors?.cardErrors ?? new Map<string, CardErrors>();
+
+        allFeedsErrors.set(feedName, {
+            shouldShowRBR,
+            hasFailedCardAssignments,
+            hasFeedError,
+            isFeedConnectionBroken,
+            cardErrors,
+        });
+
+        cardFeedErrors.set(workspaceAccountID, allFeedsErrors);
+    }
+
+    for (const card of Object.values(globalCardList ?? {})) {
+        addErrorsForCard(card);
+    }
+
+    for (const [key, workspaceCardFeedCards] of Object.entries(allWorkspaceCards ?? {})) {
+        const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
+        const workspaceCardParamParts = keyParts.at(1)?.split('_');
+
+        if (!workspaceCardParamParts) {
+            continue;
+        }
+
+        // const [workspaceAccountID, feedName] = workspaceCardParamParts as [number, CardFeed];
+
+        const {cardList, ...cards} = workspaceCardFeedCards ?? {};
+
+        for (const card of Object.values(cards ?? {})) {
+            addErrorsForCard(card);
+        }
+    }
+
+    // eslint-disable-next-line react/jsx-no-constructed-context-values
+    return <CardFeedErrorsContext.Provider value={{cardFeedErrors}}>{children}</CardFeedErrorsContext.Provider>;
+}
+
+function useCardFeedErrors() {
+    return useContext(CardFeedErrorsContext);
+}
+
+export {CardFeedErrorsContext, CardFeedErrorsContextProvider, useCardFeedErrors};
diff --git a/src/components/CardFeedErrorsContext/types.ts b/src/components/CardFeedErrorsContext/types.ts
new file mode 100644
index 000000000000..bbc08b515288
--- /dev/null
+++ b/src/components/CardFeedErrorsContext/types.ts
@@ -0,0 +1,20 @@
+import type {Card} from '@src/types/onyx';
+import type {CompanyCardFeedWithDomainID} from '@src/types/onyx/CardFeeds';
+
+type CardErrors = {
+    errors: Card['errors'];
+    errorFields: Card['errorFields'];
+    pendingAction: Card['pendingAction'];
+};
+
+type CardFeedErrors = {
+    shouldShowRBR: boolean;
+    hasFailedCardAssignments: boolean;
+    hasFeedError: boolean;
+    isFeedConnectionBroken: boolean;
+    cardErrors: Map<string, CardErrors>;
+};
+
+type AllCardFeedErrors = Map<number, Map<CompanyCardFeedWithDomainID, CardFeedErrors>>;
+
+export type {CardFeedErrors, AllCardFeedErrors, CardErrors};

From 68a22a1b9821128636d97bce29c9f96084859a16 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 23:00:48 +0000
Subject: [PATCH 009/122] refactor: extract util functions and refactor types

---
 src/hooks/useCardFeeds.tsx  | 58 ++++---------------------------------
 src/libs/CardFeedUtils.ts   | 50 +++++++++++++++++++++++++++++++-
 src/libs/CardUtils.ts       | 28 +++++++++++++-----
 src/types/onyx/CardFeeds.ts | 27 +++++++++++++++++
 src/types/onyx/index.ts     | 16 +++++++++-
 5 files changed, 117 insertions(+), 62 deletions(-)

diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 52e52bb79279..55dfd55730db 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -1,23 +1,10 @@
-import {useMemo} from 'react';
 import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
-import {getCompanyCardFeedWithDomainID} from '@libs/CardUtils';
+import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {CardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
-import type {CustomCardFeedData, DirectCardFeedData} from '@src/types/onyx/CardFeeds';
+import type {CardFeeds, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeedWithDomainID} from '@src/types/onyx';
 import useOnyx from './useOnyx';
 import useWorkspaceAccountID from './useWorkspaceAccountID';
 
-type CombinedCardFeed = CustomCardFeedData &
-    Partial<DirectCardFeedData> & {
-        /** Custom feed name, originally coming from settings.companyCardNicknames */
-        customFeedName?: string;
-
-        /** Feed name */
-        feed: CompanyCardFeed;
-    };
-
-type CombinedCardFeeds = Record<CompanyCardFeedWithDomainID, CombinedCardFeed>;
-
 /**
  * This is a custom hook that combines workspace and domain card feeds for a given policy.
  *
@@ -37,43 +24,10 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
     const [allFeeds, allFeedsResult] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true});
     const defaultFeed = allFeeds?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
 
-    const workspaceFeeds = useMemo(() => {
-        if (!policyID || !allFeeds) {
-            return undefined;
-        }
-
-        const result: CombinedCardFeeds = {};
-
-        return Object.entries(allFeeds).reduce<CombinedCardFeeds>((acc, [onyxKey, feed]) => {
-            if (!feed?.settings?.companyCards) {
-                return acc;
-            }
-
-            for (const [key, feedSettings] of Object.entries(feed.settings.companyCards)) {
-                const feedName = key as CompanyCardFeed;
-                const feedOAuthAccountDetails = feed.settings.oAuthAccountDetails?.[feedName];
-                const feedCompanyCardNickname = feed.settings.companyCardNicknames?.[feedName];
-                const domainID = onyxKey.split('_').at(-1);
-                const shouldAddFeed = domainID && (feedSettings.preferredPolicy ? feedSettings.preferredPolicy === policyID : domainID === workspaceAccountID.toString());
-
-                if (!shouldAddFeed) {
-                    continue;
-                }
-
-                const combinedFeedKey = getCompanyCardFeedWithDomainID(feedName, domainID);
-
-                acc[combinedFeedKey] = {
-                    ...feedSettings,
-                    ...feedOAuthAccountDetails,
-                    customFeedName: feedCompanyCardNickname,
-                    domainID: Number(domainID),
-                    feed: feedName,
-                };
-            }
-
-            return acc;
-        }, result);
-    }, [allFeeds, policyID, workspaceAccountID]);
+    let workspaceFeeds: CombinedCardFeeds | undefined;
+    if (policyID && allFeeds) {
+        workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds);
+    }
 
     return [workspaceFeeds, allFeedsResult, defaultFeed];
 };
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 0f86467f4e25..93505f082a2d 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -3,12 +3,14 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider';
 import type {AdditionalCardProps} from '@components/SelectionListWithSections/Search/CardListItem';
 import type IllustrationsType from '@styles/theme/illustrations/types';
 import CONST from '@src/CONST';
+import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
+import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
     getCardFeedIcon,
+    getCompanyCardFeedWithDomainID,
     getCustomOrFormattedFeedName,
     getOriginalCompanyFeeds,
     getPlaidInstitutionIconUrl,
@@ -16,6 +18,7 @@ import {
     isCard,
     isCardClosed,
     isCardHiddenFromSearch,
+    splitCompanyCardFeedWithDomainID,
 } from './CardUtils';
 import type {CompanyCardFeedIcons} from './CardUtils';
 import {getDescriptionForPolicyDomainCard, getPolicy} from './PolicyUtils';
@@ -508,6 +511,49 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
     return cardFeedsForDisplayPerPolicy;
 }
 
+function combineCardFeeds(feedName: CompanyCardFeedWithDomainID, feeds: CardFeeds | undefined) {
+    const {feedName: bankName, domainID} = splitCompanyCardFeedWithDomainID(feedName);
+
+    const feedSettings = feeds?.settings.companyCards?.[bankName];
+
+    const shouldAddFeed = domainID && feedSettings?.preferredPolicy;
+
+    if (!shouldAddFeed) {
+        return undefined;
+    }
+
+    return {
+        ...feedSettings,
+        ...feeds?.settings.oAuthAccountDetails?.[bankName],
+        customFeedName: feeds?.settings.companyCardNicknames?.[bankName],
+        domainID: Number(domainID),
+        feed: bankName,
+    };
+}
+
+function getCombinedCardFeedsFromAllFeeds(allFeeds: Record<string, CardFeeds | undefined> | undefined) {
+    return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feed]) => {
+        if (!feed?.settings?.companyCards) {
+            return acc;
+        }
+
+        for (const key of Object.keys(feed.settings.companyCards)) {
+            const bankName = key as CompanyCardFeed;
+            const domainID = Number(onyxKey.split('_').at(-1));
+            const feedName = getCompanyCardFeedWithDomainID(bankName, domainID);
+
+            const newCardFeed = combineCardFeeds(key as CompanyCardFeedWithDomainID, feed);
+            if (!newCardFeed) {
+                continue;
+            }
+
+            acc[feedName] = newCardFeed;
+        }
+
+        return acc;
+    }, {});
+}
+
 export type {CardFilterItem, CardFeedNamesWithType, CardFeedForDisplay};
 export {
     buildCardsData,
@@ -522,4 +568,6 @@ export {
     getDomainFeedData,
     getCardFeedsForDisplay,
     getCardFeedsForDisplayPerPolicy,
+    combineCardFeeds,
+    getCombinedCardFeedsFromAllFeeds,
 };
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 3f72f9e18026..dc67a6ad8da3 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -589,6 +589,16 @@ function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeed, domainID: num
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
+function splitCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithDomainID): {feedName: CompanyCardFeed; domainID: number | undefined} {
+    const feedNameParts = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR);
+
+    if (feedNameParts.length !== 2) {
+        return {feedName: feedName as CompanyCardFeed, domainID: undefined};
+    }
+
+    return {feedName: feedNameParts.at(0) as CompanyCardFeed, domainID: Number(feedNameParts.at(1))};
+}
+
 function isSelectedFeedExpired(cardFeed: CombinedCardFeed | undefined): boolean {
     return cardFeed?.expiration ? isBefore(fromUnixTime(cardFeed.expiration), new Date()) : false;
 }
@@ -671,9 +681,10 @@ function getAllCardsForWorkspace(
 ): CardList {
     const cards: CardList = {};
     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);
+    const expensifyCardFeedNames = Object.keys(expensifyCardSettings ?? {}) as CompanyCardFeedWithDomainID[];
+
+    const expensifyCardsDomainIDs = expensifyCardFeedNames.map((key) => splitCompanyCardFeedWithDomainID(key).domainID).filter((id) => !!id) as number[];
+
     for (const [key, values] of Object.entries(allCardList ?? {})) {
         const isWorkspaceAccountCards = workspaceAccountID !== CONST.DEFAULT_NUMBER_ID && key.includes(workspaceAccountID.toString());
         const isCompanyDomainCards = companyCardsDomainFeeds?.some((domainFeed) => domainFeed.domainID && key.includes(domainFeed.domainID.toString()) && key.includes(domainFeed.feedName));
@@ -761,7 +772,7 @@ function flatCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, work
  * @param card the card to check
  * @returns true if the card has a broken connection, false otherwise
  */
-function isCardWithBrokenConnection(card: Card): boolean {
+function isCardConnectionBroken(card: Card): boolean {
     return !!card.lastScrapeResult && !CONST.COMPANY_CARDS.BROKEN_CONNECTION_IGNORED_STATUSES.includes(card.lastScrapeResult);
 }
 
@@ -776,7 +787,7 @@ function checkIfFeedConnectionIsBroken(feedCards: CardList | undefined, feedToEx
         return false;
     }
 
-    return Object.values(feedCards).some((card) => !isEmptyObject(card) && card.bank !== feedToExclude && isCardWithBrokenConnection(card));
+    return Object.values(feedCards).some((card) => !isEmptyObject(card) && card.bank !== feedToExclude && isCardConnectionBroken(card));
 }
 
 /**
@@ -861,8 +872,8 @@ function getCompanyCardFeed(feedWithDomainID: string | undefined): CompanyCardFe
     if (!feedWithDomainID) {
         return '' as CompanyCardFeed;
     }
-    const [feed] = feedWithDomainID.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR);
-    return feed as CompanyCardFeed;
+
+    return splitCompanyCardFeedWithDomainID(feedWithDomainID as CompanyCardFeedWithDomainID).feedName;
 }
 
 type SplitMaskedCardNumberResult = {
@@ -966,7 +977,7 @@ export {
     isCardHiddenFromSearch,
     getFeedType,
     flatCompanyCards,
-    isCardWithBrokenConnection,
+    isCardConnectionBroken,
     checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
@@ -990,6 +1001,7 @@ export {
     getOriginalCompanyFeeds,
     getCompanyCardFeed,
     getCompanyCardFeedWithDomainID,
+    splitCompanyCardFeedWithDomainID,
     getEligibleBankAccountsForUkEuCard,
     COMPANY_CARD_FEED_ICON_NAMES,
     COMPANY_CARD_BANK_ICON_NAMES,
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 5c0b70783870..a366c381c507 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -13,6 +13,16 @@ type CompanyCardFeedWithDomainID = `${CompanyCardFeed}${typeof CONST.COMPANY_CAR
 /** Custom card feed with a number */
 type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CompanyCardFeedWithDomainID;
 
+/**
+ *
+ */
+type CardFeed = CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
+
+/**
+ *
+ */
+type CardFeedWithDomainID = `${CardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
+
 /** Statement period end */
 type StatementPeriodEnd = Exclude<ValueOf<typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE>, typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE.CUSTOM_DAY_OF_MONTH>;
 
@@ -238,12 +248,27 @@ type AddNewCompanyCardFeed = {
 /** Card fund ID */
 type FundID = number;
 
+/** Combined card feed type */
+type CombinedCardFeed = CustomCardFeedData &
+    Partial<DirectCardFeedData> & {
+        /** Custom feed name, originally coming from settings.companyCardNicknames */
+        customFeedName?: string;
+
+        /** Feed name */
+        feed: CompanyCardFeed;
+    };
+
+/** Card feeds combined by domain ID into one object */
+type CombinedCardFeeds = Record<CompanyCardFeedWithDomainID, CombinedCardFeed>;
+
 export default CardFeeds;
 export type {
     AddNewCardFeedStep,
     AddNewCompanyCardFeed,
     AddNewCardFeedData,
     CompanyCardFeed,
+    CardFeed,
+    CardFeedWithDomainID,
     CardFeedDetails,
     DirectCardFeedData,
     CardFeedProvider,
@@ -257,4 +282,6 @@ export type {
     StatementPeriodEnd,
     StatementPeriodEndDay,
     DomainSettings,
+    CombinedCardFeed,
+    CombinedCardFeeds,
 };
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index ad4550926ad7..f3667697a1f4 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -18,7 +18,17 @@ import type Card from './Card';
 import type {CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card';
 import type CardContinuousReconciliation from './CardContinuousReconciliation';
 import type CardFeeds from './CardFeeds';
-import type {AddNewCompanyCardFeed, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID} from './CardFeeds';
+import type {
+    AddNewCompanyCardFeed,
+    CardFeed,
+    CardFeedWithDomainID,
+    CombinedCardFeed,
+    CombinedCardFeeds,
+    CompanyCardFeed,
+    CompanyCardFeedWithDomainID,
+    DomainSettings,
+    FundID,
+} from './CardFeeds';
 import type CardOnWaitlist from './CardOnWaitlist';
 import type {CapturedLogs, Log} from './Console';
 import type {CorpayFields, CorpayFormField} from './CorpayFields';
@@ -176,8 +186,12 @@ export type {
     AssignCardData,
     AddNewCompanyCardFeed,
     CompanyCardFeed,
+    CombinedCardFeed,
+    CombinedCardFeeds,
     CardContinuousReconciliation,
     CompanyCardFeedWithDomainID,
+    CardFeed,
+    CardFeedWithDomainID,
     LastExportMethod,
     Locale,
     LockAccountDetails,

From 4fbe9830abc323ebd61720b68ae913696fb4a150 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 8 Jan 2026 23:49:02 +0000
Subject: [PATCH 010/122] refactor: move logic to DerivedValue

---
 src/App.tsx                                   |   2 -
 src/ONYXKEYS.ts                               |   2 +
 .../CardFeedErrorsContext/index.tsx           | 121 ------------------
 src/components/CardFeedErrorsContext/types.ts |  20 ---
 src/hooks/useCardFeedErrors.ts                |  11 ++
 .../OnyxDerived/ONYX_DERIVED_VALUES.ts        |   2 +
 .../OnyxDerived/configs/cardFeedErrors.ts     |  97 ++++++++++++++
 src/types/onyx/DerivedValues.ts               |  67 +++++++++-
 src/types/onyx/index.ts                       |   3 +-
 9 files changed, 180 insertions(+), 145 deletions(-)
 delete mode 100644 src/components/CardFeedErrorsContext/index.tsx
 delete mode 100644 src/components/CardFeedErrorsContext/types.ts
 create mode 100644 src/hooks/useCardFeedErrors.ts
 create mode 100644 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts

diff --git a/src/App.tsx b/src/App.tsx
index 4ca3f193f465..d8120f363143 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,7 +8,6 @@ import {SafeAreaProvider} from 'react-native-safe-area-context';
 import '../wdyr';
 import {ActionSheetAwareScrollViewProvider} from './components/ActionSheetAwareScrollView';
 import ActiveElementRoleProvider from './components/ActiveElementRoleProvider';
-import {CardFeedErrorsContextProvider} from './components/CardFeedErrorsContext';
 import ColorSchemeWrapper from './components/ColorSchemeWrapper';
 import ComposeProviders from './components/ComposeProviders';
 import {CurrentUserPersonalDetailsProvider} from './components/CurrentUserPersonalDetailsProvider';
@@ -133,7 +132,6 @@ function App() {
                                         ExpensifyCardContextProvider,
                                         KYCWallContextProvider,
                                         WideRHPContextProvider,
-                                        CardFeedErrorsContextProvider,
                                     ]}
                                 >
                                     <CustomStatusBarAndBackground />
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 08a7e5b8a241..097eb5bda79e 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -965,6 +965,7 @@ const ONYXKEYS = {
         REPORT_ATTRIBUTES: 'reportAttributes',
         REPORT_TRANSACTIONS_AND_VIOLATIONS: 'reportTransactionsAndViolations',
         OUTSTANDING_REPORTS_BY_POLICY_ID: 'outstandingReportsByPolicyID',
+        CARD_FEED_ERRORS: 'cardFeedErrors',
     },
 
     /** Stores HybridApp specific state required to interoperate with OldDot */
@@ -1365,6 +1366,7 @@ type OnyxDerivedValuesMapping = {
     [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: OnyxTypes.ReportAttributesDerivedValue;
     [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS]: OnyxTypes.ReportTransactionsAndViolationsDerivedValue;
     [ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID]: OnyxTypes.OutstandingReportsByPolicyIDDerivedValue;
+    [ONYXKEYS.DERIVED.CARD_FEED_ERRORS]: OnyxTypes.CardFeedErrorsDerivedValue;
 };
 
 type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping & OnyxDerivedValuesMapping;
diff --git a/src/components/CardFeedErrorsContext/index.tsx b/src/components/CardFeedErrorsContext/index.tsx
deleted file mode 100644
index 58e5079d9cf7..000000000000
--- a/src/components/CardFeedErrorsContext/index.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import React, {createContext, useContext} from 'react';
-import type {PropsWithChildren} from 'react';
-import useOnyx from '@hooks/useOnyx';
-import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
-import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
-import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
-import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import type {AllCardFeedErrors, CardErrors, CardFeedErrors} from './types';
-
-type CardFeedErrorsContextValue = {
-    cardFeedErrors: AllCardFeedErrors;
-};
-
-const defaultCardFeedErrorsContextValue: CardFeedErrorsContextValue = {
-    cardFeedErrors: new Map<number, Map<CompanyCardFeedWithDomainID, CardFeedErrors>>(),
-};
-
-const CardFeedErrorsContext = createContext<CardFeedErrorsContextValue>(defaultCardFeedErrorsContextValue);
-
-// function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
-//     return Object.entries(policyCollection ?? {}).reduce<Record<string, number>>((acc, [key, value]) => {
-//         acc[key] = value?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
-//         return acc;
-//     }, {});
-// }
-
-function CardFeedErrorsContextProvider({children}: PropsWithChildren) {
-    const [globalCardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
-
-    // const [policyWorkspaceAccountIDMapping] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true, selector: getPolicyWorkspaceAccountIDMapping});
-
-    const [allWorkspaceCards] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true});
-
-    const [companyCardFeedsPerWorkspaceAccountID] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true});
-
-    const [failedCompanyCardAssignmentsPerFeed] = useOnyx(ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, {canBeMissing: true});
-
-    // const companyCardFeeds = new Map<{workspaceAccountID: number, feedName: CardFeed}, CardFeedData>();
-
-    // for (const [key, value] of Object.entries(allWorkspaceCards ?? {})) {
-    //     const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST)
-    //     const workspaceCardParamParts = keyParts.at(1)?.split('_');
-
-    //     if (!workspaceCardParamParts) {
-    //         continue;
-    //     }
-
-    //     const [workspaceAccountID, feedName] = workspaceCardParamParts as [string, CardFeed];
-
-    //     companyCardFeeds.set({workspaceAccountID: Number(workspaceAccountID), feedName}, value);
-    // }
-
-    const cardFeedErrors: AllCardFeedErrors = new Map();
-
-    function addErrorsForCard(card: Card) {
-        const bankName = card.bank as CompanyCardFeed;
-        const workspaceAccountID = Number(card.fundID);
-        const feedName = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
-
-        // const cardFeedCards = allWorkspaceCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`];
-
-        // const companyCardFeeds = companyCardFeedsPerWorkspaceAccountID?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
-
-        const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(companyCardFeedsPerWorkspaceAccountID);
-        const selectedFeed = combinedCompanyCardFeeds?.[feedName];
-
-        const hasFailedCardAssignments = !isEmptyObject(failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedName}`]);
-
-        const hasFeedError = feedName ? !!selectedFeed?.errors : false;
-        const isFeedConnectionBroken = isCardConnectionBroken(card);
-
-        const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
-
-        const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CompanyCardFeedWithDomainID, CardFeedErrors>();
-
-        const feedErrors = allFeedsErrors.get(feedName);
-
-        const cardErrors = feedErrors?.cardErrors ?? new Map<string, CardErrors>();
-
-        allFeedsErrors.set(feedName, {
-            shouldShowRBR,
-            hasFailedCardAssignments,
-            hasFeedError,
-            isFeedConnectionBroken,
-            cardErrors,
-        });
-
-        cardFeedErrors.set(workspaceAccountID, allFeedsErrors);
-    }
-
-    for (const card of Object.values(globalCardList ?? {})) {
-        addErrorsForCard(card);
-    }
-
-    for (const [key, workspaceCardFeedCards] of Object.entries(allWorkspaceCards ?? {})) {
-        const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
-        const workspaceCardParamParts = keyParts.at(1)?.split('_');
-
-        if (!workspaceCardParamParts) {
-            continue;
-        }
-
-        // const [workspaceAccountID, feedName] = workspaceCardParamParts as [number, CardFeed];
-
-        const {cardList, ...cards} = workspaceCardFeedCards ?? {};
-
-        for (const card of Object.values(cards ?? {})) {
-            addErrorsForCard(card);
-        }
-    }
-
-    // eslint-disable-next-line react/jsx-no-constructed-context-values
-    return <CardFeedErrorsContext.Provider value={{cardFeedErrors}}>{children}</CardFeedErrorsContext.Provider>;
-}
-
-function useCardFeedErrors() {
-    return useContext(CardFeedErrorsContext);
-}
-
-export {CardFeedErrorsContext, CardFeedErrorsContextProvider, useCardFeedErrors};
diff --git a/src/components/CardFeedErrorsContext/types.ts b/src/components/CardFeedErrorsContext/types.ts
deleted file mode 100644
index bbc08b515288..000000000000
--- a/src/components/CardFeedErrorsContext/types.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import type {Card} from '@src/types/onyx';
-import type {CompanyCardFeedWithDomainID} from '@src/types/onyx/CardFeeds';
-
-type CardErrors = {
-    errors: Card['errors'];
-    errorFields: Card['errorFields'];
-    pendingAction: Card['pendingAction'];
-};
-
-type CardFeedErrors = {
-    shouldShowRBR: boolean;
-    hasFailedCardAssignments: boolean;
-    hasFeedError: boolean;
-    isFeedConnectionBroken: boolean;
-    cardErrors: Map<string, CardErrors>;
-};
-
-type AllCardFeedErrors = Map<number, Map<CompanyCardFeedWithDomainID, CardFeedErrors>>;
-
-export type {CardFeedErrors, AllCardFeedErrors, CardErrors};
diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
new file mode 100644
index 000000000000..9c019f8fbd50
--- /dev/null
+++ b/src/hooks/useCardFeedErrors.ts
@@ -0,0 +1,11 @@
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {AllCardFeedErrors} from '@src/types/onyx/DerivedValues';
+import useOnyx from './useOnyx';
+
+function useCardFeedErrors(): AllCardFeedErrors {
+    const [cardFeedErrors] = useOnyx(ONYXKEYS.DERIVED.CARD_FEED_ERRORS, {canBeMissing: true});
+
+    return cardFeedErrors ?? (new Map() as AllCardFeedErrors);
+}
+
+export default useCardFeedErrors;
diff --git a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts
index 0f9c467e518d..19b2735be76d 100644
--- a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts
+++ b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts
@@ -1,5 +1,6 @@
 import type {ValueOf} from 'type-fest';
 import ONYXKEYS from '@src/ONYXKEYS';
+import cardFeedErrorsConfig from './configs/cardFeedErrors';
 import outstandingReportsByPolicyIDConfig from './configs/outstandingReportsByPolicyID';
 import reportAttributesConfig from './configs/reportAttributes';
 import reportTransactionsAndViolationsConfig from './configs/reportTransactionsAndViolations';
@@ -13,6 +14,7 @@ const ONYX_DERIVED_VALUES = {
     [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: reportAttributesConfig,
     [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS]: reportTransactionsAndViolationsConfig,
     [ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID]: outstandingReportsByPolicyIDConfig,
+    [ONYXKEYS.DERIVED.CARD_FEED_ERRORS]: cardFeedErrorsConfig,
 } as const satisfies {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     [Key in ValueOf<typeof ONYXKEYS.DERIVED>]: OnyxDerivedValueConfig<Key, any>;
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
new file mode 100644
index 000000000000..69639749f804
--- /dev/null
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -0,0 +1,97 @@
+import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
+import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
+import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Card, CardFeed, CompanyCardFeed} from '@src/types/onyx';
+import type {AllCardFeedErrors, CardErrors, CardFeedErrors} from '@src/types/onyx/DerivedValues';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
+
+// function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
+//     return Object.entries(policyCollection ?? {}).reduce<Record<string, number>>((acc, [key, value]) => {
+//         acc[key] = value?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
+//         return acc;
+//     }, {});
+// }
+
+export default createOnyxDerivedValueConfig({
+    key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
+    dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
+    compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, companyCardFeedsPerWorkspaceAccountID]) => {
+        // const [policyWorkspaceAccountIDMapping] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true, selector: getPolicyWorkspaceAccountIDMapping});
+
+        // const companyCardFeeds = new Map<{workspaceAccountID: number, feedName: CardFeed}, CardFeedData>();
+
+        // for (const [key, value] of Object.entries(allWorkspaceCards ?? {})) {
+        //     const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST)
+        //     const workspaceCardParamParts = keyParts.at(1)?.split('_');
+
+        //     if (!workspaceCardParamParts) {
+        //         continue;
+        //     }
+
+        //     const [workspaceAccountID, feedName] = workspaceCardParamParts as [string, CardFeed];
+
+        //     companyCardFeeds.set({workspaceAccountID: Number(workspaceAccountID), feedName}, value);
+        // }
+
+        const cardFeedErrors: AllCardFeedErrors = new Map();
+
+        function addErrorsForCard(card: Card) {
+            const bankName = card.bank as CompanyCardFeed;
+            const workspaceAccountID = Number(card.fundID);
+            const feedName = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
+
+            // const cardFeedCards = allWorkspaceCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`];
+
+            // const companyCardFeeds = companyCardFeedsPerWorkspaceAccountID?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
+
+            const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(companyCardFeedsPerWorkspaceAccountID);
+            const selectedFeed = combinedCompanyCardFeeds?.[feedName];
+
+            const hasFailedCardAssignments = !isEmptyObject(
+                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedName}`],
+            );
+
+            const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, CardFeedErrors>();
+            const feedErrors = allFeedsErrors.get(bankName);
+            const cardErrors = feedErrors?.cardErrors ?? new Map<string, CardErrors>();
+
+            const hasFeedError = feedName ? !!selectedFeed?.errors : false;
+            const isFeedConnectionBroken = isCardConnectionBroken(card);
+            const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
+
+            allFeedsErrors.set(bankName, {
+                shouldShowRBR,
+                hasFailedCardAssignments,
+                hasFeedError,
+                isFeedConnectionBroken,
+                cardErrors,
+            });
+
+            cardFeedErrors.set(workspaceAccountID, allFeedsErrors);
+        }
+
+        for (const card of Object.values(globalCardList ?? {})) {
+            addErrorsForCard(card);
+        }
+
+        for (const [key, workspaceCardFeedCards] of Object.entries(allWorkspaceCards ?? {})) {
+            const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
+            const workspaceCardParamParts = keyParts.at(1)?.split('_');
+
+            if (!workspaceCardParamParts) {
+                continue;
+            }
+
+            // const [workspaceAccountID, feedName] = workspaceCardParamParts as [number, CardFeed];
+
+            const {cardList, ...cards} = workspaceCardFeedCards ?? {};
+
+            for (const card of Object.values(cards ?? {})) {
+                addErrorsForCard(card);
+            }
+        }
+
+        return cardFeedErrors;
+    },
+});
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index d39cc88e76e2..06fa547fefd9 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -1,6 +1,7 @@
 import type {OnyxCollection} from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
+import type {Card, CardFeed} from '.';
 import type {Errors} from './OnyxCommon';
 import type Report from './Report';
 import type Transaction from './Transaction';
@@ -70,5 +71,69 @@ type ReportTransactionsAndViolationsDerivedValue = Record<string, ReportTransact
  */
 type OutstandingReportsByPolicyIDDerivedValue = Record<string, OnyxCollection<Report>>;
 
+/**
+ * The errors of a card.
+ */
+type CardErrors = {
+    /**
+     *
+     */
+    errors?: Card['errors'];
+    /**
+     *
+     */
+    errorFields?: Card['errorFields'];
+    /**
+     *
+     */
+    pendingAction?: Card['pendingAction'];
+};
+
+/**
+ * The errors of a card feed.
+ */
+type CardFeedErrors = {
+    /**
+     *
+     */
+    shouldShowRBR: boolean;
+    /**
+     *
+     */
+    hasFailedCardAssignments: boolean;
+    /**
+     *
+     */
+    hasFeedError: boolean;
+    /**
+     *
+     */
+    isFeedConnectionBroken: boolean;
+    /**
+     *
+     */
+    cardErrors: Map<string, CardErrors>;
+};
+
+/**
+ * The errors of all card feeds.
+ */
+type AllCardFeedErrors = Map<number, Map<CardFeed, CardFeedErrors>>;
+
+/**
+ * The derived value for card feed errors.
+ */
+type CardFeedErrorsDerivedValue = AllCardFeedErrors;
+
 export default ReportAttributesDerivedValue;
-export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue, ReportTransactionsAndViolations, OutstandingReportsByPolicyIDDerivedValue};
+export type {
+    ReportAttributes,
+    ReportAttributesDerivedValue,
+    ReportTransactionsAndViolationsDerivedValue,
+    ReportTransactionsAndViolations,
+    OutstandingReportsByPolicyIDDerivedValue,
+    CardFeedErrorsDerivedValue,
+    AllCardFeedErrors,
+    CardFeedErrors,
+    CardErrors,
+};
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index f3667697a1f4..e7c095f6382b 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -37,7 +37,7 @@ import type Credentials from './Credentials';
 import type Currency from './Currency';
 import type {CurrencyList} from './Currency';
 import type CustomStatusDraft from './CustomStatusDraft';
-import type {OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue} from './DerivedValues';
+import type {CardFeedErrorsDerivedValue, OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue} from './DerivedValues';
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
@@ -309,6 +309,7 @@ export type {
     LastSearchParams,
     ReportTransactionsAndViolationsDerivedValue,
     OutstandingReportsByPolicyIDDerivedValue,
+    CardFeedErrorsDerivedValue,
     ScheduleCallDraft,
     ValidateUserAndGetAccessiblePolicies,
     VacationDelegate,

From cd0b85bd534f4c9711e0ff4b7ab89bc03fee3066 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 9 Jan 2026 16:40:23 +0000
Subject: [PATCH 011/122] fix: return object instead of Map for derived value

---
 src/libs/ObjectUtils.ts                          | 16 ++++++++++++++--
 .../OnyxDerived/configs/cardFeedErrors.ts        |  7 ++++---
 src/types/onyx/DerivedValues.ts                  |  8 +++++++-
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/src/libs/ObjectUtils.ts b/src/libs/ObjectUtils.ts
index 0a23693f2ba7..97c286e67953 100644
--- a/src/libs/ObjectUtils.ts
+++ b/src/libs/ObjectUtils.ts
@@ -28,5 +28,17 @@ function filterObject<TObject extends Record<string, unknown>>(obj: TObject, pre
         }, {} as TObject);
 }
 
-// eslint-disable-next-line import/prefer-default-export
-export {shallowCompare, filterObject};
+type MapToObjectResult<T> = T extends Map<infer K, infer V> ? Record<K extends string | number | symbol ? K : string, MapToObjectResult<V>> : T;
+
+/**
+ * Recursively converts a Map (and any nested Maps) to a plain object.
+ */
+function mapToObject<K extends string | number | symbol, V>(map: Map<K, V>): Record<K, MapToObjectResult<V>> {
+    const result = {} as Record<K, MapToObjectResult<V>>;
+    for (const [key, value] of map.entries()) {
+        result[key] = (value instanceof Map ? mapToObject(value) : value) as MapToObjectResult<V>;
+    }
+    return result;
+}
+
+export {shallowCompare, filterObject, mapToObject};
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 69639749f804..047802bfc2f5 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,9 +1,10 @@
 import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
+import {mapToObject} from '@libs/ObjectUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeed, CompanyCardFeed} from '@src/types/onyx';
-import type {AllCardFeedErrors, CardErrors, CardFeedErrors} from '@src/types/onyx/DerivedValues';
+import type {AllCardFeedErrors, AllCardFeedErrorsMap, CardErrors, CardFeedErrors} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
 // function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
@@ -34,7 +35,7 @@ export default createOnyxDerivedValueConfig({
         //     companyCardFeeds.set({workspaceAccountID: Number(workspaceAccountID), feedName}, value);
         // }
 
-        const cardFeedErrors: AllCardFeedErrors = new Map();
+        const cardFeedErrors: AllCardFeedErrorsMap = new Map();
 
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeed;
@@ -92,6 +93,6 @@ export default createOnyxDerivedValueConfig({
             }
         }
 
-        return cardFeedErrors;
+        return mapToObject(cardFeedErrors) as AllCardFeedErrors;
     },
 });
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 06fa547fefd9..59f5b2517afb 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -115,10 +115,15 @@ type CardFeedErrors = {
     cardErrors: Map<string, CardErrors>;
 };
 
+/**
+ *
+ */
+type AllCardFeedErrorsMap = Map<number, Map<CardFeed, CardFeedErrors>>;
+
 /**
  * The errors of all card feeds.
  */
-type AllCardFeedErrors = Map<number, Map<CardFeed, CardFeedErrors>>;
+type AllCardFeedErrors = Record<number, Record<CardFeed, CardFeedErrors>>;
 
 /**
  * The derived value for card feed errors.
@@ -133,6 +138,7 @@ export type {
     ReportTransactionsAndViolations,
     OutstandingReportsByPolicyIDDerivedValue,
     CardFeedErrorsDerivedValue,
+    AllCardFeedErrorsMap,
     AllCardFeedErrors,
     CardFeedErrors,
     CardErrors,

From ecc5f4c7c0c9dd9c2e566a044914b42b9b28ae0c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 9 Jan 2026 16:40:38 +0000
Subject: [PATCH 012/122] refactor: extract log to reduce duplicate code

---
 src/libs/actions/OnyxDerived/index.ts | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/index.ts b/src/libs/actions/OnyxDerived/index.ts
index 6b1aca2fb4da..84b0795fd8c5 100644
--- a/src/libs/actions/OnyxDerived/index.ts
+++ b/src/libs/actions/OnyxDerived/index.ts
@@ -88,6 +88,10 @@ function init() {
                 setDerivedValue(key, derivedValue);
             };
 
+            function logDependencyChange(dependencyOnyxKey: string) {
+                Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
+            }
+
             for (let i = 0; i < dependencies.length; i++) {
                 const dependencyIndex = i;
                 const dependencyOnyxKey = dependencies[dependencyIndex];
@@ -96,8 +100,8 @@ function init() {
                     Onyx.connectWithoutView({
                         key: dependencyOnyxKey,
                         waitForCollectionCallback: true,
-                        callback: (value, collectionKey, sourceValue) => {
-                            Log.info(`[OnyxDerived] dependency ${collectionKey} for derived key ${key} changed, recomputing`);
+                        callback: (value, _collectionKey, sourceValue) => {
+                            logDependencyChange(dependencyOnyxKey);
                             setDependencyValue(dependencyIndex, value as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             recomputeDerivedValue(dependencyOnyxKey, sourceValue, dependencyIndex);
                         },
@@ -118,7 +122,7 @@ function init() {
                                 Log.info(`[OnyxDerived] No locale found for derived key ${key}, skipping recompute`);
                                 return;
                             }
-                            Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
+                            logDependencyChange(dependencyOnyxKey);
                             setDependencyValue(dependencyIndex, localeValue as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             recomputeDerivedValue(dependencyOnyxKey, localeValue, dependencyIndex);
                         },
@@ -127,7 +131,7 @@ function init() {
                     Onyx.connectWithoutView({
                         key: dependencyOnyxKey,
                         callback: (value) => {
-                            Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
+                            logDependencyChange(dependencyOnyxKey);
                             setDependencyValue(dependencyIndex, value as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             // if the dependency is not a collection, pass the entire value as the source value
                             recomputeDerivedValue(dependencyOnyxKey, value, dependencyIndex);

From 19b1e4b1f09aba184591955aa2cdf2e068a534b5 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 9 Jan 2026 16:41:06 +0000
Subject: [PATCH 013/122] feat: subscribe to card feed errors in
 `InitialSettingsPage`

---
 src/pages/settings/InitialSettingsPage.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index dbccc1c121e7..41f9d946eca1 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -20,6 +20,7 @@ import Text from '@components/Text';
 import Tooltip from '@components/Tooltip';
 import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
 import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
+import useCardFeedErrors from '@hooks/useCardFeedErrors';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useNetwork from '@hooks/useNetwork';
@@ -142,7 +143,11 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const shouldDisplayLHB = !shouldUseNarrowLayout;
 
-    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
+    const cardFeedErrors = useCardFeedErrors();
+
+    const hasCardFeedErrors = Object.values(cardFeedErrors).some((feedErrors) => Object.values(feedErrors).some((feedError) => feedError.shouldShowRBR));
+
+    const hasBrokenFeedConnection = hasCardFeedErrors || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
         if (hasPaymentMethodError(bankAccountList, fundList, allCards) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) || hasBrokenFeedConnection) {

From c25bccb8245df997804f6ff3e6b726623ff07b39 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sat, 10 Jan 2026 00:15:57 +0000
Subject: [PATCH 014/122] refactor: import alias

---
 src/components/FlatList/FlatListWithScrollKey/index.ios.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx b/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
index b072e2035351..41112e293f51 100644
--- a/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
+++ b/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
@@ -1,8 +1,8 @@
 import type {ForwardedRef} from 'react';
 import React, {forwardRef, useCallback, useRef} from 'react';
 import type {LayoutChangeEvent, FlatList as RNFlatList} from 'react-native';
-import mergeRefs from '@libs/mergeRefs';
 import BaseFlatListWithScrollKey from './BaseFlatListWithScrollKey';
+import mergeRefs from './src/libs/mergeRefs';
 import type {FlatListWithScrollKeyProps} from './types';
 
 /**

From 04aeda2f1824975be1eef2f0febc8d490486b46b Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sat, 10 Jan 2026 00:16:30 +0000
Subject: [PATCH 015/122] fix: cardErrors object instead of map

---
 src/hooks/useCardFeedErrors.ts                         | 2 +-
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 2 +-
 src/types/onyx/DerivedValues.ts                        | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 9c019f8fbd50..267be20b7e83 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -5,7 +5,7 @@ import useOnyx from './useOnyx';
 function useCardFeedErrors(): AllCardFeedErrors {
     const [cardFeedErrors] = useOnyx(ONYXKEYS.DERIVED.CARD_FEED_ERRORS, {canBeMissing: true});
 
-    return cardFeedErrors ?? (new Map() as AllCardFeedErrors);
+    return cardFeedErrors ?? ({} as AllCardFeedErrors);
 }
 
 export default useCardFeedErrors;
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 047802bfc2f5..1991a3154d53 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -55,7 +55,7 @@ export default createOnyxDerivedValueConfig({
 
             const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, CardFeedErrors>();
             const feedErrors = allFeedsErrors.get(bankName);
-            const cardErrors = feedErrors?.cardErrors ?? new Map<string, CardErrors>();
+            const cardErrors = feedErrors?.cardErrors ?? ({} as Record<string, CardErrors>);
 
             const hasFeedError = feedName ? !!selectedFeed?.errors : false;
             const isFeedConnectionBroken = isCardConnectionBroken(card);
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 59f5b2517afb..a49629162844 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -112,7 +112,7 @@ type CardFeedErrors = {
     /**
      *
      */
-    cardErrors: Map<string, CardErrors>;
+    cardErrors: Record<string, CardErrors>;
 };
 
 /**

From 7ddd52c3546514015bd057a82aee84872311c58c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 14:54:13 +0000
Subject: [PATCH 016/122] fix: allow card feeds with number ids in
 cardFeedErrors

---
 src/types/onyx/DerivedValues.ts | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index a49629162844..4076064f3ff7 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -2,6 +2,7 @@ import type {OnyxCollection} from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
 import type {Card, CardFeed} from '.';
+import type {CompanyCardFeedWithNumber} from './CardFeeds';
 import type {Errors} from './OnyxCommon';
 import type Report from './Report';
 import type Transaction from './Transaction';
@@ -115,15 +116,17 @@ type CardFeedErrors = {
     cardErrors: Record<string, CardErrors>;
 };
 
+type CardFeedId = CardFeed | CompanyCardFeedWithNumber;
+
 /**
  *
  */
-type AllCardFeedErrorsMap = Map<number, Map<CardFeed, CardFeedErrors>>;
+type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, CardFeedErrors>>;
 
 /**
  * The errors of all card feeds.
  */
-type AllCardFeedErrors = Record<number, Record<CardFeed, CardFeedErrors>>;
+type AllCardFeedErrors = Record<number, Record<CardFeedId, CardFeedErrors>>;
 
 /**
  * The derived value for card feed errors.

From 2b828408f321a896194a398aa58a76fa7bef280e Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 14:55:46 +0000
Subject: [PATCH 017/122] fix: mergeRefs import

---
 src/components/FlatList/FlatListWithScrollKey/index.ios.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx b/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
index 2b84687ad375..4b07b55ff602 100644
--- a/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
+++ b/src/components/FlatList/FlatListWithScrollKey/index.ios.tsx
@@ -2,8 +2,8 @@ import type {ForwardedRef} from 'react';
 // eslint-disable-next-line no-restricted-imports
 import React, {forwardRef, useCallback, useRef} from 'react';
 import type {LayoutChangeEvent, FlatList as RNFlatList} from 'react-native';
+import mergeRefs from '@libs/mergeRefs';
 import BaseFlatListWithScrollKey from './BaseFlatListWithScrollKey';
-import mergeRefs from './src/libs/mergeRefs';
 import type {FlatListWithScrollKeyProps} from './types';
 
 /**

From 2c007d26c9f7881dd55f53937584cf951cd0f4de Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 14:58:35 +0000
Subject: [PATCH 018/122] refactor: rename hook

---
 src/pages/settings/Wallet/PaymentMethodList.tsx                 | 2 +-
 .../companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx       | 2 +-
 .../WorkspaceCompanyCardsTableHeaderButtons.tsx                 | 2 +-
 .../hooks/{useCardFeedErrors.ts => useCompanyCardFeedErrors.ts} | 0
 .../companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts      | 2 +-
 5 files changed, 4 insertions(+), 4 deletions(-)
 rename src/pages/workspace/companyCards/hooks/{useCardFeedErrors.ts => useCompanyCardFeedErrors.ts} (100%)

diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index aee0c7c50903..0761edddc53b 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -32,7 +32,7 @@ import {
 import Navigation from '@libs/Navigation/Navigation';
 import {formatPaymentMethods} from '@libs/PaymentUtils';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
-import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
+import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 89491e72de89..614eac73655e 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -31,7 +31,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {CompanyCardFeed} from '@src/types/onyx';
-import useCardFeedErrors from './hooks/useCardFeedErrors';
+import useCardFeedErrors from './hooks/useCompanyCardFeedErrors';
 
 type CardFeedListItem = ListItem & {
     /** Combined feed key */
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 46408d4e6e47..3706806d3c4e 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -18,7 +18,7 @@ import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
-import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCardFeedErrors';
+import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
 import useHasWorkspaceCompanyCardErrors from '@pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts b/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
similarity index 100%
rename from src/pages/workspace/companyCards/hooks/useCardFeedErrors.ts
rename to src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
diff --git a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
index 408e4bb7859c..fd2a1365ee24 100644
--- a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
+++ b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
@@ -1,6 +1,6 @@
 import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
 import useCompanyCards from '@hooks/useCompanyCards';
-import useCardFeedErrors from './useCardFeedErrors';
+import useCardFeedErrors from './useCompanyCardFeedErrors';
 
 type UseHasWorkspaceCompanyCardErrorsProps = {
     policyID: string | undefined;

From 5ab4770815f7832bc190c5e49fc7db59aa2c23bf Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 15:02:50 +0000
Subject: [PATCH 019/122] fix: add JSDoc

---
 src/types/onyx/DerivedValues.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 4076064f3ff7..de16452011f6 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -116,6 +116,9 @@ type CardFeedErrors = {
     cardErrors: Record<string, CardErrors>;
 };
 
+/**
+ * The ID of a card feed in the errors map/object.
+ */
 type CardFeedId = CardFeed | CompanyCardFeedWithNumber;
 
 /**

From 4bf0178b0f25872e98559699a709711044757a8d Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 15:06:39 +0000
Subject: [PATCH 020/122] fix: make OnyxUpdate types more specific

---
 src/libs/actions/CompanyCards.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index f78053f2f990..10512277f6a9 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -678,7 +678,7 @@ function updateCompanyCardName(domainOrWorkspaceAccountID: number, cardID: strin
 function updateCardTransactionStartDate(domainOrWorkspaceAccountID: number, cardID: string, newStartDate: string, bankName: CompanyCardFeed, oldStartDate?: string) {
     const authToken = NetworkStore.getAuthToken();
 
-    const optimisticData: OnyxUpdate[] = [
+    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${bankName}`,
@@ -696,7 +696,7 @@ function updateCardTransactionStartDate(domainOrWorkspaceAccountID: number, card
         },
     ];
 
-    const finallyData: OnyxUpdate[] = [
+    const finallyData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${bankName}`,
@@ -710,7 +710,7 @@ function updateCardTransactionStartDate(domainOrWorkspaceAccountID: number, card
         },
     ];
 
-    const failureData: OnyxUpdate[] = [
+    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${bankName}`,
@@ -868,7 +868,7 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, b
         feed: bankName,
     };
 
-    const failureData: OnyxUpdate[] = [
+    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
@@ -877,7 +877,7 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, b
                     companyCards: {
                         [bankName]: {
                             errors: {
-                                'Card feed fetch error': 'Could not fetch card feed due to an error',
+                                cardFetchError: 'Could not fetch card feed due to an error',
                             },
                         },
                     },

From fdb478210a4922596cd5c019b6bd5b734c53c230 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 11 Jan 2026 15:22:46 +0000
Subject: [PATCH 021/122] update JSDocs

---
 src/types/onyx/CardFeeds.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 4297038c4c4c..07e5967ef9dd 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -4,22 +4,22 @@ import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
 import type * as OnyxCommon from './OnyxCommon';
 
-/** Card feed */
+/** Company card feed name */
 type CompanyCardFeed = ValueOf<typeof CONST.COMPANY_CARD.FEED_BANK_NAME>;
 
-/** Company card feed with domain ID */
+/** Company card feed name with domain ID */
 type CompanyCardFeedWithDomainID = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
 
-/** Custom card feed with a number */
+/** Company card feed name with a number */
 type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CompanyCardFeedWithDomainID;
 
 /**
- *
+ * Either a company card feed name or the Expensify card bank name.
  */
 type CardFeed = CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
 
 /**
- *
+ * Either a company card feed name with domain ID or the Expensify card bank name with domain ID.
  */
 type CardFeedWithDomainID = `${CardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
 

From fe03b3ff5852b103d31e104996d2af4937899106 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 16:31:18 +0000
Subject: [PATCH 022/122] refactor: combined card feed utils

---
 src/libs/CardFeedUtils.ts                     | 54 ++++++++-----------
 src/libs/CardUtils.ts                         |  4 +-
 .../OnyxDerived/configs/cardFeedErrors.ts     |  4 +-
 src/types/onyx/CardFeeds.ts                   | 18 +++----
 4 files changed, 36 insertions(+), 44 deletions(-)

diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 93505f082a2d..7d059d2075b1 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -5,7 +5,8 @@ import type IllustrationsType from '@styles/theme/illustrations/types';
 import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
+import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
+import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -18,7 +19,6 @@ import {
     isCard,
     isCardClosed,
     isCardHiddenFromSearch,
-    splitCompanyCardFeedWithDomainID,
 } from './CardUtils';
 import type {CompanyCardFeedIcons} from './CardUtils';
 import {getDescriptionForPolicyDomainCard, getPolicy} from './PolicyUtils';
@@ -511,43 +511,36 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
     return cardFeedsForDisplayPerPolicy;
 }
 
-function combineCardFeeds(feedName: CompanyCardFeedWithDomainID, feeds: CardFeeds | undefined) {
-    const {feedName: bankName, domainID} = splitCompanyCardFeedWithDomainID(feedName);
+function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined) {
+    return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feeds]) => {
+        const domainID = Number(onyxKey.split('_').at(-1));
 
-    const feedSettings = feeds?.settings.companyCards?.[bankName];
+        const companyCards = feeds?.settings?.companyCards;
 
-    const shouldAddFeed = domainID && feedSettings?.preferredPolicy;
-
-    if (!shouldAddFeed) {
-        return undefined;
-    }
-
-    return {
-        ...feedSettings,
-        ...feeds?.settings.oAuthAccountDetails?.[bankName],
-        customFeedName: feeds?.settings.companyCardNicknames?.[bankName],
-        domainID: Number(domainID),
-        feed: bankName,
-    };
-}
-
-function getCombinedCardFeedsFromAllFeeds(allFeeds: Record<string, CardFeeds | undefined> | undefined) {
-    return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feed]) => {
-        if (!feed?.settings?.companyCards) {
+        if (!companyCards) {
             return acc;
         }
 
-        for (const key of Object.keys(feed.settings.companyCards)) {
-            const bankName = key as CompanyCardFeed;
-            const domainID = Number(onyxKey.split('_').at(-1));
-            const feedName = getCompanyCardFeedWithDomainID(bankName, domainID);
+        for (const feedName of Object.keys(companyCards) as CompanyCardFeedWithNumber[]) {
+            const feedSettings = companyCards?.[feedName];
+            const oAuthAccountDetails = feeds?.settings.oAuthAccountDetails?.[feedName];
+            const customFeedName = feeds?.settings.companyCardNicknames?.[feedName];
+
+            const shouldAddFeed = domainID && feedSettings?.preferredPolicy;
 
-            const newCardFeed = combineCardFeeds(key as CompanyCardFeedWithDomainID, feed);
-            if (!newCardFeed) {
+            if (!shouldAddFeed) {
                 continue;
             }
 
-            acc[feedName] = newCardFeed;
+            const combinedFeedKey = getCompanyCardFeedWithDomainID(feedName, domainID);
+
+            acc[combinedFeedKey] = {
+                ...feedSettings,
+                ...oAuthAccountDetails,
+                customFeedName,
+                domainID,
+                feed: feedName,
+            };
         }
 
         return acc;
@@ -568,6 +561,5 @@ export {
     getDomainFeedData,
     getCardFeedsForDisplay,
     getCardFeedsForDisplayPerPolicy,
-    combineCardFeeds,
     getCombinedCardFeedsFromAllFeeds,
 };
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index d0995c490d09..0e65686b82fd 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -400,7 +400,7 @@ function isCustomFeed(feed: CompanyCardFeedWithNumber | undefined): boolean {
 function getOriginalCompanyFeeds(cardFeeds: OnyxEntry<CardFeeds>): CompanyFeeds {
     return Object.fromEntries(
         Object.entries(cardFeeds?.settings?.companyCards ?? {}).filter(([key, value]) => {
-            if (value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || value.pending) {
+            if (value?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || value?.pending) {
                 return false;
             }
             return key !== CONST.EXPENSIFY_CARD.BANK;
@@ -552,7 +552,7 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry<CompanyCardFeedWithDomainID
     return isValidLastFeed ? lastSelectedFeed : defaultFeed;
 }
 
-function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeed, domainID: number | string): CompanyCardFeedWithDomainID {
+function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithNumber, domainID: number | string): CompanyCardFeedWithDomainID {
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 1991a3154d53..8ab643c127eb 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -17,7 +17,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
 export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
-    compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, companyCardFeedsPerWorkspaceAccountID]) => {
+    compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
         // const [policyWorkspaceAccountIDMapping] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true, selector: getPolicyWorkspaceAccountIDMapping});
 
         // const companyCardFeeds = new Map<{workspaceAccountID: number, feedName: CardFeed}, CardFeedData>();
@@ -46,7 +46,7 @@ export default createOnyxDerivedValueConfig({
 
             // const companyCardFeeds = companyCardFeedsPerWorkspaceAccountID?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
 
-            const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(companyCardFeedsPerWorkspaceAccountID);
+            const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
             const selectedFeed = combinedCompanyCardFeeds?.[feedName];
 
             const hasFailedCardAssignments = !isEmptyObject(
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 07e5967ef9dd..6fd82ed14298 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -7,11 +7,11 @@ import type * as OnyxCommon from './OnyxCommon';
 /** Company card feed name */
 type CompanyCardFeed = ValueOf<typeof CONST.COMPANY_CARD.FEED_BANK_NAME>;
 
-/** Company card feed name with domain ID */
-type CompanyCardFeedWithDomainID = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
-
 /** Company card feed name with a number */
-type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CompanyCardFeedWithDomainID;
+type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}`;
+
+/** Company card feed name with domain ID */
+type CompanyCardFeedWithDomainID = `${CompanyCardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
 
 /**
  * Either a company card feed name or the Expensify card bank name.
@@ -144,7 +144,7 @@ type CardFeedData = CustomCardFeedData | DirectCardFeedData;
 type CompanyFeeds = Partial<Record<CompanyCardFeed, CardFeedData>>;
 
 /** Custom feed names */
-type CompanyCardNicknames = Partial<Record<CompanyCardFeed, string>>;
+type CompanyCardNicknames = Partial<Record<CompanyCardFeedWithNumber, string>>;
 
 /** Domain settings model */
 type DomainSettings = {
@@ -167,15 +167,15 @@ type DomainSettings = {
 /** Card feeds model, including domain settings */
 type CardFeeds = {
     /** Feed settings */
-    settings: {
+    settings?: {
         /** User-friendly feed nicknames */
         companyCardNicknames?: CompanyCardNicknames;
 
         /** Company cards feeds */
-        companyCards?: Partial<Record<CompanyCardFeed, CustomCardFeedData>>;
+        companyCards?: Partial<Record<CompanyCardFeedWithNumber, CustomCardFeedData>>;
 
         /** Account details */
-        oAuthAccountDetails?: Partial<Record<CompanyCardFeed, DirectCardFeedData>>;
+        oAuthAccountDetails?: Partial<Record<CompanyCardFeedWithNumber, DirectCardFeedData>>;
 
         /** Email address of the technical contact for the domain */
         technicalContactEmail?: string;
@@ -258,7 +258,7 @@ type CombinedCardFeed = CustomCardFeedData &
         customFeedName?: string;
 
         /** Feed name */
-        feed: CompanyCardFeed;
+        feed: CompanyCardFeedWithNumber;
     };
 
 /** Card feeds combined by domain ID into one object */

From 4469a0cd32bd41ee05788a0f021f2944e3e6aad1 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 16:40:05 +0000
Subject: [PATCH 023/122] fix: include feed predicate broken

---
 src/hooks/useCardFeeds.tsx |  4 +++-
 src/libs/CardFeedUtils.ts  | 20 ++++++++++++--------
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 55dfd55730db..473b938fcc9f 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -26,7 +26,9 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
 
     let workspaceFeeds: CombinedCardFeeds | undefined;
     if (policyID && allFeeds) {
-        workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds);
+        const shouldIncludeFeedPredicate = (combinedCardFeed: CombinedCardFeed) =>
+            combinedCardFeed.preferredPolicy ? combinedCardFeed.preferredPolicy === policyID : combinedCardFeed.domainID === workspaceAccountID;
+        workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds, shouldIncludeFeedPredicate);
     }
 
     return [workspaceFeeds, allFeedsResult, defaultFeed];
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 7d059d2075b1..fee7e947bce6 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
-import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -511,7 +511,7 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
     return cardFeedsForDisplayPerPolicy;
 }
 
-function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined) {
+function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean) {
     return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feeds]) => {
         const domainID = Number(onyxKey.split('_').at(-1));
 
@@ -526,21 +526,25 @@ function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> |
             const oAuthAccountDetails = feeds?.settings.oAuthAccountDetails?.[feedName];
             const customFeedName = feeds?.settings.companyCardNicknames?.[feedName];
 
-            const shouldAddFeed = domainID && feedSettings?.preferredPolicy;
-
-            if (!shouldAddFeed) {
+            if (!domainID) {
                 continue;
             }
 
-            const combinedFeedKey = getCompanyCardFeedWithDomainID(feedName, domainID);
-
-            acc[combinedFeedKey] = {
+            const combinedCardFeed = {
                 ...feedSettings,
                 ...oAuthAccountDetails,
                 customFeedName,
                 domainID,
                 feed: feedName,
             };
+
+            if (includeFeedPredicate && !includeFeedPredicate(combinedCardFeed)) {
+                continue;
+            }
+
+            const combinedFeedKey = getCompanyCardFeedWithDomainID(feedName, domainID);
+
+            acc[combinedFeedKey] = combinedCardFeed;
         }
 
         return acc;

From 19661ffaf8f8d91a93833e6d0cec9be146bc151e Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:13:02 +0000
Subject: [PATCH 024/122] feat: restructure cardFeedErrors computation

---
 src/hooks/useCardFeedErrors.ts                | 15 ++++-
 .../OnyxDerived/configs/cardFeedErrors.ts     | 57 +++++++++++--------
 src/types/onyx/DerivedValues.ts               | 41 +++++++++++--
 3 files changed, 81 insertions(+), 32 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 267be20b7e83..bedda818a551 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -1,11 +1,20 @@
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {AllCardFeedErrors} from '@src/types/onyx/DerivedValues';
+import type {CardFeedErrors} from '@src/types/onyx/DerivedValues';
 import useOnyx from './useOnyx';
 
-function useCardFeedErrors(): AllCardFeedErrors {
+const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
+    cardFeedErrors: {},
+    shouldShowRBRForAllFeeds: false,
+    shouldShowRBRPerWorkspaceAccountID: {},
+    isSomeFeedConnectionBroken: false,
+    hasSomeFeedErrors: false,
+    hasSomeFailedCardAssignments: false,
+};
+
+function useCardFeedErrors(): CardFeedErrors {
     const [cardFeedErrors] = useOnyx(ONYXKEYS.DERIVED.CARD_FEED_ERRORS, {canBeMissing: true});
 
-    return cardFeedErrors ?? ({} as AllCardFeedErrors);
+    return cardFeedErrors ?? DEFAULT_CARD_FEED_ERRORS;
 }
 
 export default useCardFeedErrors;
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 8ab643c127eb..23fbbd258478 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -3,8 +3,9 @@ import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/Card
 import {mapToObject} from '@libs/ObjectUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CardFeed, CompanyCardFeed} from '@src/types/onyx';
-import type {AllCardFeedErrors, AllCardFeedErrorsMap, CardErrors, CardFeedErrors} from '@src/types/onyx/DerivedValues';
+import type {Card, CardFeed} from '@src/types/onyx';
+import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {AllCardFeedErrorsMap, CardErrors, CardFeedErrorsObject, FeedErrors} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
 // function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
@@ -18,27 +19,14 @@ export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
-        // const [policyWorkspaceAccountIDMapping] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true, selector: getPolicyWorkspaceAccountIDMapping});
-
-        // const companyCardFeeds = new Map<{workspaceAccountID: number, feedName: CardFeed}, CardFeedData>();
-
-        // for (const [key, value] of Object.entries(allWorkspaceCards ?? {})) {
-        //     const keyParts = key.split(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST)
-        //     const workspaceCardParamParts = keyParts.at(1)?.split('_');
-
-        //     if (!workspaceCardParamParts) {
-        //         continue;
-        //     }
-
-        //     const [workspaceAccountID, feedName] = workspaceCardParamParts as [string, CardFeed];
-
-        //     companyCardFeeds.set({workspaceAccountID: Number(workspaceAccountID), feedName}, value);
-        // }
-
         const cardFeedErrors: AllCardFeedErrorsMap = new Map();
+        let isSomeFeedConnectionBroken = false;
+        let hasSomeFeedErrors = false;
+        let hasSomeFailedCardAssignments = false;
+        const shouldShowRBRPerWorkspaceAccountID: Record<number, boolean> = {};
 
         function addErrorsForCard(card: Card) {
-            const bankName = card.bank as CompanyCardFeed;
+            const bankName = card.bank as CompanyCardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
             const feedName = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
@@ -53,7 +41,7 @@ export default createOnyxDerivedValueConfig({
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedName}`],
             );
 
-            const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, CardFeedErrors>();
+            const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, FeedErrors>();
             const feedErrors = allFeedsErrors.get(bankName);
             const cardErrors = feedErrors?.cardErrors ?? ({} as Record<string, CardErrors>);
 
@@ -61,6 +49,20 @@ export default createOnyxDerivedValueConfig({
             const isFeedConnectionBroken = isCardConnectionBroken(card);
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 
+            if (isFeedConnectionBroken) {
+                isSomeFeedConnectionBroken = true;
+            }
+
+            if (hasSomeFeedErrors) {
+                hasSomeFeedErrors = true;
+            }
+
+            if (hasFailedCardAssignments) {
+                hasSomeFailedCardAssignments = true;
+            }
+
+            shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] = shouldShowRBR;
+
             allFeedsErrors.set(bankName, {
                 shouldShowRBR,
                 hasFailedCardAssignments,
@@ -84,8 +86,6 @@ export default createOnyxDerivedValueConfig({
                 continue;
             }
 
-            // const [workspaceAccountID, feedName] = workspaceCardParamParts as [number, CardFeed];
-
             const {cardList, ...cards} = workspaceCardFeedCards ?? {};
 
             for (const card of Object.values(cards ?? {})) {
@@ -93,6 +93,15 @@ export default createOnyxDerivedValueConfig({
             }
         }
 
-        return mapToObject(cardFeedErrors) as AllCardFeedErrors;
+        const shouldShowRBRForAllFeeds = hasSomeFeedErrors || hasSomeFailedCardAssignments || isSomeFeedConnectionBroken;
+
+        return {
+            cardFeedErrors: mapToObject(cardFeedErrors) as CardFeedErrorsObject,
+            shouldShowRBRForAllFeeds,
+            shouldShowRBRPerWorkspaceAccountID,
+            isSomeFeedConnectionBroken,
+            hasSomeFeedErrors,
+            hasSomeFailedCardAssignments,
+        };
     },
 });
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index de16452011f6..e931bf2bb7fc 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -93,7 +93,7 @@ type CardErrors = {
 /**
  * The errors of a card feed.
  */
-type CardFeedErrors = {
+type FeedErrors = {
     /**
      *
      */
@@ -124,17 +124,47 @@ type CardFeedId = CardFeed | CompanyCardFeedWithNumber;
 /**
  *
  */
-type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, CardFeedErrors>>;
+type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
 
 /**
  * The errors of all card feeds.
  */
-type AllCardFeedErrors = Record<number, Record<CardFeedId, CardFeedErrors>>;
+type CardFeedErrorsObject = Record<number, Record<CardFeedId, FeedErrors>>;
+
+/**
+ *
+ */
+type CardFeedErrors = {
+    /**
+     * The errors of all card feeds.
+     */
+    cardFeedErrors: CardFeedErrorsObject;
+    /**
+     * Whether to show the RBR for all feeds.
+     */
+    shouldShowRBRForAllFeeds: boolean;
+    /**
+     * Whether to show the RBR for each workspace account ID.
+     */
+    shouldShowRBRPerWorkspaceAccountID: Record<number, boolean>;
+    /**
+     * Whether some feed connection is broken.
+     */
+    isSomeFeedConnectionBroken: boolean;
+    /**
+     * Whether some feed has errors.
+     */
+    hasSomeFeedErrors: boolean;
+    /**
+     * Whether some failed card assignments.
+     */
+    hasSomeFailedCardAssignments: boolean;
+};
 
 /**
  * The derived value for card feed errors.
  */
-type CardFeedErrorsDerivedValue = AllCardFeedErrors;
+type CardFeedErrorsDerivedValue = CardFeedErrors;
 
 export default ReportAttributesDerivedValue;
 export type {
@@ -145,7 +175,8 @@ export type {
     OutstandingReportsByPolicyIDDerivedValue,
     CardFeedErrorsDerivedValue,
     AllCardFeedErrorsMap,
-    AllCardFeedErrors,
+    CardFeedErrorsObject,
+    FeedErrors,
     CardFeedErrors,
     CardErrors,
 };

From 5467cbfc661faf1995230495f4ba4d46f23ee27a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:13:21 +0000
Subject: [PATCH 025/122] feat: use card feed errors in `InitialSettingsPage`

---
 src/pages/settings/InitialSettingsPage.tsx | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index 93858de059ea..394d3360d982 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -145,11 +145,9 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const shouldDisplayLHB = !shouldUseNarrowLayout;
 
-    const cardFeedErrors = useCardFeedErrors();
+    const {shouldShowRBRForAllFeeds} = useCardFeedErrors();
 
-    const hasCardFeedErrors = Object.values(cardFeedErrors).some((feedErrors) => Object.values(feedErrors).some((feedError) => feedError.shouldShowRBR));
-
-    const hasBrokenFeedConnection = hasCardFeedErrors || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
+    const hasBrokenFeedConnection = shouldShowRBRForAllFeeds || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
         if (

From f05a03d8d89a77ee7d7bc470f302e6fc8b9baa11 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:16:45 +0000
Subject: [PATCH 026/122] refactor: rename feedName variable and remove unused
 code

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 23fbbd258478..e7d328b03062 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -28,17 +28,13 @@ export default createOnyxDerivedValueConfig({
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
-            const feedName = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
-
-            // const cardFeedCards = allWorkspaceCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${bankName}`];
-
-            // const companyCardFeeds = companyCardFeedsPerWorkspaceAccountID?.[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`];
+            const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
             const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
-            const selectedFeed = combinedCompanyCardFeeds?.[feedName];
+            const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
 
             const hasFailedCardAssignments = !isEmptyObject(
-                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedName}`],
+                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
             );
 
             const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, FeedErrors>();

From ec09ba71622cd27dbe7ff527d46d86020155e60c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:17:47 +0000
Subject: [PATCH 027/122] refactor: rename hook to `useCompanyCardFeedErrors`

---
 src/pages/settings/Wallet/PaymentMethodList.tsx               | 4 ++--
 .../companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx     | 4 ++--
 .../WorkspaceCompanyCardsTableHeaderButtons.tsx               | 4 ++--
 .../workspace/companyCards/hooks/useCompanyCardFeedErrors.ts  | 4 ++--
 .../companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts    | 4 ++--
 5 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 0761edddc53b..426565a42bfa 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -32,7 +32,7 @@ import {
 import Navigation from '@libs/Navigation/Navigation';
 import {formatPaymentMethods} from '@libs/PaymentUtils';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
-import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
+import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -181,7 +181,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const {getCardFeedErrors} = useCardFeedErrors({policyID});
+    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 614eac73655e..592c8653bc69 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -31,7 +31,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {CompanyCardFeed} from '@src/types/onyx';
-import useCardFeedErrors from './hooks/useCompanyCardFeedErrors';
+import useCompanyCardFeedErrors from './hooks/useCompanyCardFeedErrors';
 
 type CardFeedListItem = ListItem & {
     /** Combined feed key */
@@ -56,7 +56,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
     const icons = useMemoizedLazyExpensifyIcons(['Plus']);
 
     const {companyCardFeeds, feedName: selectedFeedName} = useCompanyCards({policyID});
-    const {getCardFeedErrors} = useCardFeedErrors({policyID, feedName: selectedFeedName});
+    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID, feedName: selectedFeedName});
 
     const feeds: CardFeedListItem[] = (Object.entries(companyCardFeeds ?? {}) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([feedName, feedSettings]) => {
         const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed);
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 3706806d3c4e..49eff8e0cb57 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -18,7 +18,7 @@ import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
-import useCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
+import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
 import useHasWorkspaceCompanyCardErrors from '@pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -62,7 +62,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
     const currentFeedData = feedName ? companyFeeds?.[feedName] : undefined;
     const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`, {canBeMissing: true});
 
-    const {hasFeedError, isFeedConnectionBroken} = useCardFeedErrors({policyID, feedName});
+    const {hasFeedError, isFeedConnectionBroken} = useCompanyCardFeedErrors({policyID, feedName});
     const hasCompanyCardFeedError = useHasWorkspaceCompanyCardErrors({policyID});
 
     const openBankConnection = () => {
diff --git a/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts b/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
index 46a4f93147d9..ecc8111e24e8 100644
--- a/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
+++ b/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
@@ -24,7 +24,7 @@ type UseCompanyCardFeedErrorsResult = CardFeedErrors & {
     getCardFeedErrors: (feedName: CompanyCardFeedWithDomainID) => CardFeedErrors;
 };
 
-function useCardFeedErrors({policyID, feedName, shouldExcludeExpensifyCards = false}: UseCardFeedErrorsProps): UseCompanyCardFeedErrorsResult {
+function useCompanyCardFeedErrors({policyID, feedName, shouldExcludeExpensifyCards = false}: UseCardFeedErrorsProps): UseCompanyCardFeedErrorsResult {
     const workspaceAccountID = useWorkspaceAccountID(policyID);
 
     const {companyCardFeeds} = useCompanyCards({policyID, feedName});
@@ -62,4 +62,4 @@ function useCardFeedErrors({policyID, feedName, shouldExcludeExpensifyCards = fa
     };
 }
 
-export default useCardFeedErrors;
+export default useCompanyCardFeedErrors;
diff --git a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
index fd2a1365ee24..5cf7c4f3f805 100644
--- a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
+++ b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
@@ -1,6 +1,6 @@
 import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
 import useCompanyCards from '@hooks/useCompanyCards';
-import useCardFeedErrors from './useCompanyCardFeedErrors';
+import useCompanyCardFeedErrors from './useCompanyCardFeedErrors';
 
 type UseHasWorkspaceCompanyCardErrorsProps = {
     policyID: string | undefined;
@@ -9,7 +9,7 @@ type UseHasWorkspaceCompanyCardErrorsProps = {
 function useHasWorkspaceCompanyCardErrors({policyID}: UseHasWorkspaceCompanyCardErrorsProps): boolean {
     const {companyCardFeeds} = useCompanyCards({policyID});
     const companyCardFeedNames = Object.keys(companyCardFeeds ?? {}) as CompanyCardFeedWithDomainID[];
-    const {getCardFeedErrors} = useCardFeedErrors({policyID});
+    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
     const hasCompanyCardFeedError = companyCardFeedNames.some((feed) => getCardFeedErrors(feed).shouldShowRBR);
     return hasCompanyCardFeedError;
 }

From b1b7d9fffb77ee1bf6f5ea66ef9efaaad45c02fd Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:58:27 +0000
Subject: [PATCH 028/122] fix: migrate remaining screens to use derived value

---
 src/hooks/useCardFeedErrors.ts                |  1 +
 src/hooks/useCompanyCards.ts                  |  4 +-
 src/libs/CardUtils.ts                         | 15 +++--
 .../OnyxDerived/configs/cardFeedErrors.ts     | 13 +++-
 .../settings/Wallet/PaymentMethodList.tsx     | 21 ++++--
 src/pages/workspace/WorkspaceInitialPage.tsx  | 13 ++--
 .../WorkspaceCompanyCardFeedSelectorPage.tsx  | 10 +--
 ...orkspaceCompanyCardsTableHeaderButtons.tsx | 16 +++--
 .../hooks/useCompanyCardFeedErrors.ts         | 65 -------------------
 .../hooks/useHasWorkspaceCompanyCardErrors.ts | 17 -----
 src/types/onyx/CardFeeds.ts                   |  2 +-
 src/types/onyx/DerivedValues.ts               |  4 ++
 12 files changed, 68 insertions(+), 113 deletions(-)
 delete mode 100644 src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
 delete mode 100644 src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index bedda818a551..80fb0fc94591 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -6,6 +6,7 @@ const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     cardFeedErrors: {},
     shouldShowRBRForAllFeeds: false,
     shouldShowRBRPerWorkspaceAccountID: {},
+    shouldShowRBRPerFeedNameWithDomainID: {},
     isSomeFeedConnectionBroken: false,
     hasSomeFeedErrors: false,
     hasSomeFailedCardAssignments: false,
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 2115f8e7e358..e4fac417aac4 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -5,7 +5,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
-import type {CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed, CombinedCardFeeds} from './useCardFeeds';
@@ -21,7 +21,7 @@ type UseCompanyCardsProps = {
 
 type UseCompanyCardsResult = Partial<{
     cardFeedType: CardFeedType;
-    bankName: CompanyCardFeed;
+    bankName: CompanyCardFeedWithNumber;
     feedName: CompanyCardFeedWithDomainID;
     cardList: AssignableCardsList;
     assignedCards: CardList;
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 0e65686b82fd..c46038ef1d6c 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -344,7 +344,11 @@ function filterCardsByPersonalDetails(card: Card, searchQuery: string, personalD
     );
 }
 
-function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK, illustrations: IllustrationsType, companyCardIllustrations: CompanyCardFeedIcons): IconAsset {
+function getCardFeedIcon(
+    cardFeed: CompanyCardFeedWithNumber | typeof CONST.EXPENSIFY_CARD.BANK,
+    illustrations: IllustrationsType,
+    companyCardIllustrations: CompanyCardFeedIcons,
+): IconAsset {
     const feedIcons = {
         [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: companyCardIllustrations.VisaCompanyCardDetailLarge,
         [CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]: companyCardIllustrations.AmexCardCompanyCardDetailLarge,
@@ -368,8 +372,9 @@ function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD
         return Illustrations.ExpensifyCardImage;
     }
 
-    if (feedIcons[cardFeed]) {
-        return feedIcons[cardFeed];
+    const feedIcon = feedIcons[cardFeed as CompanyCardFeed];
+    if (feedIcon) {
+        return feedIcon;
     }
 
     // In existing OldDot setups other variations of feeds could exist, ex: vcf2, vcf3, cdfbmo
@@ -422,7 +427,7 @@ function getCompanyFeeds(cardFeeds: OnyxEntry<CombinedCardFeeds>, shouldFilterOu
     );
 }
 
-function getBankName(feedType: CompanyCardFeed): string {
+function getBankName(feedType: CompanyCardFeedWithNumber): string {
     const feedNamesMapping = {
         [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: 'Visa',
         [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: 'Mastercard',
@@ -470,7 +475,7 @@ const getBankCardDetailsImage = (bank: ValueOf<typeof CONST.COMPANY_CARDS.BANKS>
     return iconMap[bank];
 };
 
-function getCustomOrFormattedFeedName(feed?: CompanyCardFeed, customFeedName?: string, shouldAddCardsSuffix = true): string | undefined {
+function getCustomOrFormattedFeedName(feed?: CompanyCardFeedWithNumber, customFeedName?: string, shouldAddCardsSuffix = true): string | undefined {
     if (!feed) {
         return;
     }
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index e7d328b03062..495107431d46 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -24,6 +24,7 @@ export default createOnyxDerivedValueConfig({
         let hasSomeFeedErrors = false;
         let hasSomeFailedCardAssignments = false;
         const shouldShowRBRPerWorkspaceAccountID: Record<number, boolean> = {};
+        const shouldShowRBRPerFeedNameWithDomainID: Record<string, boolean> = {};
 
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeedWithNumber;
@@ -57,7 +58,9 @@ export default createOnyxDerivedValueConfig({
                 hasSomeFailedCardAssignments = true;
             }
 
-            shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] = shouldShowRBR;
+            shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] = shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
+
+            shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
 
             allFeedsErrors.set(bankName, {
                 shouldShowRBR,
@@ -92,9 +95,15 @@ export default createOnyxDerivedValueConfig({
         const shouldShowRBRForAllFeeds = hasSomeFeedErrors || hasSomeFailedCardAssignments || isSomeFeedConnectionBroken;
 
         return {
+            // The errors of all card feeds.
             cardFeedErrors: mapToObject(cardFeedErrors) as CardFeedErrorsObject,
-            shouldShowRBRForAllFeeds,
+
+            // Mappings of whether to show the RBR for each workspace account ID and per feed name with domain ID
             shouldShowRBRPerWorkspaceAccountID,
+            shouldShowRBRPerFeedNameWithDomainID,
+
+            // Whether any of the feeds has one of the below errors
+            shouldShowRBRForAllFeeds,
             isSomeFeedConnectionBroken,
             hasSomeFeedErrors,
             hasSomeFailedCardAssignments,
diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 426565a42bfa..e37e5632300d 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -12,6 +12,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
 import MenuItem from '@components/MenuItem';
 import type {PopoverMenuItem} from '@components/PopoverMenu';
 import Text from '@components/Text';
+import useCardFeedErrors from '@hooks/useCardFeedErrors';
 import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
@@ -23,6 +24,7 @@ import {
     filterPersonalCards,
     getAssignedCardSortKey,
     getCardFeedIcon,
+    getCompanyCardFeedWithDomainID,
     getPlaidInstitutionIconUrl,
     isExpensifyCard,
     isExpensifyCardPendingAction,
@@ -32,12 +34,12 @@ import {
 import Navigation from '@libs/Navigation/Navigation';
 import {formatPaymentMethods} from '@libs/PaymentUtils';
 import {getDescriptionForPolicyDomainCard} from '@libs/PolicyUtils';
-import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
-import type {BankAccount, BankAccountList, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID} from '@src/types/onyx';
+import type {BankAccount, BankAccountList, CardList, CompanyCardFeed} from '@src/types/onyx';
+import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type PaymentMethod from '@src/types/onyx/PaymentMethod';
 import {getEmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject';
 import type IconAsset from '@src/types/utils/IconAsset';
@@ -181,7 +183,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
+    const {shouldShowRBRPerFeedNameWithDomainID} = useCardFeedErrors();
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
@@ -198,8 +200,15 @@ function PaymentMethodList({
 
                 let brickRoadIndicator: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;
 
-                const cardFeedErrors = getCardFeedErrors(card.bank as CompanyCardFeedWithDomainID);
-                if (cardFeedErrors.shouldShowRBR) {
+                let shouldShowRBR = false;
+                if (card.fundID) {
+                    const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeedWithNumber, card.fundID);
+                    shouldShowRBR = shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID];
+                } else {
+                    shouldShowRBR = true;
+                }
+
+                if (shouldShowRBR) {
                     brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
                 } else if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
                     brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
@@ -391,7 +400,7 @@ function PaymentMethodList({
         cardList,
         illustrations,
         companyCardFeedIcons,
-        getCardFeedErrors,
+        shouldShowRBRPerFeedNameWithDomainID,
         privatePersonalDetails,
         onPress,
         shouldShowRightIcon,
diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx
index e3065f783d4d..1e0f2578ab11 100644
--- a/src/pages/workspace/WorkspaceInitialPage.tsx
+++ b/src/pages/workspace/WorkspaceInitialPage.tsx
@@ -11,6 +11,7 @@ import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
+import useCardFeedErrors from '@hooks/useCardFeedErrors';
 import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import useGetReceiptPartnersIntegrationData from '@hooks/useGetReceiptPartnersIntegrationData';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
@@ -23,6 +24,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSingleExecution from '@hooks/useSingleExecution';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWaitForNavigation from '@hooks/useWaitForNavigation';
+import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
 import {confirmReadyToOpenApp} from '@libs/actions/App';
 import {isConnectionInProgress} from '@libs/actions/connections';
 import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
@@ -54,7 +56,6 @@ import type {PendingAction} from '@src/types/onyx/OnyxCommon';
 import type {PolicyFeatureName} from '@src/types/onyx/Policy';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import type IconAsset from '@src/types/utils/IconAsset';
-import useHasWorkspaceCompanyCardErrors from './companyCards/hooks/useHasWorkspaceCompanyCardErrors';
 import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading';
 import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading';
 
@@ -183,6 +184,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
 
     const policyID = policy?.id;
     const policyName = policy?.name ?? '';
+    const workspaceAccountID = useWorkspaceAccountID(policyID);
 
     const hasMembersError = shouldShowEmployeeListError(policy);
     const hasPolicyCategoryError = hasPolicyCategoriesError(policyCategories);
@@ -196,7 +198,10 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
 
     const [highlightedFeature, setHighlightedFeature] = useState<string | undefined>(undefined);
 
-    const hasCompanyCardFeedError = useHasWorkspaceCompanyCardErrors({policyID});
+    const {cardFeedErrors} = useCardFeedErrors();
+    const policyCardFeedErrors = cardFeedErrors[workspaceAccountID];
+
+    const shouldShowRBR = Object.values(policyCardFeedErrors ?? {}).some((error) => error.shouldShowRBR);
 
     const workspaceMenuItems: WorkspaceMenuItem[] = useMemo(() => {
         const protectedMenuItems: WorkspaceMenuItem[] = [];
@@ -323,7 +328,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
                 icon: expensifyIcons.CreditCard,
                 action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)))),
                 screenName: SCREENS.WORKSPACE.COMPANY_CARDS,
-                brickRoadIndicator: hasCompanyCardFeedError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
+                brickRoadIndicator: shouldShowRBR ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
                 highlighted: highlightedFeature === CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED,
             });
         }
@@ -406,7 +411,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
         highlightedFeature,
         shouldShowEnterCredentialsError,
         hasPolicyCategoryError,
-        hasCompanyCardFeedError,
+        shouldShowRBR,
     ]);
 
     // We only update feature states if they aren't pending.
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 592c8653bc69..a242c500dbb1 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -8,6 +8,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 useCardFeedErrors from '@hooks/useCardFeedErrors';
 import type {CombinedCardFeed, CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
 import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
 import useCompanyCards from '@hooks/useCompanyCards';
@@ -30,15 +31,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 useCompanyCardFeedErrors from './hooks/useCompanyCardFeedErrors';
+import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 
 type CardFeedListItem = ListItem & {
     /** Combined feed key */
     value: CompanyCardFeedWithDomainID;
 
     /** Card feed value */
-    feed: CompanyCardFeed;
+    feed: CompanyCardFeedWithNumber;
 };
 
 type WorkspaceCompanyCardFeedSelectorPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED>;
@@ -56,14 +56,14 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
     const icons = useMemoizedLazyExpensifyIcons(['Plus']);
 
     const {companyCardFeeds, feedName: selectedFeedName} = useCompanyCards({policyID});
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID, feedName: selectedFeedName});
+    const {shouldShowRBRPerFeedNameWithDomainID} = useCardFeedErrors();
 
     const feeds: CardFeedListItem[] = (Object.entries(companyCardFeeds ?? {}) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([feedName, feedSettings]) => {
         const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed);
         const domain = allDomains?.[`${ONYXKEYS.COLLECTION.DOMAIN}${feedSettings.domainID}`];
         const domainName = domain?.email ? Str.extractEmailDomain(domain.email) : undefined;
 
-        const {shouldShowRBR} = getCardFeedErrors(feedName);
+        const shouldShowRBR = shouldShowRBRPerFeedNameWithDomainID[feedName];
 
         return {
             value: feedName,
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 49eff8e0cb57..b786340c1089 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -8,6 +8,7 @@ import Icon from '@components/Icon';
 // eslint-disable-next-line no-restricted-imports
 import RenderHTML from '@components/RenderHTML';
 import Table from '@components/Table';
+import useCardFeedErrors from '@hooks/useCardFeedErrors';
 import useCardFeeds from '@hooks/useCardFeeds';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
@@ -16,10 +17,9 @@ import usePolicy from '@hooks/usePolicy';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
 import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, isCustomFeed} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
-import useCompanyCardFeedErrors from '@pages/workspace/companyCards/hooks/useCompanyCardFeedErrors';
-import useHasWorkspaceCompanyCardErrors from '@pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
@@ -55,15 +55,19 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
 
     const [cardFeeds] = useCardFeeds(policyID);
     const policy = usePolicy(policyID);
+    const workspaceAccountID = useWorkspaceAccountID(policyID);
     const feed = getCompanyCardFeed(feedName);
     const formattedFeedName = feedName ? getCustomOrFormattedFeedName(feed, cardFeeds?.[feedName]?.customFeedName) : undefined;
-    const isCommercialFeed = isCustomFeed(feedName);
+    const isCommercialFeed = isCustomFeed(feed);
     const companyFeeds = getCompanyFeeds(cardFeeds);
     const currentFeedData = feedName ? companyFeeds?.[feedName] : undefined;
     const [domain] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${currentFeedData?.domainID}`, {canBeMissing: true});
 
-    const {hasFeedError, isFeedConnectionBroken} = useCompanyCardFeedErrors({policyID, feedName});
-    const hasCompanyCardFeedError = useHasWorkspaceCompanyCardErrors({policyID});
+    const {cardFeedErrors} = useCardFeedErrors();
+    const workspaceCardFeedErrors = cardFeedErrors[workspaceAccountID]?.[feed];
+    const hasFeedError = workspaceCardFeedErrors?.hasFeedError;
+    const isFeedConnectionBroken = workspaceCardFeedErrors?.isFeedConnectionBroken;
+    const shouldShowRBR = workspaceCardFeedErrors?.shouldShowRBR;
 
     const openBankConnection = () => {
         if (!feedName) {
@@ -114,7 +118,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
                         CardFeedIcon={CardFeedIcon}
                         feedName={formattedFeedName}
                         supportingText={supportingText}
-                        shouldShowRBR={hasCompanyCardFeedError}
+                        shouldShowRBR={shouldShowRBR}
                     />
                 )}
 
diff --git a/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts b/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
deleted file mode 100644
index ecc8111e24e8..000000000000
--- a/src/pages/workspace/companyCards/hooks/useCompanyCardFeedErrors.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
-import useCompanyCards from '@hooks/useCompanyCards';
-import useOnyx from '@hooks/useOnyx';
-import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
-import {checkIfFeedConnectionIsBroken, filterAllInactiveCards, filterCardsByNonExpensify, getCompanyCardFeed, getDomainOrWorkspaceAccountID} from '@libs/CardUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import {isEmptyObject} from '@src/types/utils/EmptyObject';
-
-type UseCardFeedErrorsProps = {
-    policyID: string | undefined;
-    feedName?: CompanyCardFeedWithDomainID;
-    shouldExcludeExpensifyCards?: boolean;
-};
-
-type CardFeedErrors = {
-    shouldShowRBR: boolean;
-    hasFailedCardAssignments: boolean;
-    hasFeedError: boolean;
-    isFeedConnectionBroken: boolean;
-};
-
-type UseCompanyCardFeedErrorsResult = CardFeedErrors & {
-    getCardFeedErrors: (feedName: CompanyCardFeedWithDomainID) => CardFeedErrors;
-};
-
-function useCompanyCardFeedErrors({policyID, feedName, shouldExcludeExpensifyCards = false}: UseCardFeedErrorsProps): UseCompanyCardFeedErrorsResult {
-    const workspaceAccountID = useWorkspaceAccountID(policyID);
-
-    const {companyCardFeeds} = useCompanyCards({policyID, feedName});
-    const [allCards] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
-    const filteredCards = filterAllInactiveCards(shouldExcludeExpensifyCards ? filterCardsByNonExpensify(allCards) : allCards);
-
-    const [failedCompanyCardAssignmentsPerFeed] = useOnyx(ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, {canBeMissing: true});
-
-    const getCardFeedErrors = (feedNameFn: CompanyCardFeedWithDomainID | undefined): CardFeedErrors => {
-        const bankName = getCompanyCardFeed(feedNameFn);
-        const selectedFeed = bankName && companyCardFeeds?.[bankName];
-        const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed);
-
-        const hasFailedCardAssignments = !isEmptyObject(
-            failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feedNameFn ?? ''}`],
-        );
-        const hasFeedError = feedNameFn ? !!selectedFeed?.errors : false;
-
-        const feedToExclude = shouldExcludeExpensifyCards ? CONST.EXPENSIFY_CARD.BANK : undefined;
-        const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredCards, feedToExclude);
-
-        const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
-
-        return {
-            shouldShowRBR,
-            hasFailedCardAssignments,
-            hasFeedError,
-            isFeedConnectionBroken,
-        };
-    };
-
-    return {
-        ...getCardFeedErrors(feedName),
-        getCardFeedErrors,
-    };
-}
-
-export default useCompanyCardFeedErrors;
diff --git a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts b/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
deleted file mode 100644
index 5cf7c4f3f805..000000000000
--- a/src/pages/workspace/companyCards/hooks/useHasWorkspaceCompanyCardErrors.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import type {CompanyCardFeedWithDomainID} from '@hooks/useCardFeeds';
-import useCompanyCards from '@hooks/useCompanyCards';
-import useCompanyCardFeedErrors from './useCompanyCardFeedErrors';
-
-type UseHasWorkspaceCompanyCardErrorsProps = {
-    policyID: string | undefined;
-};
-
-function useHasWorkspaceCompanyCardErrors({policyID}: UseHasWorkspaceCompanyCardErrorsProps): boolean {
-    const {companyCardFeeds} = useCompanyCards({policyID});
-    const companyCardFeedNames = Object.keys(companyCardFeeds ?? {}) as CompanyCardFeedWithDomainID[];
-    const {getCardFeedErrors} = useCompanyCardFeedErrors({policyID});
-    const hasCompanyCardFeedError = companyCardFeedNames.some((feed) => getCardFeedErrors(feed).shouldShowRBR);
-    return hasCompanyCardFeedError;
-}
-
-export default useHasWorkspaceCompanyCardErrors;
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 6fd82ed14298..27b2479ab720 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -167,7 +167,7 @@ type DomainSettings = {
 /** Card feeds model, including domain settings */
 type CardFeeds = {
     /** Feed settings */
-    settings?: {
+    settings: {
         /** User-friendly feed nicknames */
         companyCardNicknames?: CompanyCardNicknames;
 
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index e931bf2bb7fc..04c429808bd0 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -147,6 +147,10 @@ type CardFeedErrors = {
      * Whether to show the RBR for each workspace account ID.
      */
     shouldShowRBRPerWorkspaceAccountID: Record<number, boolean>;
+    /**
+     * Whether to show the RBR for each feed name with domain ID.
+     */
+    shouldShowRBRPerFeedNameWithDomainID: Record<string, boolean>;
     /**
      * Whether some feed connection is broken.
      */

From 1c4a6ef239c19214a81168488be2234c6aac18c3 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 17:59:30 +0000
Subject: [PATCH 029/122] fix: invalid variable name

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 495107431d46..31344c7bc264 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -42,7 +42,7 @@ export default createOnyxDerivedValueConfig({
             const feedErrors = allFeedsErrors.get(bankName);
             const cardErrors = feedErrors?.cardErrors ?? ({} as Record<string, CardErrors>);
 
-            const hasFeedError = feedName ? !!selectedFeed?.errors : false;
+            const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
             const isFeedConnectionBroken = isCardConnectionBroken(card);
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 

From 8e6dcb6dd00cfe7474de5cfbbfab169b42f939df Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 18:01:01 +0000
Subject: [PATCH 030/122] fix: remove unused code

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 31344c7bc264..36c526d123e7 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -8,13 +8,6 @@ import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {AllCardFeedErrorsMap, CardErrors, CardFeedErrorsObject, FeedErrors} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
-// function getPolicyWorkspaceAccountIDMapping(policyCollection: OnyxCollection<Policy>): Record<string, number> {
-//     return Object.entries(policyCollection ?? {}).reduce<Record<string, number>>((acc, [key, value]) => {
-//         acc[key] = value?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
-//         return acc;
-//     }, {});
-// }
-
 export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],

From 1bef070f03df370cd33b0f276741f028df9a670d Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 18:46:25 +0000
Subject: [PATCH 031/122] fix: add RBR to `NavigationTabBar`

---
 src/CONST/index.ts                           |  1 +
 src/hooks/useWorkspacesTabIndicatorStatus.ts | 10 ++++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 762d5be98b25..eb8e15d295c9 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7483,6 +7483,7 @@ const CONST = {
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
+        HAS_COMPANY_CARD_FEED_ERRORS: 'hasCompanyCardFeedErrors',
     },
 
     DEBUG: {
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index 5c5c56269d80..ca4b3c329dc8 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -1,10 +1,10 @@
-import {useMemo} from 'react';
 import type {ValueOf} from 'type-fest';
 import {isConnectionInProgress} from '@libs/actions/connections';
 import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
 import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
 import useTheme from './useTheme';
 
@@ -20,9 +20,14 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     const theme = useTheme();
     const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
     const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
+
+    const {cardFeedErrors} = useCardFeedErrors();
+    const workspaceAccountIDWithErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR))?.[0];
+
     // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
     // those should be cleaned out before doing any error checking
-    const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
+    const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
+
     const policyErrors = {
         [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
         [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
@@ -32,6 +37,7 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
         ),
         [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
         [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: Object.values(cleanPolicies).find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
     };
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return

From 100dd1eeb53e834cdc5739667e1344f078832fff Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 18:58:45 +0000
Subject: [PATCH 032/122] feat: add RBR for profile if company card feed errors
 exist

---
 src/hooks/useAccountTabIndicatorStatus.ts    |  6 ++++++
 src/hooks/useWorkspacesTabIndicatorStatus.ts | 16 ++++++++--------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index ee3835b2fc12..0813a549d3dc 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -6,6 +6,7 @@ import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/Sub
 import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
 import useTheme from './useTheme';
 
@@ -33,6 +34,10 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
     const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
     const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+
+    const {cardFeedErrors} = useCardFeedErrors();
+    const workspaceAccountIDWithErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR))?.[0];
+
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
     // early as soon as the first error / info condition is returned. This makes the checks very efficient since
     // we only care if a single error / info condition exists anywhere.
@@ -53,6 +58,7 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
             fundList,
             billingStatus,
         ),
+        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: !!workspaceAccountIDWithErrors,
     };
 
     const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index ca4b3c329dc8..49a205167999 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -29,14 +29,14 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
 
     const policyErrors = {
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
-        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
-        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
-            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
-        ),
-        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
-        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+        // [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
+        // [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
+        // [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
+        // [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
+        //     shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+        // ),
+        // [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
+        // [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
         [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: Object.values(cleanPolicies).find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
     };
 

From 6c4a369bd7866216511ec7100561f6e0a6bce038 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:08:30 +0000
Subject: [PATCH 033/122] fix: indicators in tab bar

---
 src/CONST/index.ts                           |  2 +-
 src/hooks/useAccountTabIndicatorStatus.ts    |  4 ++--
 src/hooks/useWorkspacesTabIndicatorStatus.ts | 16 ++++++++--------
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index eb8e15d295c9..44aa5d79d4d1 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7483,7 +7483,7 @@ const CONST = {
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
-        HAS_COMPANY_CARD_FEED_ERRORS: 'hasCompanyCardFeedErrors',
+        HAS_COMPANY_CARD_ERRORS: 'hasCompanyCardErrors',
     },
 
     DEBUG: {
diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index 0813a549d3dc..190830100839 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -36,7 +36,7 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
 
     const {cardFeedErrors} = useCardFeedErrors();
-    const workspaceAccountIDWithErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR))?.[0];
+    const hasCompanyCardErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR)) !== undefined;
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
     // early as soon as the first error / info condition is returned. This makes the checks very efficient since
@@ -58,7 +58,7 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
             fundList,
             billingStatus,
         ),
-        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: !!workspaceAccountIDWithErrors,
+        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: hasCompanyCardErrors,
     };
 
     const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index 49a205167999..ca4b3c329dc8 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -29,14 +29,14 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
 
     const policyErrors = {
-        // [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
-        // [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
-        // [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
-        // [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
-        //     shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
-        // ),
-        // [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
-        // [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
+        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
+        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
+            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+        ),
+        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
+        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
         [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: Object.values(cleanPolicies).find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
     };
 

From f8d3324623c8eea5d42242d3c18f3ffda5bda1e8 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:17:16 +0000
Subject: [PATCH 034/122] fix: remove dummy error

---
 src/libs/actions/CompanyCards.ts | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 10512277f6a9..65e4d35f3639 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -868,24 +868,6 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, b
         feed: bankName,
     };
 
-    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
-            value: {
-                settings: {
-                    companyCards: {
-                        [bankName]: {
-                            errors: {
-                                cardFetchError: 'Could not fetch card feed due to an error',
-                            },
-                        },
-                    },
-                },
-            },
-        },
-    ];
-
     API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {failureData});
 }
 

From 766d02de4f2d08034d9b4ef3b594d19a8eac0f28 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:17:26 +0000
Subject: [PATCH 035/122] fix: unused variable

---
 src/libs/actions/CompanyCards.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 65e4d35f3639..e8c9304dc50d 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -868,7 +868,7 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, b
         feed: bankName,
     };
 
-    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {failureData});
+    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
 }
 
 function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {

From 23844f50afcb3d1f6ec5a25b02fe6a1a5ca5f98f Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:22:52 +0000
Subject: [PATCH 036/122] fix: rename util functions

---
 src/libs/CardUtils.ts       | 18 +++++++++---------
 tests/unit/CardUtilsTest.ts |  6 +++---
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index c46038ef1d6c..8d50c6e59e89 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -624,7 +624,7 @@ function checkIfNewFeedConnected(prevFeedsData: CompanyFeeds, currentFeedsData:
     };
 }
 
-function filterInactiveCards(cards: CardList | undefined) {
+function filterAllInactiveCards(cards: CardList | undefined) {
     if (!cards) {
         return {};
     }
@@ -635,9 +635,9 @@ function filterInactiveCards(cards: CardList | undefined) {
     return filteredCards;
 }
 
-function filterInactiveWorkspaceCards(cardsList: WorkspaceCardsList | undefined) {
+function filterInactiveCards(cardsList: WorkspaceCardsList | undefined) {
     const {cardList, ...assignedCards} = cardsList ?? {};
-    const filteredAssignedCards = filterInactiveCards(assignedCards);
+    const filteredAssignedCards = filterAllInactiveCards(assignedCards);
 
     return {
         ...(cardList ? {cardList} : {}),
@@ -663,7 +663,7 @@ function getAllCardsForWorkspace(
         const isExpensifyDomainCards = expensifyCardsDomainIDs.some((domainID) => key.includes(domainID.toString()) && key.includes(CONST.EXPENSIFY_CARD.BANK));
         if ((isWorkspaceAccountCards || isCompanyDomainCards || isExpensifyDomainCards) && values) {
             const {cardList: assignableCards, ...assignedCards} = values ?? {};
-            const filteredCards = filterInactiveWorkspaceCards(assignedCards);
+            const filteredCards = filterInactiveCards(assignedCards);
             Object.assign(cards, filteredCards);
         }
     }
@@ -721,7 +721,7 @@ function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
  * @param workspaceAccountID the workspace account id we want to get cards for
  * @param domainIDs the domain ids we want to get cards for
  */
-function flatCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
+function flattenCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
     if (!allCardsList) {
         return {} as CardList;
     }
@@ -732,7 +732,7 @@ function flatCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, work
             return acc;
         }
         const {cardList, ...feedCards} = cards ?? {};
-        const filteredCards = filterInactiveWorkspaceCards(feedCards);
+        const filteredCards = filterInactiveCards(feedCards);
         Object.assign(acc, filteredCards);
         return acc;
     }, {});
@@ -985,15 +985,15 @@ export {
     getAllCardsForWorkspace,
     isCardHiddenFromSearch,
     getFeedType,
-    flatCompanyCards,
+    flattenCompanyCards,
     isCardConnectionBroken,
     checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
     hasIssuedExpensifyCard,
     isExpensifyCardFullySetUp,
-    filterInactiveCards as filterAllInactiveCards,
-    filterInactiveWorkspaceCards as filterInactiveCards,
+    filterAllInactiveCards,
+    filterInactiveCards,
     isCardPendingIssue,
     isCardPendingActivate,
     hasPendingExpensifyCardAction,
diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index a77b42a993c2..336ae28bdc1b 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -9,7 +9,7 @@ import IntlStore from '@src/languages/IntlStore';
 import {
     checkIfFeedConnectionIsBroken,
     filterInactiveCards,
-    flatCompanyCards,
+    flattenCompanyCards,
     formatCardExpiration,
     getAllCardsForWorkspace,
     getAssignedCardSortKey,
@@ -888,7 +888,7 @@ describe('CardUtils', () => {
     describe('flatAllCardsList', () => {
         it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => {
             const workspaceAccountID = 11111111;
-            const flattenedCardsList = flatCompanyCards(allCardsList, workspaceAccountID);
+            const flattenedCardsList = flattenCompanyCards(allCardsList, workspaceAccountID);
             const {cardList, ...customCards} = customFeedCardsList;
             expect(flattenedCardsList).toStrictEqual({
                 ...directFeedCardsMultipleList,
@@ -898,7 +898,7 @@ describe('CardUtils', () => {
 
         it('should return undefined if not defined cards list was provided', () => {
             const workspaceAccountID = 11111111;
-            const flattenedCardsList = flatCompanyCards(undefined, workspaceAccountID);
+            const flattenedCardsList = flattenCompanyCards(undefined, workspaceAccountID);
             expect(flattenedCardsList).toBeUndefined();
         });
     });

From 2653e4a9c29a54b770aa50288a9d9477120a5cc3 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:31:42 +0000
Subject: [PATCH 037/122] fix: TS

---
 .../workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index 0a7a666943e6..d61e71f8f94c 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -12,7 +12,7 @@ import useNetwork from '@hooks/useNetwork';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {getDomainOrWorkspaceAccountID, isMaskedCardNumberEqual} from '@libs/CardUtils';
+import {isMaskedCardNumberEqual} from '@libs/CardUtils';
 import tokenizedSearch from '@libs/tokenizedSearch';
 import WorkspaceCompanyCardPageEmptyState from '@pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState';
 import WorkspaceCompanyCardsFeedAddedEmptyPage from '@pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage';

From 4f8bcb5dab92d34e952b615fc518547e2f3acef0 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:33:24 +0000
Subject: [PATCH 038/122] fix: invalid type signature

---
 src/libs/CardUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 8d50c6e59e89..5df7eb4b038b 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -394,7 +394,7 @@ function getCardFeedIcon(
 /**
  * Verify if the feed is a custom feed. Those are also referred to as commercial feeds.
  */
-function isCustomFeed(feed: CompanyCardFeedWithNumber | undefined): boolean {
+function isCustomFeed(feed: CompanyCardFeedWithNumber | CompanyCardFeedWithDomainID | undefined): boolean {
     if (!feed) {
         return false;
     }

From 7f244972bcf158d7181bc75787b19af445e46961 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:33:29 +0000
Subject: [PATCH 039/122] fix: update test

---
 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 336ae28bdc1b..91861fce2b26 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -885,7 +885,7 @@ describe('CardUtils', () => {
         });
     });
 
-    describe('flatAllCardsList', () => {
+    describe('flattenCompanyCards', () => {
         it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => {
             const workspaceAccountID = 11111111;
             const flattenedCardsList = flattenCompanyCards(allCardsList, workspaceAccountID);

From 69dec624e23b7c646e0ff689d8b072a2b7ced1bc Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 19:34:44 +0000
Subject: [PATCH 040/122] fix: invalid return type

---
 src/libs/CardUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 5df7eb4b038b..7afb253ffad7 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -723,7 +723,7 @@ function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
  */
 function flattenCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
     if (!allCardsList) {
-        return {} as CardList;
+        return undefined;
     }
 
     return Object.entries(allCardsList).reduce((acc, [key, cards]) => {

From 17fb4c49959c8cf52ff9a7080cfeffc281faaf8e Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 20:16:15 +0000
Subject: [PATCH 041/122] fix: TS errors

---
 src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts | 4 ++--
 src/libs/actions/CompanyCards.ts                            | 5 +++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
index 739045ae61c2..0aa6c93e5a42 100644
--- a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
+++ b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
@@ -1,9 +1,9 @@
-import type {CompanyCardFeed} from '@src/types/onyx';
+import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 
 type OpenPolicyCompanyCardsFeedParams = {
     domainAccountID?: number;
     policyID: string;
-    feed: CompanyCardFeed;
+    feed: CompanyCardFeedWithNumber;
 };
 
 export default OpenPolicyCompanyCardsFeedParams;
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index e8c9304dc50d..d47156caf58f 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -32,6 +32,7 @@ import type {
     CardFeedDetails,
     CompanyCardFeed,
     CompanyCardFeedWithDomainID,
+    CompanyCardFeedWithNumber,
     StatementPeriodEnd,
     StatementPeriodEndDay,
 } from '@src/types/onyx/CardFeeds';
@@ -861,11 +862,11 @@ function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID
     API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, finallyData});
 }
 
-function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, bankName: CompanyCardFeed) {
+function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, feed: CompanyCardFeedWithNumber) {
     const parameters: OpenPolicyCompanyCardsFeedParams = {
         domainAccountID,
         policyID,
-        feed: bankName,
+        feed,
     };
 
     API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);

From e700221ec0281cb062caf1202baef936e5c2b47d Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 12 Jan 2026 20:17:03 +0000
Subject: [PATCH 042/122] fix: remove explicit undefined return

---
 src/libs/CardUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 7afb253ffad7..1ef0efbaee24 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -723,7 +723,7 @@ function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
  */
 function flattenCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
     if (!allCardsList) {
-        return undefined;
+        return;
     }
 
     return Object.entries(allCardsList).reduce((acc, [key, cards]) => {

From 281961dcb3316be86f8a8ff1175910471d28d9e5 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 12:46:27 +0000
Subject: [PATCH 043/122] refactor: rename card feed error states

---
 src/hooks/useCardFeedErrors.ts                | 12 ++++----
 .../OnyxDerived/configs/cardFeedErrors.ts     | 28 +++++++++----------
 src/pages/settings/InitialSettingsPage.tsx    |  4 +--
 .../settings/Wallet/PaymentMethodList.tsx     |  6 ++--
 .../WorkspaceCompanyCardFeedSelectorPage.tsx  |  4 +--
 src/types/onyx/DerivedValues.ts               | 12 ++++----
 6 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 80fb0fc94591..62265b03dd0c 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -4,12 +4,12 @@ import useOnyx from './useOnyx';
 
 const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     cardFeedErrors: {},
-    shouldShowRBRForAllFeeds: false,
-    shouldShowRBRPerWorkspaceAccountID: {},
-    shouldShowRBRPerFeedNameWithDomainID: {},
-    isSomeFeedConnectionBroken: false,
-    hasSomeFeedErrors: false,
-    hasSomeFailedCardAssignments: false,
+    shouldShowRBR: false,
+    rbrWorkspaceAccountIDMapping: {},
+    rbrFeedNameWithDomainIDMapping: {},
+    isFeedConnectionBroken: false,
+    hasFeedErrors: false,
+    hasFailedCardAssignment: false,
 };
 
 function useCardFeedErrors(): CardFeedErrors {
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 36c526d123e7..c2aa5f75c65e 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -13,11 +13,11 @@ export default createOnyxDerivedValueConfig({
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
         const cardFeedErrors: AllCardFeedErrorsMap = new Map();
+        const rbrWorkspaceAccountIDMapping: Record<number, boolean> = {};
+        const rbrFeedNameWithDomainIDMapping: Record<string, boolean> = {};
         let isSomeFeedConnectionBroken = false;
         let hasSomeFeedErrors = false;
-        let hasSomeFailedCardAssignments = false;
-        const shouldShowRBRPerWorkspaceAccountID: Record<number, boolean> = {};
-        const shouldShowRBRPerFeedNameWithDomainID: Record<string, boolean> = {};
+        let hasSomeFailedCardAssignment = false;
 
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeedWithNumber;
@@ -43,17 +43,17 @@ export default createOnyxDerivedValueConfig({
                 isSomeFeedConnectionBroken = true;
             }
 
-            if (hasSomeFeedErrors) {
+            if (hasFeedError) {
                 hasSomeFeedErrors = true;
             }
 
             if (hasFailedCardAssignments) {
-                hasSomeFailedCardAssignments = true;
+                hasSomeFailedCardAssignment = true;
             }
 
-            shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] = shouldShowRBRPerWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
+            rbrWorkspaceAccountIDMapping[workspaceAccountID] = rbrWorkspaceAccountIDMapping[workspaceAccountID] || shouldShowRBR;
 
-            shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
+            rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] || shouldShowRBR;
 
             allFeedsErrors.set(bankName, {
                 shouldShowRBR,
@@ -85,21 +85,21 @@ export default createOnyxDerivedValueConfig({
             }
         }
 
-        const shouldShowRBRForAllFeeds = hasSomeFeedErrors || hasSomeFailedCardAssignments || isSomeFeedConnectionBroken;
+        const shouldShowRBR = hasSomeFeedErrors || hasSomeFailedCardAssignment || isSomeFeedConnectionBroken;
 
         return {
             // The errors of all card feeds.
             cardFeedErrors: mapToObject(cardFeedErrors) as CardFeedErrorsObject,
 
             // Mappings of whether to show the RBR for each workspace account ID and per feed name with domain ID
-            shouldShowRBRPerWorkspaceAccountID,
-            shouldShowRBRPerFeedNameWithDomainID,
+            rbrWorkspaceAccountIDMapping,
+            rbrFeedNameWithDomainIDMapping,
 
             // Whether any of the feeds has one of the below errors
-            shouldShowRBRForAllFeeds,
-            isSomeFeedConnectionBroken,
-            hasSomeFeedErrors,
-            hasSomeFailedCardAssignments,
+            shouldShowRBR,
+            isFeedConnectionBroken: isSomeFeedConnectionBroken,
+            hasFeedErrors: hasSomeFeedErrors,
+            hasFailedCardAssignment: hasSomeFailedCardAssignment,
         };
     },
 });
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index 394d3360d982..750498d108a3 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -145,9 +145,9 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const shouldDisplayLHB = !shouldUseNarrowLayout;
 
-    const {shouldShowRBRForAllFeeds} = useCardFeedErrors();
+    const {shouldShowRBR} = useCardFeedErrors();
 
-    const hasBrokenFeedConnection = shouldShowRBRForAllFeeds || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
+    const hasBrokenFeedConnection = shouldShowRBR || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
         if (
diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index e37e5632300d..969d8873afd8 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -183,7 +183,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const {shouldShowRBRPerFeedNameWithDomainID} = useCardFeedErrors();
+    const {rbrFeedNameWithDomainIDMapping} = useCardFeedErrors();
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
@@ -203,7 +203,7 @@ function PaymentMethodList({
                 let shouldShowRBR = false;
                 if (card.fundID) {
                     const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeedWithNumber, card.fundID);
-                    shouldShowRBR = shouldShowRBRPerFeedNameWithDomainID[feedNameWithDomainID];
+                    shouldShowRBR = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID];
                 } else {
                     shouldShowRBR = true;
                 }
@@ -400,7 +400,7 @@ function PaymentMethodList({
         cardList,
         illustrations,
         companyCardFeedIcons,
-        shouldShowRBRPerFeedNameWithDomainID,
+        rbrFeedNameWithDomainIDMapping,
         privatePersonalDetails,
         onPress,
         shouldShowRightIcon,
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index a242c500dbb1..0b3a3aec0949 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -56,14 +56,14 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
     const icons = useMemoizedLazyExpensifyIcons(['Plus']);
 
     const {companyCardFeeds, feedName: selectedFeedName} = useCompanyCards({policyID});
-    const {shouldShowRBRPerFeedNameWithDomainID} = useCardFeedErrors();
+    const {rbrFeedNameWithDomainIDMapping} = useCardFeedErrors();
 
     const feeds: CardFeedListItem[] = (Object.entries(companyCardFeeds ?? {}) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([feedName, feedSettings]) => {
         const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed);
         const domain = allDomains?.[`${ONYXKEYS.COLLECTION.DOMAIN}${feedSettings.domainID}`];
         const domainName = domain?.email ? Str.extractEmailDomain(domain.email) : undefined;
 
-        const shouldShowRBR = shouldShowRBRPerFeedNameWithDomainID[feedName];
+        const shouldShowRBR = rbrFeedNameWithDomainIDMapping[feedName];
 
         return {
             value: feedName,
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 04c429808bd0..818484f93241 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -142,27 +142,27 @@ type CardFeedErrors = {
     /**
      * Whether to show the RBR for all feeds.
      */
-    shouldShowRBRForAllFeeds: boolean;
+    shouldShowRBR: boolean;
     /**
      * Whether to show the RBR for each workspace account ID.
      */
-    shouldShowRBRPerWorkspaceAccountID: Record<number, boolean>;
+    rbrWorkspaceAccountIDMapping: Record<number, boolean>;
     /**
      * Whether to show the RBR for each feed name with domain ID.
      */
-    shouldShowRBRPerFeedNameWithDomainID: Record<string, boolean>;
+    rbrFeedNameWithDomainIDMapping: Record<string, boolean>;
     /**
      * Whether some feed connection is broken.
      */
-    isSomeFeedConnectionBroken: boolean;
+    isFeedConnectionBroken: boolean;
     /**
      * Whether some feed has errors.
      */
-    hasSomeFeedErrors: boolean;
+    hasFeedErrors: boolean;
     /**
      * Whether some failed card assignments.
      */
-    hasSomeFailedCardAssignments: boolean;
+    hasFailedCardAssignment: boolean;
 };
 
 /**

From 7b35774c397b7a1d5101cce08fcc0257e796512e Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 15:47:30 +0000
Subject: [PATCH 044/122] fix: also add individual card errors to
 `cardFeedErrors` derived value

---
 .../OnyxDerived/configs/cardFeedErrors.ts     | 42 +++++++++++++------
 1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index c2aa5f75c65e..c029d2dd17f3 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,18 +1,18 @@
+import type {LETTER_DEFAULTS} from '@libs/Avatars/PresetAvatarCatalog';
 import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
-import {mapToObject} from '@libs/ObjectUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CardFeed} from '@src/types/onyx';
+import type {Card} from '@src/types/onyx';
 import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
-import type {AllCardFeedErrorsMap, CardErrors, CardFeedErrorsObject, FeedErrors} from '@src/types/onyx/DerivedValues';
+import type {CardErrors, CardFeedErrorsObject, FeedErrors} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
 export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
-        const cardFeedErrors: AllCardFeedErrorsMap = new Map();
+        const cardFeedErrors: CardFeedErrorsObject = {};
         const rbrWorkspaceAccountIDMapping: Record<number, boolean> = {};
         const rbrFeedNameWithDomainIDMapping: Record<string, boolean> = {};
         let isSomeFeedConnectionBroken = false;
@@ -31,12 +31,30 @@ export default createOnyxDerivedValueConfig({
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
             );
 
-            const allFeedsErrors = cardFeedErrors.get(workspaceAccountID) ?? new Map<CardFeed, FeedErrors>();
-            const feedErrors = allFeedsErrors.get(bankName);
-            const cardErrors = feedErrors?.cardErrors ?? ({} as Record<string, CardErrors>);
+            const allFeedsErrors = cardFeedErrors[workspaceAccountID] ?? {};
+            const feedErrors = allFeedsErrors[bankName];
+            let cardErrors: Record<string, CardErrors> = feedErrors?.cardErrors ?? {};
+
+            const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
+
+            if (hasCardErrors) {
+                cardErrors = {
+                    ...cardErrors,
+                    [card.cardID]: {
+                        errors: card.errors,
+                        errorFields: card.errorFields,
+                        pendingAction: card.pendingAction,
+                    },
+                };
+            }
 
             const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
-            const isFeedConnectionBroken = isCardConnectionBroken(card);
+            let isFeedConnectionBroken = isCardConnectionBroken(card);
+
+            if (card.bank === 'vcf2') {
+                isFeedConnectionBroken = true;
+            }
+
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 
             if (isFeedConnectionBroken) {
@@ -55,15 +73,15 @@ export default createOnyxDerivedValueConfig({
 
             rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] || shouldShowRBR;
 
-            allFeedsErrors.set(bankName, {
+            allFeedsErrors[bankName] = {
                 shouldShowRBR,
                 hasFailedCardAssignments,
                 hasFeedError,
                 isFeedConnectionBroken,
                 cardErrors,
-            });
+            };
 
-            cardFeedErrors.set(workspaceAccountID, allFeedsErrors);
+            cardFeedErrors[workspaceAccountID] = allFeedsErrors;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {
@@ -89,7 +107,7 @@ export default createOnyxDerivedValueConfig({
 
         return {
             // The errors of all card feeds.
-            cardFeedErrors: mapToObject(cardFeedErrors) as CardFeedErrorsObject,
+            cardFeedErrors,
 
             // Mappings of whether to show the RBR for each workspace account ID and per feed name with domain ID
             rbrWorkspaceAccountIDMapping,

From dd992488b747f8acc0e497f87c7a44ed344f109b Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 15:47:44 +0000
Subject: [PATCH 045/122] fix: remove manual error mock

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index c029d2dd17f3..0751ca272cdc 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -50,11 +50,6 @@ export default createOnyxDerivedValueConfig({
 
             const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
             let isFeedConnectionBroken = isCardConnectionBroken(card);
-
-            if (card.bank === 'vcf2') {
-                isFeedConnectionBroken = true;
-            }
-
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 
             if (isFeedConnectionBroken) {

From 7006a0fd6ff9765b053e8c0451c33ca9d3ae9bf9 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 17:03:18 +0000
Subject: [PATCH 046/122] refactor: remove unused `mapToObject` function

---
 src/libs/ObjectUtils.ts | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/src/libs/ObjectUtils.ts b/src/libs/ObjectUtils.ts
index 97c286e67953..1f3fe18fb153 100644
--- a/src/libs/ObjectUtils.ts
+++ b/src/libs/ObjectUtils.ts
@@ -28,17 +28,4 @@ function filterObject<TObject extends Record<string, unknown>>(obj: TObject, pre
         }, {} as TObject);
 }
 
-type MapToObjectResult<T> = T extends Map<infer K, infer V> ? Record<K extends string | number | symbol ? K : string, MapToObjectResult<V>> : T;
-
-/**
- * Recursively converts a Map (and any nested Maps) to a plain object.
- */
-function mapToObject<K extends string | number | symbol, V>(map: Map<K, V>): Record<K, MapToObjectResult<V>> {
-    const result = {} as Record<K, MapToObjectResult<V>>;
-    for (const [key, value] of map.entries()) {
-        result[key] = (value instanceof Map ? mapToObject(value) : value) as MapToObjectResult<V>;
-    }
-    return result;
-}
-
-export {shallowCompare, filterObject, mapToObject};
+export {shallowCompare, filterObject};

From beb6a13d481d3ba01dbdaaa457c8257f8c5beb26 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:15:07 +0000
Subject: [PATCH 047/122] feat: extract card feed status

---
 src/hooks/useCardFeeds.tsx   |  9 ++++++---
 src/hooks/useCompanyCards.ts |  8 +++++---
 src/libs/CardFeedUtils.ts    | 24 +++++++++++++++++++++---
 src/types/onyx/CardFeeds.ts  | 15 +++++++++++----
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 473b938fcc9f..5a6f8a019465 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -1,7 +1,8 @@
 import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
-import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
+import {getAllCardFeedsStatus, getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeedWithDomainID} from '@src/types/onyx';
+import type {CardFeedsStatus} from '@src/types/onyx/CardFeeds';
 import useOnyx from './useOnyx';
 import useWorkspaceAccountID from './useWorkspaceAccountID';
 
@@ -19,7 +20,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): [CombinedCardFeeds | undefined, ResultMetadata<OnyxCollection<CardFeeds>>, CardFeeds | undefined] => {
+const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata<OnyxCollection<CardFeeds>>, CardFeeds | undefined, CardFeedsStatus] => {
     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}`];
@@ -31,7 +32,9 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
         workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds, shouldIncludeFeedPredicate);
     }
 
-    return [workspaceFeeds, allFeedsResult, defaultFeed];
+    const cardFeedsStatus = getAllCardFeedsStatus(allFeeds);
+
+    return [workspaceFeeds, allFeedsResult, defaultFeed, cardFeedsStatus];
 };
 
 export default useCardFeeds;
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index e4fac417aac4..6859b2a14e3f 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -5,10 +5,10 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
-import type {CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatus, CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
-import type {CombinedCardFeed, CombinedCardFeeds} from './useCardFeeds';
+import type {CombinedCardFeed} from './useCardFeeds';
 import useCardsList from './useCardsList';
 import useOnyx from './useOnyx';
 
@@ -27,6 +27,7 @@ type UseCompanyCardsResult = Partial<{
     assignedCards: CardList;
     cardNames: string[];
     allCardFeeds: CombinedCardFeeds;
+    cardFeedsStatus: CardFeedsStatus;
     companyCardFeeds: CompanyFeeds;
     selectedFeed: CombinedCardFeed;
 }> & {
@@ -48,7 +49,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
     const policyIDKey = policyID || CONST.DEFAULT_MISSING_ID;
 
     const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyIDKey}`, {canBeMissing: true});
-    const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID);
+    const [allCardFeeds, allCardFeedsMetadata, defaultFeed, cardFeedsStatus] = useCardFeeds(policyID);
 
     const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);
     const bankName = feedName ? getCompanyCardFeed(feedName) : undefined;
@@ -86,6 +87,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
 
     return {
         allCardFeeds,
+        cardFeedsStatus,
         feedName,
         companyCardFeeds,
         cardList,
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index fee7e947bce6..1aab91b81dd6 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
-import type {CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatus, CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -511,11 +511,27 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
     return cardFeedsForDisplayPerPolicy;
 }
 
-function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean) {
+function getCardFeedStatus(feed: CardFeeds | undefined): CardFeedsStatus {
+    return {
+        errors: feed?.errors,
+        isLoading: feed?.isLoading,
+    };
+}
+
+function getAllCardFeedsStatus(allFeeds: OnyxCollection<CardFeeds> | undefined): CardFeedsStatus {
+    return Object.entries(allFeeds ?? {}).reduce<CardFeedsStatus>((acc, [onyxKey, feeds]) => {
+        const {errors, isLoading} = feeds ?? ({} as CardFeeds);
+        acc = getCardFeedStatus(feeds);
+        return acc;
+    }, {});
+}
+
+function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean): CombinedCardFeeds {
     return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feeds]) => {
         const domainID = Number(onyxKey.split('_').at(-1));
 
-        const companyCards = feeds?.settings?.companyCards;
+        const {settings: feedSettings, ...feedStatus} = feeds ?? ({} as CardFeeds);
+        const companyCards = feedSettings?.companyCards;
 
         if (!companyCards) {
             return acc;
@@ -566,4 +582,6 @@ export {
     getCardFeedsForDisplay,
     getCardFeedsForDisplayPerPolicy,
     getCombinedCardFeedsFromAllFeeds,
+    getCardFeedStatus,
+    getAllCardFeedsStatus,
 };
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 27b2479ab720..69705a3d8aea 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -164,6 +164,14 @@ type DomainSettings = {
     };
 };
 
+type CardFeedsStatus = {
+    /** Whether we are loading the data via the API */
+    isLoading?: boolean;
+
+    /** Collection of errors coming from BE */
+    errors?: OnyxCommon.Errors;
+};
+
 /** Card feeds model, including domain settings */
 type CardFeeds = {
     /** Feed settings */
@@ -183,10 +191,8 @@ type CardFeeds = {
         /** Whether to use the technical contact's billing card */
         useTechnicalContactBillingCard?: boolean;
     };
-
-    /** Whether we are loading the data via the API */
-    isLoading?: boolean;
-} & DomainSettings;
+} & CardFeedsStatus &
+    DomainSettings;
 
 /** Data required to be sent to add a new card */
 type AddNewCardFeedData = {
@@ -276,6 +282,7 @@ export type {
     DirectCardFeedData,
     CardFeedProvider,
     CardFeedData,
+    CardFeedsStatus,
     CompanyFeeds,
     CompanyCardFeedWithDomainID,
     CustomCardFeedData,

From 59f29f5a369afd9ff1bb2152e88df410d5205165 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:15:48 +0000
Subject: [PATCH 048/122] feat: introduce error display if feed cannot be
 loaded

---
 src/languages/en.ts                           |  3 ++
 src/libs/actions/CompanyCards.ts              | 46 ++++++++++++++-----
 .../WorkspaceCompanyCardsTable/index.tsx      | 45 ++++++++++++------
 3 files changed, 68 insertions(+), 26 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 4eba58e91e09..78423b9b2ae0 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4677,6 +4677,9 @@ const translations = {
         companyCards: {
             addCards: 'Add cards',
             selectCards: 'Select cards',
+            error: {
+                feedCouldNotBeLoaded: 'An error occurred while loading the feed. Please try again.',
+            },
             addNewCard: {
                 other: 'Other',
                 cardProviders: {
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 165b57a32e81..d00aacde1d3a 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -842,19 +842,10 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, f
         feed,
     };
 
-    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
-}
-
-function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {
-    const parameters: OpenPolicyCompanyCardsFeedParams = {
-        policyID,
-        feed,
-    };
-
     const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
+            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
             value: {
                 isLoading: true,
             },
@@ -864,14 +855,45 @@ function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainO
     const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
+            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
             value: {
                 isLoading: false,
+                errors: null,
             },
         },
     ];
 
     const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
+            value: {
+                isLoading: false,
+                errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.error.feedCouldNotBeLoaded'),
+            },
+        },
+    ];
+
+    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {optimisticData, successData, failureData});
+}
+
+function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {
+    const parameters: OpenPolicyCompanyCardsFeedParams = {
+        policyID,
+        feed,
+    };
+
+    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
+            value: {
+                isLoading: true,
+            },
+        },
+    ];
+
+    const finallyData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
@@ -881,7 +903,7 @@ function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainO
         },
     ];
 
-    API.read(READ_COMMANDS.OPEN_ASSIGN_FEED_CARD_PAGE, parameters, {optimisticData, successData, failureData});
+    API.read(READ_COMMANDS.OPEN_ASSIGN_FEED_CARD_PAGE, parameters, {optimisticData, finallyData});
 }
 
 function openPolicyAddCardFeedPage(policyID: string | undefined) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index d61e71f8f94c..d9bd3c117aed 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -2,6 +2,8 @@ import type {ListRenderItemInfo} from '@shopify/flash-list';
 import React, {useEffect, useRef, useState} from 'react';
 import {View} from 'react-native';
 import CardFeedIcon from '@components/CardFeedIcon';
+import ErrorMessageRow from '@components/ErrorMessageRow';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ScrollView from '@components/ScrollView';
 import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton';
 import Table from '@components/Table';
@@ -21,6 +23,7 @@ import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import WorkspaceCompanyCardsTableHeaderButtons from './WorkspaceCompanyCardsTableHeaderButtons';
 import WorkspaceCompanyCardTableItem from './WorkspaceCompanyCardsTableItem';
@@ -55,6 +58,8 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         feedName,
         cardList,
         assignedCards,
+        allCardFeeds,
+        cardFeedsStatus,
         cardNames,
         cardFeedType,
         selectedFeed,
@@ -72,10 +77,14 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
 
     const hasNoAssignedCard = Object.keys(assignedCards ?? {}).length === 0;
     const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || isLoadingOnyxValue(lastSelectedFeedMetadata);
+
+    const isFeedsError = !isEmptyObject(cardFeedsStatus?.errors);
+
     const isLoadingCards = cardFeedType === 'directFeed' ? selectedFeed?.accountList === undefined : isLoadingOnyxValue(cardListMetadata) || cardList === undefined;
+
     const isLoadingPage = !isOffline && (isLoadingFeed || isLoadingOnyxValue(personalDetailsMetadata));
-    const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed;
-    const showTableControls = showCards && !!selectedFeed && !isLoadingCards;
+    const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed && !isFeedsError;
+    const showTableControls = showCards && !!selectedFeed && !isLoadingCards && !isFeedsError;
 
     const isGB = countryByIp === CONST.COUNTRY.GB;
     const shouldShowGBDisclaimer = isGB && (isNoFeed || hasNoAssignedCard);
@@ -104,25 +113,26 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         },
     ];
 
-    const cardsData: WorkspaceCompanyCardTableItemData[] = isLoadingCards
-        ? []
-        : (cardNames?.map((cardName) => {
-              const assignedCardPredicate = (card: Card) => (isDirectCardFeed ? card.cardName === cardName : isMaskedCardNumberEqual(card.cardName, cardName));
+    const cardsData: WorkspaceCompanyCardTableItemData[] =
+        isLoadingCards || isFeedsError
+            ? []
+            : (cardNames?.map((cardName) => {
+                  const assignedCardPredicate = (card: Card) => (isDirectCardFeed ? card.cardName === cardName : isMaskedCardNumberEqual(card.cardName, cardName));
 
-              const assignedCard = Object.values(assignedCards ?? {}).find(assignedCardPredicate);
+                  const assignedCard = Object.values(assignedCards ?? {}).find(assignedCardPredicate);
 
-              const failedCompanyCardAssignment = failedCompanyCardAssignments?.[cardName];
+                  const failedCompanyCardAssignment = failedCompanyCardAssignments?.[cardName];
 
-              const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
+                  const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
 
-              const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined;
+                  const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined;
 
-              const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
+                  const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
 
-              const isAssigned = !!assignedCard;
+                  const isAssigned = !!assignedCard;
 
-              return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder, failedCompanyCardAssignment};
-          }) ?? []);
+                  return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder, failedCompanyCardAssignment};
+              }) ?? []);
 
     const keyExtractor = (item: WorkspaceCompanyCardTableItemData, index: number) => `${item.cardName}_${index}`;
 
@@ -305,6 +315,13 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
                 </ScrollView>
             )}
 
+            {isFeedsError && (
+                <ErrorMessageRow
+                    errors={cardFeedsStatus?.errors}
+                    errorRowStyles={[styles.mh5]}
+                />
+            )}
+
             {showCards && (
                 <>
                     {!shouldUseNarrowTableLayout && !isLoadingFeed && <Table.Header />}

From 7fb309dab5f45f55b6df022c68c21d68cb5541fe Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:17:36 +0000
Subject: [PATCH 049/122] fix: update error message

---
 src/languages/en.ts              | 2 +-
 src/libs/actions/CompanyCards.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 78423b9b2ae0..5ea8ef346ac0 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4678,7 +4678,7 @@ const translations = {
             addCards: 'Add cards',
             selectCards: 'Select cards',
             error: {
-                feedCouldNotBeLoaded: 'An error occurred while loading the feed. Please try again.',
+                feedsCouldNotBeLoaded: 'An error occurred while loading the feeds. Please try again or contact your administrator.',
             },
             addNewCard: {
                 other: 'Other',
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index d00aacde1d3a..1a76c23fb25e 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -869,7 +869,7 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, f
             key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
             value: {
                 isLoading: false,
-                errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.error.feedCouldNotBeLoaded'),
+                errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.error.feedsCouldNotBeLoaded'),
             },
         },
     ];

From 8458ef8143fdb929b4262c47ff6f116b3f8cece9 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:18:45 +0000
Subject: [PATCH 050/122] Update es.ts

---
 src/languages/es.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/languages/es.ts b/src/languages/es.ts
index 80e29ddee6c9..938f72e59d95 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4410,6 +4410,9 @@ ${amount} para ${merchant} - ${date}`,
         companyCards: {
             addCards: 'Añadir tarjetas',
             selectCards: 'Seleccionar tarjetas',
+            error: {
+                feedsCouldNotBeLoaded: 'Ocurrió un error al cargar los feeds. Por favor, inténtelo de nuevo o contacte a su administrador.',
+            },
             addNewCard: {
                 other: 'Otros',
                 cardProviders: {

From 09d320d1292c7a879585cf681e3e3965ee0a9c4a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:49:48 +0000
Subject: [PATCH 051/122] add jsdoc

---
 src/types/onyx/CardFeeds.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 69705a3d8aea..ca4eb221e381 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -164,6 +164,7 @@ type DomainSettings = {
     };
 };
 
+/** Card feeds status */
 type CardFeedsStatus = {
     /** Whether we are loading the data via the API */
     isLoading?: boolean;

From bf9a6088d7f7fe3a2d1f7442750994fb43a32051 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 13 Jan 2026 21:50:46 +0000
Subject: [PATCH 052/122] fix: TS errors

---
 .../workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index d9bd3c117aed..95ff74929c21 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -3,7 +3,6 @@ import React, {useEffect, useRef, useState} from 'react';
 import {View} from 'react-native';
 import CardFeedIcon from '@components/CardFeedIcon';
 import ErrorMessageRow from '@components/ErrorMessageRow';
-import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ScrollView from '@components/ScrollView';
 import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton';
 import Table from '@components/Table';
@@ -58,7 +57,6 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         feedName,
         cardList,
         assignedCards,
-        allCardFeeds,
         cardFeedsStatus,
         cardNames,
         cardFeedType,

From 390068d418edb72afeba050d15d848d8a0cf3172 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 19:34:59 +0000
Subject: [PATCH 053/122] fix: invalid card feed status function

---
 src/hooks/useCardFeeds.tsx                    |  6 ++---
 src/hooks/useCompanyCards.ts                  |  6 ++---
 src/libs/CardFeedUtils.ts                     | 22 +++++++++----------
 .../OnyxDerived/configs/cardFeedErrors.ts     |  5 ++---
 .../WorkspaceCompanyCardsTable/index.tsx      |  2 +-
 src/types/onyx/CardFeeds.ts                   |  6 +++++
 6 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 5a6f8a019465..6695383c2ff6 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -1,5 +1,5 @@
 import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
-import {getAllCardFeedsStatus, getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
+import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeedWithDomainID} from '@src/types/onyx';
 import type {CardFeedsStatus} from '@src/types/onyx/CardFeeds';
@@ -32,9 +32,9 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
         workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds, shouldIncludeFeedPredicate);
     }
 
-    const cardFeedsStatus = getAllCardFeedsStatus(allFeeds);
+    const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(allFeeds);
 
-    return [workspaceFeeds, allFeedsResult, defaultFeed, cardFeedsStatus];
+    return [workspaceFeeds, allFeedsResult, defaultFeed, workspaceCardFeedsStatus];
 };
 
 export default useCardFeeds;
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 6859b2a14e3f..579e39517b52 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -27,7 +27,7 @@ type UseCompanyCardsResult = Partial<{
     assignedCards: CardList;
     cardNames: string[];
     allCardFeeds: CombinedCardFeeds;
-    cardFeedsStatus: CardFeedsStatus;
+    workspaceCardFeedsStatus: CardFeedsStatus;
     companyCardFeeds: CompanyFeeds;
     selectedFeed: CombinedCardFeed;
 }> & {
@@ -49,7 +49,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
     const policyIDKey = policyID || CONST.DEFAULT_MISSING_ID;
 
     const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyIDKey}`, {canBeMissing: true});
-    const [allCardFeeds, allCardFeedsMetadata, defaultFeed, cardFeedsStatus] = useCardFeeds(policyID);
+    const [allCardFeeds, allCardFeedsMetadata, , workspaceCardFeedsStatus] = useCardFeeds(policyID);
 
     const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);
     const bankName = feedName ? getCompanyCardFeed(feedName) : undefined;
@@ -87,7 +87,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
 
     return {
         allCardFeeds,
-        cardFeedsStatus,
+        workspaceCardFeedsStatus,
         feedName,
         companyCardFeeds,
         cardList,
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 1aab91b81dd6..04c2295ef64b 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
-import type {CardFeedsStatus, CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatus, CardFeedsStatusByDomainID, CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -518,20 +518,20 @@ function getCardFeedStatus(feed: CardFeeds | undefined): CardFeedsStatus {
     };
 }
 
-function getAllCardFeedsStatus(allFeeds: OnyxCollection<CardFeeds> | undefined): CardFeedsStatus {
-    return Object.entries(allFeeds ?? {}).reduce<CardFeedsStatus>((acc, [onyxKey, feeds]) => {
-        const {errors, isLoading} = feeds ?? ({} as CardFeeds);
-        acc = getCardFeedStatus(feeds);
+function getWorkspaceCardFeedsStatus(allFeeds: OnyxCollection<CardFeeds> | undefined): CardFeedsStatusByDomainID {
+    return Object.entries(allFeeds ?? {}).reduce<CardFeedsStatusByDomainID>((acc, [onyxKey, feeds]) => {
+        const domainID = Number(onyxKey.split('_').at(-1));
+        acc[domainID] = getCardFeedStatus(feeds);
         return acc;
-    }, {});
+    }, {} as CardFeedsStatusByDomainID);
 }
 
 function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean): CombinedCardFeeds {
     return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feeds]) => {
         const domainID = Number(onyxKey.split('_').at(-1));
 
-        const {settings: feedSettings, ...feedStatus} = feeds ?? ({} as CardFeeds);
-        const companyCards = feedSettings?.companyCards;
+        const workspaceFeedsSettings = feeds?.settings;
+        const companyCards = workspaceFeedsSettings?.companyCards;
 
         if (!companyCards) {
             return acc;
@@ -539,8 +539,8 @@ function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> |
 
         for (const feedName of Object.keys(companyCards) as CompanyCardFeedWithNumber[]) {
             const feedSettings = companyCards?.[feedName];
-            const oAuthAccountDetails = feeds?.settings.oAuthAccountDetails?.[feedName];
-            const customFeedName = feeds?.settings.companyCardNicknames?.[feedName];
+            const oAuthAccountDetails = workspaceFeedsSettings?.oAuthAccountDetails?.[feedName];
+            const customFeedName = workspaceFeedsSettings?.companyCardNicknames?.[feedName];
 
             if (!domainID) {
                 continue;
@@ -583,5 +583,5 @@ export {
     getCardFeedsForDisplayPerPolicy,
     getCombinedCardFeedsFromAllFeeds,
     getCardFeedStatus,
-    getAllCardFeedsStatus,
+    getWorkspaceCardFeedsStatus,
 };
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 0751ca272cdc..257897240253 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,11 +1,10 @@
-import type {LETTER_DEFAULTS} from '@libs/Avatars/PresetAvatarCatalog';
 import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
 import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
-import type {CardErrors, CardFeedErrorsObject, FeedErrors} from '@src/types/onyx/DerivedValues';
+import type {CardErrors, CardFeedErrorsObject} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 
 export default createOnyxDerivedValueConfig({
@@ -49,7 +48,7 @@ export default createOnyxDerivedValueConfig({
             }
 
             const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
-            let isFeedConnectionBroken = isCardConnectionBroken(card);
+            const isFeedConnectionBroken = isCardConnectionBroken(card);
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
 
             if (isFeedConnectionBroken) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index 95ff74929c21..81627e01a2e0 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -57,7 +57,7 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         feedName,
         cardList,
         assignedCards,
-        cardFeedsStatus,
+        workspaceCardFeedsStatus: cardFeedsStatus,
         cardNames,
         cardFeedType,
         selectedFeed,
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index ca4eb221e381..5723d49b67d9 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -173,6 +173,11 @@ type CardFeedsStatus = {
     errors?: OnyxCommon.Errors;
 };
 
+/**
+ * Collection of card feeds status by domain ID
+ */
+type CardFeedsStatusByDomainID = Record<number, CardFeedsStatus>;
+
 /** Card feeds model, including domain settings */
 type CardFeeds = {
     /** Feed settings */
@@ -284,6 +289,7 @@ export type {
     CardFeedProvider,
     CardFeedData,
     CardFeedsStatus,
+    CardFeedsStatusByDomainID,
     CompanyFeeds,
     CompanyCardFeedWithDomainID,
     CustomCardFeedData,

From 4b3612b0bc8d5cd7633bd35cff6b0f80166dbc28 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 19:42:55 +0000
Subject: [PATCH 054/122] fix: include workspace card feed errors in derived
 value

---
 .../OnyxDerived/configs/cardFeedErrors.ts     | 14 +++++++++++--
 src/types/onyx/DerivedValues.ts               | 21 +++++++++++++++----
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 257897240253..0ebaa8fbe212 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,4 +1,4 @@
-import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
+import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
 import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -16,6 +16,7 @@ export default createOnyxDerivedValueConfig({
         const rbrFeedNameWithDomainIDMapping: Record<string, boolean> = {};
         let isSomeFeedConnectionBroken = false;
         let hasSomeFeedErrors = false;
+        let hasSomeWorkspaceErrors = false;
         let hasSomeFailedCardAssignment = false;
 
         function addErrorsForCard(card: Card) {
@@ -24,6 +25,8 @@ export default createOnyxDerivedValueConfig({
             const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
             const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
+            const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
+
             const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
 
             const hasFailedCardAssignments = !isEmptyObject(
@@ -47,6 +50,7 @@ export default createOnyxDerivedValueConfig({
                 };
             }
 
+            const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
             const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
             const isFeedConnectionBroken = isCardConnectionBroken(card);
             const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
@@ -59,6 +63,10 @@ export default createOnyxDerivedValueConfig({
                 hasSomeFeedErrors = true;
             }
 
+            if (hasWorkspaceErrors) {
+                hasSomeWorkspaceErrors = true;
+            }
+
             if (hasFailedCardAssignments) {
                 hasSomeFailedCardAssignment = true;
             }
@@ -69,9 +77,10 @@ export default createOnyxDerivedValueConfig({
 
             allFeedsErrors[bankName] = {
                 shouldShowRBR,
-                hasFailedCardAssignments,
+                hasWorkspaceErrors,
                 hasFeedError,
                 isFeedConnectionBroken,
+                hasFailedCardAssignments,
                 cardErrors,
             };
 
@@ -111,6 +120,7 @@ export default createOnyxDerivedValueConfig({
             shouldShowRBR,
             isFeedConnectionBroken: isSomeFeedConnectionBroken,
             hasFeedErrors: hasSomeFeedErrors,
+            hasWorkspaceErrors: hasSomeWorkspaceErrors,
             hasFailedCardAssignment: hasSomeFailedCardAssignment,
         };
     },
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 818484f93241..0a3749ac21f0 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -95,7 +95,12 @@ type CardErrors = {
  */
 type FeedErrors = {
     /**
-     *
+     * Whether to show the RBR for a specific feed within a workspace/domain.
+     * This will be true, if any of the below conditions are true:
+     * - There are failed card assignments for the feed.
+     * - There are errors for the workspace/domain.
+     * - There are errors for the feed.
+     * - The feed connection is broken.
      */
     shouldShowRBR: boolean;
     /**
@@ -103,15 +108,19 @@ type FeedErrors = {
      */
     hasFailedCardAssignments: boolean;
     /**
-     *
+     * Whether some workspace errors.
+     */
+    hasWorkspaceErrors: boolean;
+    /**
+     * Whether a specific feed within a workspace/domain has errors.
      */
     hasFeedError: boolean;
     /**
-     *
+     * Whether some feed connection is broken.
      */
     isFeedConnectionBroken: boolean;
     /**
-     *
+     * The errors of all cards for a specific feed within a workspace/domain.
      */
     cardErrors: Record<string, CardErrors>;
 };
@@ -159,6 +168,10 @@ type CardFeedErrors = {
      * Whether some feed has errors.
      */
     hasFeedErrors: boolean;
+    /**
+     * Whether some workspace has errors.
+     */
+    hasWorkspaceErrors: boolean;
     /**
      * Whether some failed card assignments.
      */

From 86127529ba6b516cd7266d2aa71cdf8b0d9687fa Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 20:08:31 +0000
Subject: [PATCH 055/122] fix: use `splitCompanyCardFeedWithDomainID` instead
 of manually splitting card feed names

---
 src/hooks/useCardsList.tsx | 6 +++---
 src/libs/CardUtils.ts      | 8 ++++++--
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx
index 1a65b7a033c5..0110e0c7dbef 100644
--- a/src/hooks/useCardsList.tsx
+++ b/src/hooks/useCardsList.tsx
@@ -1,5 +1,5 @@
 import type {ResultMetadata} from 'react-native-onyx';
-import {filterInactiveCards} from '@libs/CardUtils';
+import {filterInactiveCards, splitCompanyCardFeedWithDomainID} from '@libs/CardUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
@@ -7,8 +7,8 @@ import useOnyx from './useOnyx';
 
 /* Custom hook that retrieves a list of company cards for the given selected feed. */
 const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [WorkspaceCardsList | undefined, ResultMetadata<WorkspaceCardsList>] => {
-    const [feed, domainOrWorkspaceAccountID] = selectedFeed?.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR) ?? [];
-    const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feed}`, {
+    const {feedName, domainID} = splitCompanyCardFeedWithDomainID(selectedFeed);
+    const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainID}_${feedName}`, {
         selector: filterInactiveCards,
         canBeMissing: true,
     });
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 1ef0efbaee24..1bb9f359cdd9 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -561,7 +561,11 @@ function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithNumber, dom
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
-function splitCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithDomainID): {feedName: CompanyCardFeed; domainID: number | undefined} {
+function splitCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithDomainID | undefined): {feedName: CompanyCardFeed | undefined; domainID: number | undefined} {
+    if (!feedName) {
+        return {feedName: undefined, domainID: undefined};
+    }
+
     const feedNameParts = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR);
 
     if (feedNameParts.length !== 2) {
@@ -845,7 +849,7 @@ function getCompanyCardFeed(feedWithDomainID: string | undefined): CompanyCardFe
         return '' as CompanyCardFeed;
     }
 
-    return splitCompanyCardFeedWithDomainID(feedWithDomainID as CompanyCardFeedWithDomainID).feedName;
+    return splitCompanyCardFeedWithDomainID(feedWithDomainID as CompanyCardFeedWithDomainID).feedName ?? ('' as CompanyCardFeed);
 }
 
 /**

From b50aa99ba62617b572036e42f497dc1f524e1860 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 20:08:39 +0000
Subject: [PATCH 056/122] test: add unit tests for
 `splitCompanyCardFeedWithDomainID`

---
 tests/unit/CardUtilsTest.ts | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 91861fce2b26..08ca41e0bef4 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -39,6 +39,7 @@ import {
     lastFourNumbersFromCardName,
     maskCardNumber,
     sortCardsByCardholderName,
+    splitCompanyCardFeedWithDomainID,
     splitMaskedCardNumber,
 } from '@src/libs/CardUtils';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx';
@@ -1361,6 +1362,15 @@ describe('CardUtils', () => {
         });
     });
 
+    describe('splitCompanyCardFeedWithDomainID', () => {
+        it('should split the feed name and domain ID', () => {
+            const feedName = 'vcf#11111111';
+            const {feedName: splitFeedName, domainID} = splitCompanyCardFeedWithDomainID(feedName);
+            expect(splitFeedName).toBe('visa');
+            expect(domainID).toBe(11111111);
+        });
+    });
+
     describe('getPlaidInstitutionId', () => {
         it('should return institution ID from plaid feed name without domain ID', () => {
             const feedName = 'plaid.ins_123456';

From 5e17598580e7e11c9c6a05e9afe3e0ee342d6fec Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 20:08:50 +0000
Subject: [PATCH 057/122] remove unused import

---
 src/hooks/useCardsList.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx
index 0110e0c7dbef..023f61623d40 100644
--- a/src/hooks/useCardsList.tsx
+++ b/src/hooks/useCardsList.tsx
@@ -1,6 +1,5 @@
 import type {ResultMetadata} from 'react-native-onyx';
 import {filterInactiveCards, splitCompanyCardFeedWithDomainID} from '@libs/CardUtils';
-import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
 import useOnyx from './useOnyx';

From ae2f0a7c38a6927cdc185c2cb5194750e289824a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 20:17:22 +0000
Subject: [PATCH 058/122] fix: use `useCardsList` instead of manual cards onyx
 key lookup

---
 src/hooks/useAssignCard.ts | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts
index 7e51bd999a29..90aeb1c19f0b 100644
--- a/src/hooks/useAssignCard.ts
+++ b/src/hooks/useAssignCard.ts
@@ -24,6 +24,7 @@ import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard';
 import {getEmptyObject} from '@src/types/utils/EmptyObject';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
+import useCardsList from './useCardsList';
 import useIsAllowedToIssueCompanyCard from './useIsAllowedToIssueCompanyCard';
 import useNetwork from './useNetwork';
 import useOnyx from './useOnyx';
@@ -41,7 +42,6 @@ type UseAssignCardProps = {
 };
 
 function useAssignCard({feedName, policyID, setShouldShowOfflineModal}: UseAssignCardProps) {
-    const [allFeedsCards] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: false});
     const [cardFeeds] = useCardFeeds(policyID);
     const companyFeeds = getCompanyFeeds(cardFeeds);
     const currentFeedData = feedName ? companyFeeds?.[feedName] : ({} as CombinedCardFeed);
@@ -59,9 +59,8 @@ function useAssignCard({feedName, policyID, setShouldShowOfflineModal}: UseAssig
 
     const {isOffline} = useNetwork({onReconnect: fetchCompanyCards});
 
-    const cardList = allFeedsCards?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${feedName}`];
-
-    const filteredFeedCards = filterInactiveCards(cardList);
+    const [cardsList] = useCardsList(feedName);
+    const filteredFeedCards = filterInactiveCards(cardsList);
     const hasFeedError = feedName ? !!cardFeeds?.[feedName]?.errors : false;
     const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError;
     const isAllowedToIssueCompanyCard = useIsAllowedToIssueCompanyCard({policyID});

From 7b57a43bb737916665dc3c790479fb61db4db55c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 20:30:40 +0000
Subject: [PATCH 059/122] fix: duplicate RBR in payment method list

---
 .../settings/Wallet/PaymentMethodList.tsx     | 25 +++++++------------
 .../settings/Wallet/PaymentMethodListItem.tsx |  3 +--
 2 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 969d8873afd8..bc47b855a4fb 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -198,8 +198,6 @@ function PaymentMethodList({
                 const isDisabled = card.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
                 const icon = getCardFeedIcon(card.bank as CompanyCardFeed, illustrations, companyCardFeedIcons);
 
-                let brickRoadIndicator: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;
-
                 let shouldShowRBR = false;
                 if (card.fundID) {
                     const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeedWithNumber, card.fundID);
@@ -208,12 +206,15 @@ function PaymentMethodList({
                     shouldShowRBR = true;
                 }
 
-                if (shouldShowRBR) {
-                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
-                } else if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
-                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
-                } else if (isExpensifyCardPendingAction(card, privatePersonalDetails)) {
-                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+                let brickRoadIndicator: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;
+                if (!card.errors) {
+                    if (shouldShowRBR) {
+                        brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                    } else if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL) {
+                        brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                    } else if (isExpensifyCard(card) && isExpensifyCardPendingAction(card, privatePersonalDetails)) {
+                        brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
+                    }
                 }
 
                 if (!isExpensifyCard(card)) {
@@ -229,7 +230,6 @@ function PaymentMethodList({
                             : getDescriptionForPolicyDomainCard(card.domainName),
                         interactive: !isDisabled,
                         disabled: isDisabled,
-                        canDismissError: false,
                         shouldShowRightIcon,
                         errors: card.errors,
                         pendingAction: card.pendingAction,
@@ -278,12 +278,6 @@ function PaymentMethodList({
 
                 const pressHandler = onPress as CardPressHandler;
 
-                if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL || !!card.errors) {
-                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
-                } else if (isExpensifyCardPendingAction(card, privatePersonalDetails)) {
-                    brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
-                }
-
                 // The card shouldn't be grouped or it's domain group doesn't exist yet
                 const cardDescription =
                     card?.nameValuePairs?.issuedBy && card?.lastFourPAN
@@ -312,7 +306,6 @@ function PaymentMethodList({
                     shouldShowRightIcon: true,
                     interactive: !isDisabled,
                     disabled: isDisabled,
-                    canDismissError: true,
                     errors: card.errors,
                     pendingAction: card.pendingAction,
                     brickRoadIndicator,
diff --git a/src/pages/settings/Wallet/PaymentMethodListItem.tsx b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
index e2c5c38884c1..2ca43ed6ad41 100644
--- a/src/pages/settings/Wallet/PaymentMethodListItem.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
@@ -25,7 +25,6 @@ type PaymentMethodItem = PaymentMethod & {
     description: string;
     onPress?: (e: GestureResponderEvent | KeyboardEvent | undefined) => void;
     isGroupedCardDomain?: boolean;
-    canDismissError?: boolean;
     disabled?: boolean;
     shouldShowRightIcon?: boolean;
     interactive?: boolean;
@@ -113,7 +112,7 @@ function PaymentMethodListItem({item, shouldShowDefaultBadge, threeDotsMenuItems
 
     return (
         <OfflineWithFeedback
-            onClose={item.canDismissError ? () => dismissError(item) : undefined}
+            onClose={() => dismissError(item)}
             pendingAction={item.pendingAction}
             errors={item.errors}
             errorRowStyles={styles.ph6}

From 2245c12bf9aefad8eea4ffaff70fec528d3b7696 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 22:38:29 +0000
Subject: [PATCH 060/122] fix: add missing property on `CardFeedErrors` type

---
 src/hooks/useCardFeedErrors.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 62265b03dd0c..779486518add 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -9,6 +9,7 @@ const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     rbrFeedNameWithDomainIDMapping: {},
     isFeedConnectionBroken: false,
     hasFeedErrors: false,
+    hasWorkspaceErrors: false,
     hasFailedCardAssignment: false,
 };
 

From 141040f5c72c0b4be75f679394de36278822ced9 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 23:04:02 +0000
Subject: [PATCH 061/122] fix: broken test

---
 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 08ca41e0bef4..0800d00ae5b9 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -1366,7 +1366,7 @@ describe('CardUtils', () => {
         it('should split the feed name and domain ID', () => {
             const feedName = 'vcf#11111111';
             const {feedName: splitFeedName, domainID} = splitCompanyCardFeedWithDomainID(feedName);
-            expect(splitFeedName).toBe('visa');
+            expect(splitFeedName).toBe('vcf');
             expect(domainID).toBe(11111111);
         });
     });

From 819a3b4e321ada4fa8ae4c69c0da08a2b498d61d Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Thu, 15 Jan 2026 23:21:54 +0000
Subject: [PATCH 062/122] fix: only allow dismissing expensify cards

---
 src/pages/settings/Wallet/PaymentMethodList.tsx     | 2 ++
 src/pages/settings/Wallet/PaymentMethodListItem.tsx | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index bc47b855a4fb..4f6c588950e8 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -232,6 +232,7 @@ function PaymentMethodList({
                         disabled: isDisabled,
                         shouldShowRightIcon,
                         errors: card.errors,
+                        canDismissError: false,
                         pendingAction: card.pendingAction,
                         brickRoadIndicator,
                         icon,
@@ -307,6 +308,7 @@ function PaymentMethodList({
                     interactive: !isDisabled,
                     disabled: isDisabled,
                     errors: card.errors,
+                    canDismissError: true,
                     pendingAction: card.pendingAction,
                     brickRoadIndicator,
                     icon,
diff --git a/src/pages/settings/Wallet/PaymentMethodListItem.tsx b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
index 2ca43ed6ad41..e2c5c38884c1 100644
--- a/src/pages/settings/Wallet/PaymentMethodListItem.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodListItem.tsx
@@ -25,6 +25,7 @@ type PaymentMethodItem = PaymentMethod & {
     description: string;
     onPress?: (e: GestureResponderEvent | KeyboardEvent | undefined) => void;
     isGroupedCardDomain?: boolean;
+    canDismissError?: boolean;
     disabled?: boolean;
     shouldShowRightIcon?: boolean;
     interactive?: boolean;
@@ -112,7 +113,7 @@ function PaymentMethodListItem({item, shouldShowDefaultBadge, threeDotsMenuItems
 
     return (
         <OfflineWithFeedback
-            onClose={() => dismissError(item)}
+            onClose={item.canDismissError ? () => dismissError(item) : undefined}
             pendingAction={item.pendingAction}
             errors={item.errors}
             errorRowStyles={styles.ph6}

From 7fbb0d662b278fa1fc86cc71b1e7828c59904283 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 17:14:33 +0000
Subject: [PATCH 063/122] fix: only show RBR in Account NavigationTabBar if
 user is non-admin of workspace

---
 src/hooks/useAccountTabIndicatorStatus.ts | 29 ++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index 190830100839..2c02173f880d 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -2,10 +2,12 @@ import type {ValueOf} from 'type-fest';
 import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
 import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
 import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {isUserPolicyAdmin} from '@libs/PolicyUtils';
 import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
 import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import type {Policy} from '@src/types/onyx';
 import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
 import useTheme from './useTheme';
@@ -34,9 +36,30 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
     const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
     const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
 
-    const {cardFeedErrors} = useCardFeedErrors();
-    const hasCompanyCardErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR)) !== undefined;
+    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+    // those should be cleaned out before doing any error checking
+    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
+
+    const {rbrWorkspaceAccountIDMapping} = useCardFeedErrors();
+    let hasCompanyCardErrors = false;
+    const policiesWithCardFeedErrors: Policy[] = [];
+    for (const [workspaceAccountID, value] of Object.entries(rbrWorkspaceAccountIDMapping)) {
+        if (!value) {
+            continue;
+        }
+        hasCompanyCardErrors = true;
+
+        const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
+        if (!policyWithCardFeedErrors) {
+            continue;
+        }
+
+        policiesWithCardFeedErrors.push(policyWithCardFeedErrors);
+    }
+
+    const isWorkspaceAdmin = policiesWithCardFeedErrors.some((policy) => isUserPolicyAdmin(policy, session?.email));
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
     // early as soon as the first error / info condition is returned. This makes the checks very efficient since
@@ -58,7 +81,7 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
             fundList,
             billingStatus,
         ),
-        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: hasCompanyCardErrors,
+        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: !isWorkspaceAdmin && hasCompanyCardErrors,
     };
 
     const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {

From ea03ae6684e91b4db5c1b0a0e8c005bf21699fcd Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 17:14:54 +0000
Subject: [PATCH 064/122] refactor: remove redundant object initialization

---
 src/hooks/useWorkspacesTabIndicatorStatus.ts | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index ca4b3c329dc8..27e024acd431 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -26,18 +26,18 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
 
     // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
     // those should be cleaned out before doing any error checking
-    const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
+    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
     const policyErrors = {
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
-        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
-        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: cleanPolicies.find(shouldShowPolicyError),
+        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: cleanPolicies.find(shouldShowCustomUnitsError),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: cleanPolicies.find(shouldShowEmployeeListError),
+        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: cleanPolicies.find((cleanPolicy) =>
             shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
         ),
-        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
-        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
-        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: Object.values(cleanPolicies).find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
+        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
+        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
     };
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return

From b24138605ab80b3786d51a4be3961f82d2fdf4a6 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 18:04:35 +0000
Subject: [PATCH 065/122] feat: extract duplicate code into separate hook

---
 src/CONST/index.ts                            |   2 +-
 src/components/Navigation/DebugTabView.tsx    |   2 +-
 src/hooks/useAccountTabIndicatorStatus.ts     | 101 +---------------
 src/hooks/useIndicatorStatus.ts               |  92 +-------------
 .../useNavigationTabBarIndicatorChecks.ts     | 113 ++++++++++++++++++
 src/hooks/usePoliciesWithCardFeedErrors.ts    |  45 +++++++
 src/hooks/useWorkspacesTabIndicatorStatus.ts  |  49 +-------
 tests/unit/useIndicatorStatusTest.tsx         |   2 +-
 8 files changed, 178 insertions(+), 228 deletions(-)
 create mode 100644 src/hooks/useNavigationTabBarIndicatorChecks.ts
 create mode 100644 src/hooks/usePoliciesWithCardFeedErrors.ts

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index e8392187f24f..834b75d2e4fc 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7573,7 +7573,7 @@ const CONST = {
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
-        HAS_COMPANY_CARD_ERRORS: 'hasCompanyCardErrors',
+        HAS_CARD_FEED_ERRORS: 'hasCompanyCardErrors',
     },
 
     DEBUG: {
diff --git a/src/components/Navigation/DebugTabView.tsx b/src/components/Navigation/DebugTabView.tsx
index 19248e166673..2af72a9b1107 100644
--- a/src/components/Navigation/DebugTabView.tsx
+++ b/src/components/Navigation/DebugTabView.tsx
@@ -7,9 +7,9 @@ import Button from '@components/Button';
 import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
 import Text from '@components/Text';
-import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
 import useLocalize from '@hooks/useLocalize';
+import type {IndicatorStatus} from '@hooks/useNavigationTabBarIndicatorChecks';
 import useOnyx from '@hooks/useOnyx';
 import {useSidebarOrderedReports} from '@hooks/useSidebarOrderedReports';
 import useStyleUtils from '@hooks/useStyleUtils';
diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index 2c02173f880d..480a814f41b9 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -1,108 +1,19 @@
-import type {ValueOf} from 'type-fest';
-import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
-import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
-import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
-import {isUserPolicyAdmin} from '@libs/PolicyUtils';
-import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
-import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import type {Policy} from '@src/types/onyx';
-import useCardFeedErrors from './useCardFeedErrors';
-import useOnyx from './useOnyx';
+import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
+import type {IndicatorStatus} from './useNavigationTabBarIndicatorChecks';
 import useTheme from './useTheme';
 
-type AccountTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
-
 type AccountTabIndicatorStatusResult = {
     indicatorColor: string;
-    status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
+    status: IndicatorStatus | undefined;
 };
 
 function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const theme = useTheme();
-    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
-    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
-    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
-    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
-    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
-    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
-    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
-    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
-    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
-    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
-    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
-    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
-    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
-    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
-    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
-    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
-
-    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
-    // those should be cleaned out before doing any error checking
-    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
-
-    const {rbrWorkspaceAccountIDMapping} = useCardFeedErrors();
-    let hasCompanyCardErrors = false;
-    const policiesWithCardFeedErrors: Policy[] = [];
-    for (const [workspaceAccountID, value] of Object.entries(rbrWorkspaceAccountIDMapping)) {
-        if (!value) {
-            continue;
-        }
-        hasCompanyCardErrors = true;
-
-        const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
-        if (!policyWithCardFeedErrors) {
-            continue;
-        }
-
-        policiesWithCardFeedErrors.push(policyWithCardFeedErrors);
-    }
-
-    const isWorkspaceAdmin = policiesWithCardFeedErrors.some((policy) => isUserPolicyAdmin(policy, session?.email));
-
-    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
-    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
-    // we only care if a single error / info condition exists anywhere.
-    const errorChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
-        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
-        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
-        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
-        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
-        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: !isWorkspaceAdmin && hasCompanyCardErrors,
-    };
-
-    const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
-        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO]: hasPartiallySetupBankAccount(bankAccountList),
-    };
 
-    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
-    const [info] = Object.entries(infoChecking).find(([, value]) => value) ?? [];
+    const {accountStatus, infoStatus} = useNavigationTabBarIndicatorChecks();
 
-    const status = (error ?? info) as AccountTabIndicatorStatus | undefined;
-    const indicatorColor = error ? theme.danger : theme.success;
+    const status = accountStatus ?? infoStatus;
+    const indicatorColor = accountStatus ? theme.danger : theme.success;
 
     return {indicatorColor, status};
 }
diff --git a/src/hooks/useIndicatorStatus.ts b/src/hooks/useIndicatorStatus.ts
index 17b3cf6ba191..8b3d81138b26 100644
--- a/src/hooks/useIndicatorStatus.ts
+++ b/src/hooks/useIndicatorStatus.ts
@@ -1,19 +1,8 @@
-import {useMemo} from 'react';
 import type {ValueOf} from 'type-fest';
-import {isConnectionInProgress} from '@libs/actions/connections';
-import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
-import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
-import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
-import {shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
-import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
-import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import useOnyx from './useOnyx';
+import type CONST from '@src/CONST';
+import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
 import useTheme from './useTheme';
 
-type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
-
 type IndicatorStatusResult = {
     indicatorColor: string;
     status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
@@ -22,84 +11,13 @@ type IndicatorStatusResult = {
 
 function useIndicatorStatus(): IndicatorStatusResult {
     const theme = useTheme();
-    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
-    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
-    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
-    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
-    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
-    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
-    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
-    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
-    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
-    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
-    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
-    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
-    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
-    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
-    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
-    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
-    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
-
-    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
-    // those should be cleaned out before doing any error checking
-    const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
-
-    const policyErrors = {
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
-        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
-        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
-            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
-        ),
-        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
-    };
 
-    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
-    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
-    // we only care if a single error / info condition exists anywhere.
-    const errorChecking: Partial<Record<IndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
-        ...(Object.fromEntries(Object.entries(policyErrors).map(([error, policy]) => [error, !!policy])) as Record<keyof typeof policyErrors, boolean>),
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
-        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
-        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
-        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
-        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
-    };
+    const {accountStatus, infoStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
 
-    const infoChecking: Partial<Record<IndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
-    };
-
-    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
-    const [info] = Object.entries(infoChecking).find(([, value]) => value) ?? [];
-
-    const status = (error ?? info) as IndicatorStatus | undefined;
-    const policyIDWithErrors = Object.values(policyErrors).find(Boolean)?.id;
-    const indicatorColor = error ? theme.danger : theme.success;
+    const status = accountStatus ?? infoStatus;
+    const indicatorColor = accountStatus ? theme.danger : theme.success;
 
     return {indicatorColor, status, policyIDWithErrors};
 }
 
 export default useIndicatorStatus;
-
-export type {IndicatorStatus};
diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
new file mode 100644
index 000000000000..084e90e80a65
--- /dev/null
+++ b/src/hooks/useNavigationTabBarIndicatorChecks.ts
@@ -0,0 +1,113 @@
+import type {ValueOf} from 'type-fest';
+import {isConnectionInProgress} from '@libs/actions/connections';
+import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
+import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
+import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
+import {hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
+import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
+import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Policy} from '@src/types/onyx';
+import useOnyx from './useOnyx';
+import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
+
+type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
+
+type NavigationTabBarChecksResult = {
+    accountStatus: IndicatorStatus | undefined;
+    policyStatus: IndicatorStatus | undefined;
+    infoStatus: IndicatorStatus | undefined;
+    policyIDWithErrors: string | undefined;
+};
+
+function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
+    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
+    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
+    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
+    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
+    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
+    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
+    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
+    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
+    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
+    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
+    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
+    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
+    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
+    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
+    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
+
+    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+    // those should be cleaned out before doing any error checking
+    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
+
+    const {policiesWithCardFeedErrors, isPolicyAdmin, hasCardFeedErrors} = usePoliciesWithCardFeedErrors();
+
+    const policyChecks: Partial<Record<IndicatorStatus, Policy | undefined>> = {
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: cleanPolicies.find(shouldShowPolicyError),
+        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: cleanPolicies.find(shouldShowCustomUnitsError),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: cleanPolicies.find(shouldShowEmployeeListError),
+        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: cleanPolicies.find((cleanPolicy) =>
+            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+        ),
+        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
+        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
+    };
+
+    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
+    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
+    // we only care if a single error / info condition exists anywhere.
+    const accountChecks: Partial<Record<IndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
+        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
+        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
+        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
+        [CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS]: !isPolicyAdmin && hasCardFeedErrors,
+    };
+
+    const infoChecks: Partial<Record<IndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
+        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+        [CONST.INDICATOR_STATUS.HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO]: hasPartiallySetupBankAccount(bankAccountList),
+    };
+
+    const [accountStatus] = Object.entries(accountChecks).find(([, value]) => value) ?? [];
+    const [policyStatus] = Object.entries(policyChecks).find(([, value]) => value) ?? [];
+    const [infoStatus] = Object.entries(infoChecks).find(([, value]) => value) ?? [];
+
+    const policyIDWithErrors = Object.values(policyChecks).find(Boolean)?.id;
+
+    return {
+        accountStatus: accountStatus as IndicatorStatus | undefined,
+        policyStatus: policyStatus as IndicatorStatus | undefined,
+        infoStatus: infoStatus as IndicatorStatus | undefined,
+        policyIDWithErrors,
+    };
+}
+
+export default useNavigationTabBarIndicatorChecks;
+
+export type {IndicatorStatus};
diff --git a/src/hooks/usePoliciesWithCardFeedErrors.ts b/src/hooks/usePoliciesWithCardFeedErrors.ts
new file mode 100644
index 000000000000..5a4b23245abc
--- /dev/null
+++ b/src/hooks/usePoliciesWithCardFeedErrors.ts
@@ -0,0 +1,45 @@
+import {isPolicyAdmin} from '@libs/PolicyUtils';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Policy} from '@src/types/onyx';
+import useCardFeedErrors from './useCardFeedErrors';
+import useOnyx from './useOnyx';
+
+type PoliciesWithCardFeedErrors = {
+    policiesWithCardFeedErrors: Policy[];
+    hasCardFeedErrors: boolean;
+    isPolicyAdmin: boolean;
+};
+
+function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
+    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
+    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
+
+    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+    // those should be cleaned out before doing any error checking
+    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
+
+    const {rbrWorkspaceAccountIDMapping} = useCardFeedErrors();
+    let hasCardFeedErrors = false;
+    const policiesWithCardFeedErrors: Policy[] = [];
+    for (const [workspaceAccountID, value] of Object.entries(rbrWorkspaceAccountIDMapping)) {
+        if (!value) {
+            continue;
+        }
+        hasCardFeedErrors = true;
+
+        const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
+        if (!policyWithCardFeedErrors) {
+            continue;
+        }
+
+        policiesWithCardFeedErrors.push(policyWithCardFeedErrors);
+    }
+
+    return {
+        policiesWithCardFeedErrors,
+        hasCardFeedErrors,
+        isPolicyAdmin: policiesWithCardFeedErrors.some((policy) => isPolicyAdmin(policy, session?.email)),
+    };
+}
+
+export default usePoliciesWithCardFeedErrors;
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index 27e024acd431..d5a705202a7c 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -1,57 +1,20 @@
-import type {ValueOf} from 'type-fest';
-import {isConnectionInProgress} from '@libs/actions/connections';
-import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
-import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import useCardFeedErrors from './useCardFeedErrors';
-import useOnyx from './useOnyx';
+import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
+import type {IndicatorStatus} from './useNavigationTabBarIndicatorChecks';
 import useTheme from './useTheme';
 
-type WorkspacesTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
-
 type WorkspacesTabIndicatorStatusResult = {
     indicatorColor: string;
-    status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
+    status: IndicatorStatus | undefined;
     policyIDWithErrors: string | undefined;
 };
 
 function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     const theme = useTheme();
-    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
-    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
-
-    const {cardFeedErrors} = useCardFeedErrors();
-    const workspaceAccountIDWithErrors = Object.entries(cardFeedErrors ?? {}).find(([, feeds]) => Object.values(feeds).some((feed) => feed.shouldShowRBR))?.[0];
-
-    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
-    // those should be cleaned out before doing any error checking
-    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
-
-    const policyErrors = {
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: cleanPolicies.find(shouldShowPolicyError),
-        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: cleanPolicies.find(shouldShowCustomUnitsError),
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: cleanPolicies.find(shouldShowEmployeeListError),
-        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: cleanPolicies.find((cleanPolicy) =>
-            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
-        ),
-        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
-        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
-        [CONST.INDICATOR_STATUS.HAS_COMPANY_CARD_ERRORS]: cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountIDWithErrors)),
-    };
-
-    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
-    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
-    // we only care if a single error / info condition exists anywhere.
-    const errorChecking: Partial<Record<WorkspacesTabIndicatorStatus, boolean>> = {
-        ...(Object.fromEntries(Object.entries(policyErrors).map(([error, policy]) => [error, !!policy])) as Record<keyof typeof policyErrors, boolean>),
-    };
 
-    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
+    const {policyStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
 
-    const status = error as WorkspacesTabIndicatorStatus | undefined;
-    const policyIDWithErrors = Object.values(policyErrors).find(Boolean)?.id;
-    const indicatorColor = error ? theme.danger : theme.success;
+    const status = policyStatus;
+    const indicatorColor = policyStatus ? theme.danger : theme.success;
 
     return {indicatorColor, status, policyIDWithErrors};
 }
diff --git a/tests/unit/useIndicatorStatusTest.tsx b/tests/unit/useIndicatorStatusTest.tsx
index 69f4ac51503a..e25242628d86 100644
--- a/tests/unit/useIndicatorStatusTest.tsx
+++ b/tests/unit/useIndicatorStatusTest.tsx
@@ -1,8 +1,8 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
-import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
+import type {IndicatorStatus} from '@hooks/useNavigationTabBarIndicatorChecks';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';

From 62b41fbf660aed35967b824bc94b334cf1017cf9 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 18:05:14 +0000
Subject: [PATCH 066/122] fix: remove duplicate `isUserPolicyAdmin` util
 function

---
 src/libs/PolicyUtils.ts             | 7 +------
 src/libs/actions/Policy/Member.ts   | 4 ++--
 src/pages/RoomMemberDetailsPage.tsx | 4 ++--
 src/pages/RoomMembersPage.tsx       | 4 ++--
 4 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 203735d086d5..61dbdc603203 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -110,7 +110,7 @@ function getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(policies:
 /**
  * Checks if the current user is an admin of the policy.
  */
-const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;
+const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string): boolean => getPolicyRole(policy, login) === CONST.POLICY.ROLE.ADMIN;
 
 /**
  * Checks if we have any errors stored within the policy?.employeeList. Determines whether we should show a red brick road error or not.
@@ -428,11 +428,6 @@ function isExpensifyTeam(email: string | undefined): boolean {
     return emailDomain === CONST.EXPENSIFY_PARTNER_NAME || emailDomain === CONST.EMAIL.GUIDES_DOMAIN;
 }
 
-/**
- * Checks if the user with login is an admin of the policy.
- */
-const isUserPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string) => !!(policy && policy.employeeList && login && policy.employeeList[login]?.role === CONST.POLICY.ROLE.ADMIN);
-
 /**
  * Checks if the current user is of the role "user" on the policy.
  */
diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts
index 2baa86a2e902..084d1a058870 100644
--- a/src/libs/actions/Policy/Member.ts
+++ b/src/libs/actions/Policy/Member.ts
@@ -22,7 +22,7 @@ import Parser from '@libs/Parser';
 import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
 import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
 import * as PhoneNumber from '@libs/PhoneNumber';
-import {getDefaultApprover, isUserPolicyAdmin} from '@libs/PolicyUtils';
+import {getDefaultApprover, isPolicyAdmin} from '@libs/PolicyUtils';
 import * as ReportActionsUtils from '@libs/ReportActionsUtils';
 import * as ReportUtils from '@libs/ReportUtils';
 import * as FormActions from '@userActions/FormActions';
@@ -276,7 +276,7 @@ function resetAccountingPreferredExporter(policy: OnyxEntry<Policy>, loginList:
     }
     const owner = policy?.owner ?? ReportUtils.getPersonalDetailsForAccountID(policy?.ownerAccountID).login ?? '';
     const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policy.id}` as const;
-    const adminLoginList = loginList.filter((login) => isUserPolicyAdmin(policy, login));
+    const adminLoginList = loginList.filter((login) => isPolicyAdmin(policy, login));
     const connections = [CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.POLICY.CONNECTIONS.NAME.QBD];
 
     if (!adminLoginList.length) {
diff --git a/src/pages/RoomMemberDetailsPage.tsx b/src/pages/RoomMemberDetailsPage.tsx
index 04ddd3e0bc48..2c7e3c16aa73 100644
--- a/src/pages/RoomMemberDetailsPage.tsx
+++ b/src/pages/RoomMemberDetailsPage.tsx
@@ -18,7 +18,7 @@ import {removeFromRoom} from '@libs/actions/Report';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types';
 import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
-import {isUserPolicyAdmin} from '@libs/PolicyUtils';
+import {isPolicyAdmin} from '@libs/PolicyUtils';
 import {isPolicyExpenseChat} from '@libs/ReportUtils';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
@@ -52,7 +52,7 @@ function RoomMemberDetailsPage({report, route}: RoomMemberDetailsPagePageProps)
     const displayName = formatPhoneNumber(getDisplayNameOrDefault(details));
     const isSelectedMemberCurrentUser = accountID === currentUserPersonalDetails?.accountID;
     const isSelectedMemberOwner = accountID === report.ownerAccountID;
-    const shouldDisableRemoveUser = (isPolicyExpenseChat(report) && isUserPolicyAdmin(policy, details.login)) || isSelectedMemberCurrentUser || isSelectedMemberOwner;
+    const shouldDisableRemoveUser = (isPolicyExpenseChat(report) && isPolicyAdmin(policy, details.login)) || isSelectedMemberCurrentUser || isSelectedMemberOwner;
     const removeUser = useCallback(() => {
         setIsRemoveMemberConfirmModalVisible(false);
         removeFromRoom(report?.reportID, [accountID]);
diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx
index 3ba50430bd8c..437516145011 100644
--- a/src/pages/RoomMembersPage.tsx
+++ b/src/pages/RoomMembersPage.tsx
@@ -35,7 +35,7 @@ import type {PlatformStackRouteProp, PlatformStackScreenProps} from '@libs/Navig
 import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types';
 import {isPersonalDetailsReady, isSearchStringMatchUserDetails} from '@libs/OptionsListUtils';
 import {getDisplayNameOrDefault, getPersonalDetailsByIDs} from '@libs/PersonalDetailsUtils';
-import {isPolicyEmployee as isPolicyEmployeeUtils, isUserPolicyAdmin} from '@libs/PolicyUtils';
+import {isPolicyAdmin, isPolicyEmployee as isPolicyEmployeeUtils} from '@libs/PolicyUtils';
 import {getReportName, getReportPersonalDetailsParticipants, isChatThread, isDefaultRoom, isPolicyExpenseChat as isPolicyExpenseChatUtils, isUserCreatedPolicyRoom} from '@libs/ReportUtils';
 import StringUtils from '@libs/StringUtils';
 import {clearAddRoomMemberError, openRoomMembersPage, removeFromRoom} from '@userActions/Report';
@@ -256,7 +256,7 @@ function RoomMembersPage({report, policy}: RoomMembersPageProps) {
                 continue;
             }
             const pendingChatMember = reportMetadata?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString());
-            const isAdmin = isUserPolicyAdmin(policy, details.login);
+            const isAdmin = isPolicyAdmin(policy, details.login);
             const isDisabled = pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || details.isOptimisticPersonalDetail;
             const isDisabledCheckbox =
                 (isPolicyExpenseChat && isAdmin) ||

From 193a8ee1d8081063a8859e1032f3f75a1bcc8d5f Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 18:12:50 +0000
Subject: [PATCH 067/122] fix: remove export

---
 src/libs/PolicyUtils.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 61dbdc603203..d3e1b73168f6 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -1725,7 +1725,6 @@ export {
     getCorrectedAutoReportingFrequency,
     isPaidGroupPolicy,
     isPendingDeletePolicy,
-    isUserPolicyAdmin,
     isPolicyAdmin,
     isPolicyUser,
     isPolicyAuditor,

From e9af644c5d345fe5568106ad3ad25bfca9caba89 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 16 Jan 2026 18:13:02 +0000
Subject: [PATCH 068/122] fix: show RBR in workspaces list for company card
 feed errors

---
 src/pages/workspace/WorkspacesListPage.tsx | 42 ++++++++++++++++------
 1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx
index 70c9d4781672..62c3b23c49f1 100755
--- a/src/pages/workspace/WorkspacesListPage.tsx
+++ b/src/pages/workspace/WorkspacesListPage.tsx
@@ -32,6 +32,7 @@ import useLocalize from '@hooks/useLocalize';
 import useNetwork from '@hooks/useNetwork';
 import useOnyx from '@hooks/useOnyx';
 import usePayAndDowngrade from '@hooks/usePayAndDowngrade';
+import usePoliciesWithCardFeedErrors from '@hooks/usePoliciesWithCardFeedErrors';
 import usePreferredPolicy from '@hooks/usePreferredPolicy';
 import usePrevious from '@hooks/usePrevious';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
@@ -533,6 +534,8 @@ function WorkspacesListPage() {
         Navigation.navigate(ROUTES.DOMAIN_INITIAL.getRoute(domainAccountID));
     }, []);
 
+    const {policiesWithCardFeedErrors} = usePoliciesWithCardFeedErrors();
+
     /**
      * Add free policies (workspaces) to the list of menu items and returns the list of menu items
      */
@@ -547,6 +550,24 @@ function WorkspacesListPage() {
             .map((policy): WorkspaceItem => {
                 const receiptUberBrickRoadIndicator = getUberConnectionErrorDirectlyFromPolicy(policy as OnyxEntry<PolicyType>) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined;
 
+                let brickRoadIndicator: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | undefined;
+                if (isPolicyAdmin(policy, session?.email)) {
+                    const indicator = reimbursementAccountBrickRoadIndicator ?? receiptUberBrickRoadIndicator;
+
+                    if (indicator) {
+                        brickRoadIndicator = indicator;
+                    } else if (policiesWithCardFeedErrors.find((p) => p.id === policy.id)) {
+                        brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                    } else if (shouldShowEmployeeListError(policy)) {
+                        brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
+                    } else {
+                        brickRoadIndicator = getPolicyBrickRoadIndicatorStatus(
+                            policy,
+                            isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy),
+                        );
+                    }
+                }
+
                 if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) {
                     const policyInfo = Object.values(policy.policyDetailsForNonMembers).at(0) as PolicyDetailsForNonMembers;
                     const id = Object.keys(policy.policyDetailsForNonMembers).at(0);
@@ -573,15 +594,7 @@ function WorkspacesListPage() {
                     title: policy.name,
                     icon: policy.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy.name),
                     action: () => navigateToWorkspace(policy.id),
-                    brickRoadIndicator: !isPolicyAdmin(policy)
-                        ? undefined
-                        : (reimbursementAccountBrickRoadIndicator ??
-                          receiptUberBrickRoadIndicator ??
-                          (shouldShowEmployeeListError(policy) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined) ??
-                          getPolicyBrickRoadIndicatorStatus(
-                              policy,
-                              isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy),
-                          )),
+                    brickRoadIndicator,
                     pendingAction: policy.pendingAction,
                     errors: policy.errors,
                     dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction),
@@ -596,7 +609,16 @@ function WorkspacesListPage() {
                     employeeList: policy.employeeList,
                 };
             });
-    }, [reimbursementAccount?.errors, policies, session?.email, allConnectionSyncProgresses, theme.textLight, icons.FallbackWorkspaceAvatar, navigateToWorkspace]);
+    }, [
+        reimbursementAccount?.errors,
+        policies,
+        session?.email,
+        theme.textLight,
+        icons.FallbackWorkspaceAvatar,
+        policiesWithCardFeedErrors,
+        allConnectionSyncProgresses,
+        navigateToWorkspace,
+    ]);
 
     const filterWorkspace = useCallback((workspace: WorkspaceItem, inputValue: string) => workspace.title.toLowerCase().includes(inputValue), []);
     const sortWorkspace = useCallback((workspaceItems: WorkspaceItem[]) => workspaceItems.sort((a, b) => localeCompare(a.title, b.title)), [localeCompare]);

From a9f763bc4aac1a5c44ea3e882934456e713f54e8 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 15:24:29 +0000
Subject: [PATCH 069/122] fix: include policy errors in `useIndicatorStatus`

---
 src/hooks/useIndicatorStatus.ts | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/hooks/useIndicatorStatus.ts b/src/hooks/useIndicatorStatus.ts
index 8b3d81138b26..3d5fc0021f53 100644
--- a/src/hooks/useIndicatorStatus.ts
+++ b/src/hooks/useIndicatorStatus.ts
@@ -12,10 +12,11 @@ type IndicatorStatusResult = {
 function useIndicatorStatus(): IndicatorStatusResult {
     const theme = useTheme();
 
-    const {accountStatus, infoStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
+    const {accountStatus, infoStatus, policyStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
 
-    const status = accountStatus ?? infoStatus;
-    const indicatorColor = accountStatus ? theme.danger : theme.success;
+    const errorStatus = accountStatus ?? policyStatus;
+    const status = errorStatus ?? infoStatus;
+    const indicatorColor = errorStatus ? theme.danger : theme.success;
 
     return {indicatorColor, status, policyIDWithErrors};
 }

From 0abcc17581f5b4276d9c1e1f94a784ef41c17971 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 15:24:44 +0000
Subject: [PATCH 070/122] fix: remove `HAS_CARD_CONNECTION_ERROR` indicator
 status

---
 src/CONST/index.ts                             |  1 -
 tests/unit/useAccountTabIndicatorStatusTest.ts | 10 +++++-----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index f09cf7e2e71c..c0e163c63fce 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7578,7 +7578,6 @@ const CONST = {
         HAS_LOGIN_LIST_INFO: 'hasLoginListInfo',
         HAS_SUBSCRIPTION_INFO: 'hasSubscriptionInfo',
         HAS_PHONE_NUMBER_ERROR: 'hasPhoneNumberError',
-        HAS_CARD_CONNECTION_ERROR: 'hasCardConnectionError',
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index d5b118320db9..3567c602f54e 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -86,12 +86,12 @@ const getMockForStatus = (status: string) =>
             // eslint-disable-next-line @typescript-eslint/naming-convention
             card123: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS ? 403 : 200,
             },
             // eslint-disable-next-line @typescript-eslint/naming-convention
             card456: {
                 bank: 'ANOTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS ? 403 : 200,
             },
         },
         [ONYXKEYS.SESSION]: {
@@ -132,9 +132,9 @@ const TEST_CASES: TestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS,
     },
     {
-        name: 'has card connection error',
+        name: 'has card feed errors',
         indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR,
+        status: CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS,
     },
     {
         name: 'has phone number error',
@@ -387,7 +387,7 @@ describe('useAccountTabIndicatorStatus', () => {
             await waitForBatchedUpdatesWithAct();
             const {status, indicatorColor} = result.current;
 
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR);
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS);
             expect(indicatorColor).toBe(defaultTheme.danger);
         });
 

From 8f0e3cfa39c2f2750a18c5a94477e25b4b1f2760 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:14:40 +0000
Subject: [PATCH 071/122] fix: add extra flag in `isPolicyAdmin` util function

---
 src/libs/PolicyUtils.ts           | 6 +++---
 src/libs/actions/Policy/Member.ts | 3 ++-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index d3e1b73168f6..ec87953bcd93 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -110,7 +110,7 @@ function getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(policies:
 /**
  * Checks if the current user is an admin of the policy.
  */
-const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string): boolean => getPolicyRole(policy, login) === CONST.POLICY.ROLE.ADMIN;
+const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string, shouldCheckGlobalPolicyRole = true): boolean => getPolicyRole(policy, login, shouldCheckGlobalPolicyRole) === CONST.POLICY.ROLE.ADMIN;
 
 /**
  * Checks if we have any errors stored within the policy?.employeeList. Determines whether we should show a red brick road error or not.
@@ -349,8 +349,8 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>, isConnecti
     return undefined;
 }
 
-function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin: string | undefined): string | undefined {
-    if (policy?.role) {
+function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string, shouldCheckGlobalPolicyRole = true): string | undefined {
+    if (shouldCheckGlobalPolicyRole && policy?.role) {
         return policy.role;
     }
 
diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts
index 084d1a058870..ef77766b47d5 100644
--- a/src/libs/actions/Policy/Member.ts
+++ b/src/libs/actions/Policy/Member.ts
@@ -276,7 +276,8 @@ function resetAccountingPreferredExporter(policy: OnyxEntry<Policy>, loginList:
     }
     const owner = policy?.owner ?? ReportUtils.getPersonalDetailsForAccountID(policy?.ownerAccountID).login ?? '';
     const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policy.id}` as const;
-    const adminLoginList = loginList.filter((login) => isPolicyAdmin(policy, login));
+
+    const adminLoginList = loginList.filter((login) => isPolicyAdmin(policy, login, false));
     const connections = [CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.POLICY.CONNECTIONS.NAME.QBD];
 
     if (!adminLoginList.length) {

From 3cbb22202d90a1842c096e673d414adfec61e21a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:14:53 +0000
Subject: [PATCH 072/122] refactor: rename constant

---
 src/CONST/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index c0e163c63fce..197759a6a860 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7581,7 +7581,7 @@ const CONST = {
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
-        HAS_CARD_FEED_ERRORS: 'hasCompanyCardErrors',
+        HAS_CARD_FEED_ERRORS: 'hasCardFeedErrors',
     },
 
     DEBUG: {

From 24e3d973efa7e595b9067b49b016938384dea5b8 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:53:17 +0000
Subject: [PATCH 073/122] fix: re-structure card feed errors object and extract
 `feedErrors`

---
 src/hooks/useCardFeedErrors.ts                |  1 +
 .../OnyxDerived/configs/cardFeedErrors.ts     | 50 +++++++++----------
 src/types/onyx/CardFeeds.ts                   | 20 ++++++--
 src/types/onyx/DerivedValues.ts               | 25 +++++++---
 4 files changed, 58 insertions(+), 38 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 779486518add..50e96bd7c469 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -4,6 +4,7 @@ import useOnyx from './useOnyx';
 
 const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     cardFeedErrors: {},
+    cardsWithBrokenFeedConnection: {},
     shouldShowRBR: false,
     rbrWorkspaceAccountIDMapping: {},
     rbrFeedNameWithDomainIDMapping: {},
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 0ebaa8fbe212..a20edd677180 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,5 +1,5 @@
 import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
-import {getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
+import {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
@@ -19,24 +19,24 @@ export default createOnyxDerivedValueConfig({
         let hasSomeWorkspaceErrors = false;
         let hasSomeFailedCardAssignment = false;
 
+        const cardsWithBrokenFeedConnection: Record<string, Card> = {};
+
+        const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
+        const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
+
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
             const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
-            const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
-            const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
-
             const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
+            const feedErrors =selectedFeed?.errors
 
             const hasFailedCardAssignments = !isEmptyObject(
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
             );
 
-            const allFeedsErrors = cardFeedErrors[workspaceAccountID] ?? {};
-            const feedErrors = allFeedsErrors[bankName];
-            let cardErrors: Record<string, CardErrors> = feedErrors?.cardErrors ?? {};
-
+            let cardErrors: Record<string, CardErrors> = cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {};
             const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
 
             if (hasCardErrors) {
@@ -51,15 +51,26 @@ export default createOnyxDerivedValueConfig({
             }
 
             const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
-            const hasFeedError = feedNameWithDomainID ? !!selectedFeed?.errors : false;
+            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false;
             const isFeedConnectionBroken = isCardConnectionBroken(card);
-            const shouldShowRBR = hasFailedCardAssignments || hasFeedError || isFeedConnectionBroken;
+            const shouldShowRBR = hasFailedCardAssignments || hasFeedErrors || isFeedConnectionBroken;
+
+            cardFeedErrors[feedNameWithDomainID] = {
+                cardErrors,
+                feedErrors,
+                shouldShowRBR,
+                hasWorkspaceErrors,
+                hasFeedErrors,
+                isFeedConnectionBroken,
+                hasFailedCardAssignments,
+            };
 
             if (isFeedConnectionBroken) {
                 isSomeFeedConnectionBroken = true;
+                cardsWithBrokenFeedConnection[card.cardID] = card;
             }
 
-            if (hasFeedError) {
+            if (hasFeedErrors) {
                 hasSomeFeedErrors = true;
             }
 
@@ -74,17 +85,6 @@ export default createOnyxDerivedValueConfig({
             rbrWorkspaceAccountIDMapping[workspaceAccountID] = rbrWorkspaceAccountIDMapping[workspaceAccountID] || shouldShowRBR;
 
             rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] || shouldShowRBR;
-
-            allFeedsErrors[bankName] = {
-                shouldShowRBR,
-                hasWorkspaceErrors,
-                hasFeedError,
-                isFeedConnectionBroken,
-                hasFailedCardAssignments,
-                cardErrors,
-            };
-
-            cardFeedErrors[workspaceAccountID] = allFeedsErrors;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {
@@ -99,9 +99,8 @@ export default createOnyxDerivedValueConfig({
                 continue;
             }
 
-            const {cardList, ...cards} = workspaceCardFeedCards ?? {};
-
-            for (const card of Object.values(cards ?? {})) {
+            const {cardList, ...filteredCards} = filterInactiveCards(workspaceCardFeedCards);
+            for (const card of Object.values(filteredCards)) {
                 addErrorsForCard(card);
             }
         }
@@ -111,6 +110,7 @@ export default createOnyxDerivedValueConfig({
         return {
             // The errors of all card feeds.
             cardFeedErrors,
+            cardsWithBrokenFeedConnection,
 
             // Mappings of whether to show the RBR for each workspace account ID and per feed name with domain ID
             rbrWorkspaceAccountIDMapping,
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 5723d49b67d9..650b0a5c4bf4 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -19,9 +19,18 @@ type CompanyCardFeedWithDomainID = `${CompanyCardFeedWithNumber}${typeof CONST.C
 type CardFeed = CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
 
 /**
- * Either a company card feed name with domain ID or the Expensify card bank name with domain ID.
+ * Either a company card feed name or the Expensify card bank name with a number.
+ */
+type CardFeedWithNumber = CardFeed | `${CardFeed}${number}`;
+
+/**
+ * Card feed name with domain ID
  */
-type CardFeedWithDomainID = `${CardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
+type CardFeedWithDomainID = `${CardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
+
+/**
+ * Either a company card feed name with domain ID or the Expensify card bank name with domain ID.
+type CardFeedWithDomainID = `${CardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
 
 /** Statement period end */
 type StatementPeriodEnd = Exclude<ValueOf<typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE>, typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE.CUSTOM_DAY_OF_MONTH>;
@@ -281,9 +290,12 @@ export type {
     AddNewCardFeedStep,
     AddNewCompanyCardFeed,
     AddNewCardFeedData,
-    CompanyCardFeed,
     CardFeed,
+    CardFeedWithNumber,
     CardFeedWithDomainID,
+    CompanyCardFeed,
+    CompanyCardFeedWithNumber,
+    CompanyCardFeedWithDomainID,
     CardFeedDetails,
     DirectCardFeedData,
     CardFeedProvider,
@@ -291,10 +303,8 @@ export type {
     CardFeedsStatus,
     CardFeedsStatusByDomainID,
     CompanyFeeds,
-    CompanyCardFeedWithDomainID,
     CustomCardFeedData,
     CompanyCardNicknames,
-    CompanyCardFeedWithNumber,
     FundID,
     StatementPeriodEnd,
     StatementPeriodEndDay,
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 0a3749ac21f0..8ec990162a97 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -2,7 +2,7 @@ import type {OnyxCollection} from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
 import type {Card, CardFeed} from '.';
-import type {CompanyCardFeedWithNumber} from './CardFeeds';
+import type {CardFeedWithDomainID, CompanyCardFeedWithNumber} from './CardFeeds';
 import type {Errors} from './OnyxCommon';
 import type Report from './Report';
 import type Transaction from './Transaction';
@@ -94,6 +94,15 @@ type CardErrors = {
  * The errors of a card feed.
  */
 type FeedErrors = {
+    /**
+     * The errors of the feed.
+     */
+    feedErrors?: Errors;
+    /**
+     * The errors of all cards for a specific feed within a workspace/domain.
+     */
+    cardErrors: Record<string, CardErrors>;
+
     /**
      * Whether to show the RBR for a specific feed within a workspace/domain.
      * This will be true, if any of the below conditions are true:
@@ -114,15 +123,11 @@ type FeedErrors = {
     /**
      * Whether a specific feed within a workspace/domain has errors.
      */
-    hasFeedError: boolean;
+    hasFeedErrors: boolean;
     /**
      * Whether some feed connection is broken.
      */
     isFeedConnectionBroken: boolean;
-    /**
-     * The errors of all cards for a specific feed within a workspace/domain.
-     */
-    cardErrors: Record<string, CardErrors>;
 };
 
 /**
@@ -138,16 +143,20 @@ type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
 /**
  * The errors of all card feeds.
  */
-type CardFeedErrorsObject = Record<number, Record<CardFeedId, FeedErrors>>;
+type CardFeedErrorsObject = Record<CardFeedWithDomainID, FeedErrors>;
 
 /**
  *
  */
 type CardFeedErrors = {
     /**
-     * The errors of all card feeds.
+     * The errors of all card feeds by feed name with domain ID.
      */
     cardFeedErrors: CardFeedErrorsObject;
+    /**
+     * The cards with a broken feed connection.
+     */
+    cardsWithBrokenFeedConnection: Record<string, Card>;
     /**
      * Whether to show the RBR for all feeds.
      */

From 6a225c95cf396d566d041248e26e761042f91a09 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:53:44 +0000
Subject: [PATCH 074/122] fix: remove `checkIfFeedConnectionIsBroken` util
 function

---
 src/hooks/useAssignCard.ts                       | 12 +++++-------
 src/hooks/useUpdateFeedBrokenConnection.ts       | 16 +++++++---------
 src/pages/settings/InitialSettingsPage.tsx       |  7 +++----
 src/pages/workspace/WorkspaceInitialPage.tsx     |  4 ++--
 .../WorkspaceCompanyCardsTableHeaderButtons.tsx  | 15 +++++++++------
 5 files changed, 26 insertions(+), 28 deletions(-)

diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts
index 90aeb1c19f0b..863b619e7277 100644
--- a/src/hooks/useAssignCard.ts
+++ b/src/hooks/useAssignCard.ts
@@ -2,8 +2,6 @@ import {useContext, useRef} from 'react';
 import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
 import {importPlaidAccounts} from '@libs/actions/Plaid';
 import {
-    checkIfFeedConnectionIsBroken,
-    filterInactiveCards,
     getCompanyCardFeed,
     getCompanyFeeds,
     getDomainOrWorkspaceAccountID,
@@ -24,11 +22,11 @@ import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard';
 import {getEmptyObject} from '@src/types/utils/EmptyObject';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
-import useCardsList from './useCardsList';
 import useIsAllowedToIssueCompanyCard from './useIsAllowedToIssueCompanyCard';
 import useNetwork from './useNetwork';
 import useOnyx from './useOnyx';
 import usePolicy from './usePolicy';
+import useCardFeedErrors from './useCardFeedErrors';
 
 type UseAssignCardProps = {
     /** The currently selected card feed */
@@ -59,10 +57,10 @@ function useAssignCard({feedName, policyID, setShouldShowOfflineModal}: UseAssig
 
     const {isOffline} = useNetwork({onReconnect: fetchCompanyCards});
 
-    const [cardsList] = useCardsList(feedName);
-    const filteredFeedCards = filterInactiveCards(cardsList);
-    const hasFeedError = feedName ? !!cardFeeds?.[feedName]?.errors : false;
-    const isSelectedFeedConnectionBroken = checkIfFeedConnectionIsBroken(filteredFeedCards) || hasFeedError;
+    const {cardFeedErrors} = useCardFeedErrors();
+    const feedErrors = feedName ? cardFeedErrors[feedName] : undefined;
+    const isSelectedFeedConnectionBroken = !!feedErrors?.isFeedConnectionBroken || !!feedErrors?.hasFeedErrors;
+
     const isAllowedToIssueCompanyCard = useIsAllowedToIssueCompanyCard({policyID});
 
     const {isActingAsDelegate, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts
index 77fdc8ffe815..23ce0b486c96 100644
--- a/src/hooks/useUpdateFeedBrokenConnection.ts
+++ b/src/hooks/useUpdateFeedBrokenConnection.ts
@@ -1,30 +1,28 @@
 import {useCallback} from 'react';
-import {checkIfFeedConnectionIsBroken, getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getFeedConnectionBrokenCard} from '@libs/CardUtils';
+import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID} from '@libs/CardUtils';
 import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards';
 import CONST from '@src/CONST';
 import type {CompanyCardFeedWithDomainID} from '@src/types/onyx';
 import useCardFeeds from './useCardFeeds';
-import useCardsList from './useCardsList';
 import usePolicy from './usePolicy';
+import useCardFeedErrors from './useCardFeedErrors';
 
 export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CompanyCardFeedWithDomainID}) {
-    const [cardsList] = useCardsList(feed);
     const policy = usePolicy(policyID);
     const [cardFeeds] = useCardFeeds(policyID);
     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;
-    const isFeedConnectionBroken = checkIfFeedConnectionIsBroken(cards);
-    const brokenCard = getFeedConnectionBrokenCard(cards);
-    const brokenCardId = brokenCard?.cardID?.toString();
+    const {isFeedConnectionBroken, cardsWithBrokenFeedConnection} = useCardFeedErrors();
+
+    const [brokenCardId, firstCardWithBrokenFeedConnection] = Object.entries(cardsWithBrokenFeedConnection).at(0) ?? [];
 
     const updateBrokenConnection = useCallback(() => {
         if (!brokenCardId || !feed) {
             return;
         }
-        updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getCompanyCardFeed(feed), brokenCard?.lastScrapeResult);
-    }, [brokenCard?.lastScrapeResult, brokenCardId, domainOrWorkspaceAccountID, feed]);
+        updateWorkspaceCompanyCard(domainOrWorkspaceAccountID, brokenCardId, getCompanyCardFeed(feed), firstCardWithBrokenFeedConnection?.lastScrapeResult);
+    }, [firstCardWithBrokenFeedConnection?.lastScrapeResult, brokenCardId, domainOrWorkspaceAccountID, feed]);
 
     return {updateBrokenConnection, isFeedConnectionBroken};
 }
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index bb8d3d27700b..ce1595199b66 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -35,7 +35,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
 import {resetExitSurveyForm} from '@libs/actions/ExitSurvey';
 import {closeReactNativeApp} from '@libs/actions/HybridApp';
 import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
-import {checkIfFeedConnectionIsBroken, filterPersonalCards, hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {filterPersonalCards, hasPendingExpensifyCardAction} from '@libs/CardUtils';
 import {convertToDisplayString} from '@libs/CurrencyUtils';
 import useIsSidebarRouteActive from '@libs/Navigation/helpers/useIsSidebarRouteActive';
 import Navigation from '@libs/Navigation/Navigation';
@@ -147,7 +147,6 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const {shouldShowRBR} = useCardFeedErrors();
 
-    const hasBrokenFeedConnection = shouldShowRBR || checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
         if (
@@ -155,7 +154,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
             !isEmptyObject(userWallet?.errors) ||
             !isEmptyObject(walletTerms?.errors) ||
             !isEmptyObject(unsharedBankAccount?.errors) ||
-            hasBrokenFeedConnection
+            shouldShowRBR
         ) {
             return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
         }
@@ -163,7 +162,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
             return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
         }
         return undefined;
-    }, [allCards, bankAccountList, fundList, hasBrokenFeedConnection, hasPendingCardAction, unsharedBankAccount?.errors, userWallet?.errors, walletTerms?.errors]);
+    }, [allCards, bankAccountList, fundList, hasPendingCardAction, unsharedBankAccount?.errors, userWallet?.errors, walletTerms?.errors, shouldShowRBR]);
 
     const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false);
 
diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx
index 1e0f2578ab11..596d2c31fe8b 100644
--- a/src/pages/workspace/WorkspaceInitialPage.tsx
+++ b/src/pages/workspace/WorkspaceInitialPage.tsx
@@ -199,9 +199,9 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
     const [highlightedFeature, setHighlightedFeature] = useState<string | undefined>(undefined);
 
     const {cardFeedErrors} = useCardFeedErrors();
-    const policyCardFeedErrors = cardFeedErrors[workspaceAccountID];
+    const policyCardFeedErrors = Object.entries(cardFeedErrors ?? {}).filter(([key]) => key.includes(String(workspaceAccountID)));
 
-    const shouldShowRBR = Object.values(policyCardFeedErrors ?? {}).some((error) => error.shouldShowRBR);
+    const shouldShowRBR = policyCardFeedErrors.some(([, feedErrors]) => feedErrors.shouldShowRBR);
 
     const workspaceMenuItems: WorkspaceMenuItem[] = useMemo(() => {
         const protectedMenuItems: WorkspaceMenuItem[] = [];
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 4f3dee99c95c..82574040c11c 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -18,7 +18,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWorkspaceAccountID from '@hooks/useWorkspaceAccountID';
-import {getCompanyCardFeed, getCompanyFeeds, getCustomOrFormattedFeedName, getPlaidCountry, getPlaidInstitutionId, isCustomFeed} from '@libs/CardUtils';
+import {getCompanyCardFeed, getCompanyCardFeedWithDomainID, getCompanyFeeds, getCustomOrFormattedFeedName, getPlaidCountry, getPlaidInstitutionId, isCustomFeed} from '@libs/CardUtils';
 import Navigation from '@navigation/Navigation';
 import {setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@userActions/CompanyCards';
 import CONST from '@src/CONST';
@@ -57,8 +57,11 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
 
     const [cardFeeds] = useCardFeeds(policyID);
     const policy = usePolicy(policyID);
+
     const workspaceAccountID = useWorkspaceAccountID(policyID);
     const feed = getCompanyCardFeed(feedName);
+const feedWithDomainID = getCompanyCardFeedWithDomainID(feed, workspaceAccountID);
+
     const formattedFeedName = feedName ? getCustomOrFormattedFeedName(feed, cardFeeds?.[feedName]?.customFeedName) : undefined;
     const isCommercialFeed = isCustomFeed(feed);
     const companyFeeds = getCompanyFeeds(cardFeeds);
@@ -68,10 +71,10 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
     const [currencyList = getEmptyObject<CurrencyList>()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true});
 
     const {cardFeedErrors} = useCardFeedErrors();
-    const workspaceCardFeedErrors = cardFeedErrors[workspaceAccountID]?.[feed];
-    const hasFeedError = workspaceCardFeedErrors?.hasFeedError;
-    const isFeedConnectionBroken = workspaceCardFeedErrors?.isFeedConnectionBroken;
-    const shouldShowRBR = workspaceCardFeedErrors?.shouldShowRBR;
+    const feedErrors = cardFeedErrors[feedWithDomainID];
+    const hasFeedErrors = feedErrors?.hasFeedErrors;
+    const isFeedConnectionBroken = feedErrors?.isFeedConnectionBroken;
+    const shouldShowRBR = feedErrors?.shouldShowRBR;
 
     const openBankConnection = () => {
         if (!feedName) {
@@ -168,7 +171,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
                     </View>
                 </View>
             </View>
-            {(isFeedConnectionBroken || hasFeedError) && (
+            {(isFeedConnectionBroken || hasFeedErrors) && (
                 <View style={[styles.flexRow, styles.ph5, styles.alignItemsCenter]}>
                     <Icon
                         src={expensifyIcons.DotIndicator}

From bef0ec2cb6b0a617dc5ba7e99d35048b6cc382c2 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:53:57 +0000
Subject: [PATCH 075/122] fix: remove `checkIfFeedConnectionIsBroken` util
 function

---
 src/libs/CardUtils.ts | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 1bb9f359cdd9..c7692cc71ace 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -752,20 +752,6 @@ function isCardConnectionBroken(card: Card): boolean {
     return !!card.lastScrapeResult && !CONST.COMPANY_CARDS.BROKEN_CONNECTION_IGNORED_STATUSES.includes(card.lastScrapeResult);
 }
 
-/**
- * Check if any card from the provided feed(s) has a broken connection
- *
- * @param feedCards the list of the cards, related to one or several feeds
- * @param [feedToExclude] the feed to ignore during the check, it's useful for checking broken connection error only in the feeds other than the selected one
- */
-function checkIfFeedConnectionIsBroken(feedCards: CardList | undefined, feedToExclude?: string): boolean {
-    if (!feedCards || isEmptyObject(feedCards)) {
-        return false;
-    }
-
-    return Object.values(feedCards).some((card) => !isEmptyObject(card) && card.bank !== feedToExclude && isCardConnectionBroken(card));
-}
-
 /**
  * Checks if an Expensify Card was issued for a given workspace.
  */
@@ -991,7 +977,6 @@ export {
     getFeedType,
     flattenCompanyCards,
     isCardConnectionBroken,
-    checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
     hasIssuedExpensifyCard,

From c693a91fa3968cdd729733d1f1b6b2cca1f1860c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 20:53:59 +0000
Subject: [PATCH 076/122] Update CardUtilsTest.ts

---
 tests/unit/CardUtilsTest.ts | 62 -------------------------------------
 1 file changed, 62 deletions(-)

diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 0800d00ae5b9..ff1ec7f29da2 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -7,7 +7,6 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import IntlStore from '@src/languages/IntlStore';
 import {
-    checkIfFeedConnectionIsBroken,
     filterInactiveCards,
     flattenCompanyCards,
     formatCardExpiration,
@@ -157,23 +156,6 @@ const directFeedCardsSingleList: WorkspaceCardsList = {
     },
 };
 
-const commercialFeedCardsSingleList: WorkspaceCardsList = {
-    // eslint-disable-next-line @typescript-eslint/naming-convention
-    '21570652': {
-        accountID: 18439984,
-        bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-        cardID: 21570652,
-        cardName: 'CREDIT CARD...5501',
-        domainName: 'expensify-policy17f617b9fe23d2f1.exfy',
-        fraud: 'none',
-        lastFourPAN: '5501',
-        lastScrape: '',
-        lastUpdated: '',
-        lastScrapeResult: 531,
-        scrapeMinDate: '2024-08-27',
-        state: 3,
-    },
-};
 const directFeedCardsMultipleList: WorkspaceCardsList = {
     // eslint-disable-next-line @typescript-eslint/naming-convention
     '21570655': {
@@ -904,50 +886,6 @@ describe('CardUtils', () => {
         });
     });
 
-    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;

From 4bf2cbc1f8b2272a92b68d7a91bc1866358a81fb Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:26:23 +0000
Subject: [PATCH 077/122] test: add unit tests for `cardFeedErrors` derived
 value

---
 tests/unit/CardFeedErrorsDerivedValueTest.ts | 617 +++++++++++++++++++
 1 file changed, 617 insertions(+)
 create mode 100644 tests/unit/CardFeedErrorsDerivedValueTest.ts

diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
new file mode 100644
index 000000000000..f087f3c7c920
--- /dev/null
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -0,0 +1,617 @@
+import type {OnyxCollection} from 'react-native-onyx';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import cardFeedErrorsConfig from '@src/libs/actions/OnyxDerived/configs/cardFeedErrors';
+import type {Card, CardFeeds, CardList, FailedCompanyCardAssignments, WorkspaceCardsList} from '@src/types/onyx';
+
+// Helper to create a card with specified properties
+function createCard(overrides: Partial<Card> = {}): Card {
+    return {
+        cardID: 1,
+        accountID: 1,
+        bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA,
+        cardName: 'Test Card',
+        domainName: 'test.exfy',
+        fraud: 'none',
+        lastFourPAN: '1234',
+        lastScrape: '',
+        lastUpdated: '',
+        state: CONST.EXPENSIFY_CARD.STATE.OPEN,
+        ...overrides,
+    };
+}
+
+// Helper to create workspace cards list
+function createWorkspaceCardsList(cards: Record<string, Card>, cardList?: Record<string, string>): WorkspaceCardsList {
+    return {
+        ...cards,
+        ...(cardList ? {cardList} : {}),
+    } as WorkspaceCardsList;
+}
+
+describe('CardFeedErrors Derived Value', () => {
+    describe('compute function', () => {
+        it('should return empty errors when no cards exist', () => {
+            const result = cardFeedErrorsConfig.compute([{}, {}, {}, {}]);
+
+            expect(result.cardFeedErrors).toEqual({});
+            expect(result.cardsWithBrokenFeedConnection).toEqual({});
+            expect(result.shouldShowRBR).toBe(false);
+            expect(result.isFeedConnectionBroken).toBe(false);
+            expect(result.hasFeedErrors).toBe(false);
+            expect(result.hasWorkspaceErrors).toBe(false);
+            expect(result.hasFailedCardAssignment).toBe(false);
+        });
+
+        it('should return empty errors when all inputs are undefined', () => {
+            const result = cardFeedErrorsConfig.compute([undefined, undefined, undefined, undefined]);
+
+            expect(result.cardFeedErrors).toEqual({});
+            expect(result.shouldShowRBR).toBe(false);
+        });
+
+        describe('broken feed connection detection', () => {
+            it('should detect broken connection when lastScrapeResult is not in ignored statuses', () => {
+                const workspaceAccountID = 11111111;
+                const card = createCard({
+                    cardID: 123,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 403, // Not in BROKEN_CONNECTION_IGNORED_STATUSES
+                });
+
+                const globalCardList: CardList = {123: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.isFeedConnectionBroken).toBe(true);
+                expect(result.shouldShowRBR).toBe(true);
+                expect(result.cardsWithBrokenFeedConnection['123']).toEqual(card);
+            });
+
+            it('should NOT detect broken connection when lastScrapeResult is 200 (success)', () => {
+                const workspaceAccountID = 11111111;
+                const card = createCard({
+                    cardID: 123,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 200,
+                });
+
+                const globalCardList: CardList = {123: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.isFeedConnectionBroken).toBe(false);
+                expect(result.cardsWithBrokenFeedConnection).toEqual({});
+            });
+
+            it.each([200, 531, 530, 500, 666])('should NOT detect broken connection when lastScrapeResult is %s (ignored status)', (status) => {
+                const workspaceAccountID = 11111111;
+                const card = createCard({
+                    cardID: 123,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: status,
+                });
+
+                const globalCardList: CardList = {123: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.isFeedConnectionBroken).toBe(false);
+            });
+
+            it('should NOT detect broken connection when lastScrapeResult is undefined', () => {
+                const workspaceAccountID = 11111111;
+                const card = createCard({
+                    cardID: 123,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: undefined,
+                });
+
+                const globalCardList: CardList = {123: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.isFeedConnectionBroken).toBe(false);
+            });
+        });
+
+        describe('processing cards from workspace cards collection', () => {
+            it('should process cards from allWorkspaceCards', () => {
+                const workspaceAccountID = 22222222;
+                const card = createCard({
+                    cardID: 456,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 401, // Broken connection
+                });
+
+                const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE}`]: createWorkspaceCardsList({456: card}),
+                };
+
+                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}]);
+
+                expect(result.isFeedConnectionBroken).toBe(true);
+                expect(result.cardsWithBrokenFeedConnection['456']).toEqual(card);
+            });
+
+            it('should filter out inactive cards (closed, deactivated, suspended)', () => {
+                const workspaceAccountID = 33333333;
+
+                const closedCard = createCard({
+                    cardID: 1,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    state: CONST.EXPENSIFY_CARD.STATE.CLOSED,
+                    lastScrapeResult: 403,
+                });
+
+                const deactivatedCard = createCard({
+                    cardID: 2,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED,
+                    lastScrapeResult: 403,
+                });
+
+                const suspendedCard = createCard({
+                    cardID: 3,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED,
+                    lastScrapeResult: 403,
+                });
+
+                const activeCard = createCard({
+                    cardID: 4,
+                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+                    fundID: workspaceAccountID.toString(),
+                    state: CONST.EXPENSIFY_CARD.STATE.OPEN,
+                    lastScrapeResult: 403,
+                });
+
+                const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}`]: createWorkspaceCardsList({
+                        1: closedCard,
+                        2: deactivatedCard,
+                        3: suspendedCard,
+                        4: activeCard,
+                    }),
+                };
+
+                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}]);
+
+                // Only the active card should be processed and detected as broken
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('4');
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('1');
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('2');
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('3');
+            });
+        });
+
+        describe('failed card assignments detection', () => {
+            it('should detect failed card assignments', () => {
+                const workspaceAccountID = 44444444;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 789,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {789: card};
+
+                const failedAssignments: OnyxCollection<FailedCompanyCardAssignments> = {
+                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`]: {
+                        someCardNumber: {
+                            errors: {error1: 'Failed to assign card'},
+                        },
+                    },
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}]);
+
+                expect(result.hasFailedCardAssignment).toBe(true);
+                expect(result.shouldShowRBR).toBe(true);
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasFailedCardAssignments).toBe(true);
+            });
+
+            it('should NOT detect failed card assignments when empty', () => {
+                const workspaceAccountID = 44444444;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+
+                const card = createCard({
+                    cardID: 789,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {789: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.hasFailedCardAssignment).toBe(false);
+            });
+        });
+
+        describe('feed errors detection', () => {
+            it('should detect feed errors from cardFeeds', () => {
+                const workspaceAccountID = 55555555;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 101,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {101: card};
+
+                const cardFeeds: OnyxCollection<CardFeeds> = {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                        settings: {
+                            companyCards: {
+                                [feedName]: {
+                                    pending: false,
+                                    errors: {
+                                        feedError: 'Connection failed',
+                                    },
+                                },
+                            },
+                        },
+                    },
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+
+                expect(result.hasFeedErrors).toBe(true);
+                expect(result.shouldShowRBR).toBe(true);
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasFeedErrors).toBe(true);
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.feedErrors).toEqual({feedError: 'Connection failed'});
+            });
+        });
+
+        describe('workspace errors detection', () => {
+            it('should detect workspace errors', () => {
+                const workspaceAccountID = 66666666;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.BREX;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 202,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {202: card};
+
+                const cardFeeds: OnyxCollection<CardFeeds> = {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                        errors: {
+                            workspaceError: 'Workspace connection issue',
+                        },
+                        settings: {
+                            companyCards: {
+                                [feedName]: {
+                                    pending: false,
+                                },
+                            },
+                        },
+                    },
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+
+                expect(result.hasWorkspaceErrors).toBe(true);
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
+            });
+        });
+
+        describe('card-level errors detection', () => {
+            it('should detect card errors', () => {
+                const workspaceAccountID = 77777777;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 303,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    errors: {
+                        cardError: 'Card sync failed',
+                    },
+                });
+
+                const globalCardList: CardList = {303: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['303']).toEqual({
+                    errors: {cardError: 'Card sync failed'},
+                    errorFields: undefined,
+                    pendingAction: undefined,
+                });
+            });
+
+            it('should detect card errorFields', () => {
+                const workspaceAccountID = 77777777;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 304,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    errorFields: {
+                        cardName: {error: 'Invalid card name'},
+                    },
+                });
+
+                const globalCardList: CardList = {304: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['304']?.errorFields).toEqual({
+                    cardName: {error: 'Invalid card name'},
+                });
+            });
+
+            it('should detect card pendingAction', () => {
+                const workspaceAccountID = 77777777;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 305,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+                });
+
+                const globalCardList: CardList = {305: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['305']?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE);
+            });
+        });
+
+        describe('RBR mappings', () => {
+            it('should set rbrWorkspaceAccountIDMapping correctly', () => {
+                const workspaceAccountID = 88888888;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.WELLS_FARGO;
+
+                const card = createCard({
+                    cardID: 404,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 403, // Broken connection
+                });
+
+                const globalCardList: CardList = {404: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.rbrWorkspaceAccountIDMapping[workspaceAccountID]).toBe(true);
+            });
+
+            it('should set rbrFeedNameWithDomainIDMapping correctly', () => {
+                const workspaceAccountID = 99999999;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CITIBANK;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 505,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 403, // Broken connection
+                });
+
+                const globalCardList: CardList = {505: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.rbrFeedNameWithDomainIDMapping[feedNameWithDomainID]).toBe(true);
+            });
+
+            it('should not set RBR when no errors exist', () => {
+                const workspaceAccountID = 10101010;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.BANK_OF_AMERICA;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 606,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 200, // Success
+                });
+
+                const globalCardList: CardList = {606: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.rbrWorkspaceAccountIDMapping[workspaceAccountID]).toBe(false);
+                expect(result.rbrFeedNameWithDomainIDMapping[feedNameWithDomainID]).toBe(false);
+                expect(result.shouldShowRBR).toBe(false);
+            });
+        });
+
+        describe('multiple feeds and workspaces', () => {
+            it('should handle multiple feeds across different workspaces', () => {
+                const workspace1 = 11111111;
+                const workspace2 = 22222222;
+                const feed1 = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+                const feed2 = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE;
+
+                const card1 = createCard({
+                    cardID: 1,
+                    bank: feed1,
+                    fundID: workspace1.toString(),
+                    lastScrapeResult: 403, // Broken
+                });
+
+                const card2 = createCard({
+                    cardID: 2,
+                    bank: feed2,
+                    fundID: workspace2.toString(),
+                    lastScrapeResult: 200, // OK
+                });
+
+                const globalCardList: CardList = {1: card1, 2: card2};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.rbrWorkspaceAccountIDMapping[workspace1]).toBe(true);
+                expect(result.rbrWorkspaceAccountIDMapping[workspace2]).toBe(false);
+                expect(result.isFeedConnectionBroken).toBe(true);
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('1');
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('2');
+            });
+
+            it('should aggregate errors from both globalCardList and workspaceCards', () => {
+                const workspaceAccountID = 33333333;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT;
+
+                const globalCard = createCard({
+                    cardID: 1,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 403,
+                });
+
+                const workspaceCard = createCard({
+                    cardID: 2,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 401,
+                });
+
+                const globalCardList: CardList = {1: globalCard};
+                const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feedName}`]: createWorkspaceCardsList({2: workspaceCard}),
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, allWorkspaceCards, {}, {}]);
+
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('1');
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('2');
+            });
+        });
+
+        describe('shouldShowRBR computation', () => {
+            it('should return true when hasFeedErrors is true', () => {
+                const workspaceAccountID = 12121212;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+
+                const card = createCard({
+                    cardID: 1,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {1: card};
+
+                const cardFeeds: OnyxCollection<CardFeeds> = {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                        settings: {
+                            companyCards: {
+                                [feedName]: {
+                                    pending: false,
+                                    errors: {error: 'Feed error'},
+                                },
+                            },
+                        },
+                    },
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+
+                expect(result.shouldShowRBR).toBe(true);
+            });
+
+            it('should return true when hasFailedCardAssignment is true', () => {
+                const workspaceAccountID = 13131313;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD;
+                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+
+                const card = createCard({
+                    cardID: 1,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                });
+
+                const globalCardList: CardList = {1: card};
+
+                const failedAssignments: OnyxCollection<FailedCompanyCardAssignments> = {
+                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`]: {
+                        cardNumber: {errors: {error: 'Assignment failed'}},
+                    },
+                };
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}]);
+
+                expect(result.shouldShowRBR).toBe(true);
+            });
+
+            it('should return true when isFeedConnectionBroken is true', () => {
+                const workspaceAccountID = 14141414;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE;
+
+                const card = createCard({
+                    cardID: 1,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 403,
+                });
+
+                const globalCardList: CardList = {1: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.shouldShowRBR).toBe(true);
+            });
+
+            it('should return false when no errors exist', () => {
+                const workspaceAccountID = 15151515;
+                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE;
+
+                const card = createCard({
+                    cardID: 1,
+                    bank: feedName,
+                    fundID: workspaceAccountID.toString(),
+                    lastScrapeResult: 200,
+                });
+
+                const globalCardList: CardList = {1: card};
+
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+
+                expect(result.shouldShowRBR).toBe(false);
+            });
+        });
+    });
+
+    describe('dependencies', () => {
+        it('should have correct dependencies defined', () => {
+            expect(cardFeedErrorsConfig.dependencies).toEqual([
+                ONYXKEYS.CARD_LIST,
+                ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST,
+                ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS,
+                ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER,
+            ]);
+        });
+    });
+
+    describe('key', () => {
+        it('should have correct key defined', () => {
+            expect(cardFeedErrorsConfig.key).toBe(ONYXKEYS.DERIVED.CARD_FEED_ERRORS);
+        });
+    });
+});

From 5d2b84b2e13fc31dca464456ccb202ccba1e260d Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:27:00 +0000
Subject: [PATCH 078/122] fix: split indicator card feed error statuses in two

---
 src/CONST/index.ts                              | 3 ++-
 src/hooks/useNavigationTabBarIndicatorChecks.ts | 4 ++--
 tests/unit/useAccountTabIndicatorStatusTest.ts  | 8 ++++----
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 197759a6a860..93e8bfa937b0 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7581,7 +7581,8 @@ const CONST = {
         HAS_PENDING_CARD_INFO: 'hasPendingCardInfo',
         HAS_UBER_CREDENTIALS_ERROR: 'hasUberCredentialsError',
         HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO: 'hasPartiallySetupBankAccountInfo',
-        HAS_CARD_FEED_ERRORS: 'hasCardFeedErrors',
+        HAS_EMPLOYEE_CARD_FEED_ERRORS: 'hasEmployeeCardFeedErrors',
+        HAS_POLICY_ADMIN_CARD_FEED_ERRORS: 'hasPolicyAdminCardFeedErrors',
     },
 
     DEBUG: {
diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
index 084e90e80a65..cfaa954fce74 100644
--- a/src/hooks/useNavigationTabBarIndicatorChecks.ts
+++ b/src/hooks/useNavigationTabBarIndicatorChecks.ts
@@ -55,7 +55,7 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
         ),
         [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
         [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
-        [CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
     };
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
@@ -77,7 +77,7 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
         // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
         [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
         [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
-        [CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS]: !isPolicyAdmin && hasCardFeedErrors,
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin && hasCardFeedErrors,
     };
 
     const infoChecks: Partial<Record<IndicatorStatus, boolean>> = {
diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 3567c602f54e..0127f6e3d603 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -86,12 +86,12 @@ const getMockForStatus = (status: string) =>
             // eslint-disable-next-line @typescript-eslint/naming-convention
             card123: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS ? 403 : 200,
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS ? 403 : 200,
             },
             // eslint-disable-next-line @typescript-eslint/naming-convention
             card456: {
                 bank: 'ANOTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS ? 403 : 200,
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS ? 403 : 200,
             },
         },
         [ONYXKEYS.SESSION]: {
@@ -134,7 +134,7 @@ const TEST_CASES: TestCase[] = [
     {
         name: 'has card feed errors',
         indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
     },
     {
         name: 'has phone number error',
@@ -387,7 +387,7 @@ describe('useAccountTabIndicatorStatus', () => {
             await waitForBatchedUpdatesWithAct();
             const {status, indicatorColor} = result.current;
 
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_CARD_FEED_ERRORS);
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS);
             expect(indicatorColor).toBe(defaultTheme.danger);
         });
 

From da04af0681815ecaf27968f564b55f2dd27b62d1 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:27:07 +0000
Subject: [PATCH 079/122] refactor: rename variable

---
 src/hooks/usePoliciesWithCardFeedErrors.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/hooks/usePoliciesWithCardFeedErrors.ts b/src/hooks/usePoliciesWithCardFeedErrors.ts
index 5a4b23245abc..2388fcd26ed0 100644
--- a/src/hooks/usePoliciesWithCardFeedErrors.ts
+++ b/src/hooks/usePoliciesWithCardFeedErrors.ts
@@ -21,8 +21,8 @@ function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
     const {rbrWorkspaceAccountIDMapping} = useCardFeedErrors();
     let hasCardFeedErrors = false;
     const policiesWithCardFeedErrors: Policy[] = [];
-    for (const [workspaceAccountID, value] of Object.entries(rbrWorkspaceAccountIDMapping)) {
-        if (!value) {
+    for (const [workspaceAccountID, hasErrors] of Object.entries(rbrWorkspaceAccountIDMapping)) {
+        if (!hasErrors) {
             continue;
         }
         hasCardFeedErrors = true;

From b3bed025e759c5e1f0062efedcb4d3f6a6b3b339 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:55:54 +0000
Subject: [PATCH 080/122] fix: prettier

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index a20edd677180..239513921f4f 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -30,7 +30,7 @@ export default createOnyxDerivedValueConfig({
             const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
             const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
-            const feedErrors =selectedFeed?.errors
+            const feedErrors = selectedFeed?.errors
 
             const hasFailedCardAssignments = !isEmptyObject(
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],

From 3e5f2db0bc33680c39f232d044e673be3ae62db5 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:56:41 +0000
Subject: [PATCH 081/122] fix: extract constants in tests

---
 tests/unit/CardFeedErrorsDerivedValueTest.ts | 476 ++++++++++---------
 1 file changed, 242 insertions(+), 234 deletions(-)

diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index f087f3c7c920..c1bc07721f41 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -3,11 +3,49 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import cardFeedErrorsConfig from '@src/libs/actions/OnyxDerived/configs/cardFeedErrors';
 import type {Card, CardFeeds, CardList, FailedCompanyCardAssignments, WorkspaceCardsList} from '@src/types/onyx';
+import type { DerivedValueContext } from '@libs/actions/OnyxDerived/types';
+import { getCompanyCardFeedWithDomainID } from '@libs/CardUtils';
+import type { CardFeed, CardFeedWithDomainID } from '@src/types/onyx/CardFeeds';
+
+const DERIVED_VALUE_CONTEXT: DerivedValueContext<typeof cardFeedErrorsConfig.key, typeof cardFeedErrorsConfig.dependencies> = {
+    currentValue: undefined,
+    sourceValues: undefined,
+    areAllConnectionsSet: false,
+};
+
+const CARD_FEEDS = {
+    [CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE]: {
+        workspaceAccountID: 44444444,
+        feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+        feedNameWithDomainID: getCompanyCardFeedWithDomainID(CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE, 44444444),
+    },
+    [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: {
+        workspaceAccountID: 55555555,
+        feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD,
+        feedNameWithDomainID: getCompanyCardFeedWithDomainID(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD, 55555555),
+    },
+    [CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT]: {
+        workspaceAccountID: 66666666,
+        feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT,
+        feedNameWithDomainID: getCompanyCardFeedWithDomainID(CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT, 66666666),
+}} as const satisfies Partial<Record<CardFeed, {
+    workspaceAccountID: number;
+    feedName: CardFeed;
+    feedNameWithDomainID: CardFeedWithDomainID;
+}>>;
+
+const CARD_IDS = {
+    card1: 1,
+    card2: 2,
+    card3: 3,
+    card4: 4,
+};
+
 
 // Helper to create a card with specified properties
 function createCard(overrides: Partial<Card> = {}): Card {
     return {
-        cardID: 1,
+        cardID: CARD_IDS.card1,
         accountID: 1,
         bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA,
         cardName: 'Test Card',
@@ -32,7 +70,7 @@ function createWorkspaceCardsList(cards: Record<string, Card>, cardList?: Record
 describe('CardFeedErrors Derived Value', () => {
     describe('compute function', () => {
         it('should return empty errors when no cards exist', () => {
-            const result = cardFeedErrorsConfig.compute([{}, {}, {}, {}]);
+            const result = cardFeedErrorsConfig.compute([{}, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
             expect(result.cardFeedErrors).toEqual({});
             expect(result.cardsWithBrokenFeedConnection).toEqual({});
@@ -44,7 +82,7 @@ describe('CardFeedErrors Derived Value', () => {
         });
 
         it('should return empty errors when all inputs are undefined', () => {
-            const result = cardFeedErrorsConfig.compute([undefined, undefined, undefined, undefined]);
+            const result = cardFeedErrorsConfig.compute([undefined, undefined, undefined, undefined], DERIVED_VALUE_CONTEXT);
 
             expect(result.cardFeedErrors).toEqual({});
             expect(result.shouldShowRBR).toBe(false);
@@ -52,68 +90,68 @@ describe('CardFeedErrors Derived Value', () => {
 
         describe('broken feed connection detection', () => {
             it('should detect broken connection when lastScrapeResult is not in ignored statuses', () => {
-                const workspaceAccountID = 11111111;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card = createCard({
-                    cardID: 123,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 403, // Not in BROKEN_CONNECTION_IGNORED_STATUSES
                 });
 
-                const globalCardList: CardList = {123: card};
+                const globalCardList: CardList = {card1: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.isFeedConnectionBroken).toBe(true);
                 expect(result.shouldShowRBR).toBe(true);
-                expect(result.cardsWithBrokenFeedConnection['123']).toEqual(card);
+                expect(result.cardsWithBrokenFeedConnection[CARD_IDS.card1]).toEqual(card);
             });
 
             it('should NOT detect broken connection when lastScrapeResult is 200 (success)', () => {
-                const workspaceAccountID = 11111111;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card = createCard({
-                    cardID: 123,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 200,
                 });
 
-                const globalCardList: CardList = {123: card};
+                const globalCardList: CardList = {card1: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.isFeedConnectionBroken).toBe(false);
                 expect(result.cardsWithBrokenFeedConnection).toEqual({});
             });
 
             it.each([200, 531, 530, 500, 666])('should NOT detect broken connection when lastScrapeResult is %s (ignored status)', (status) => {
-                const workspaceAccountID = 11111111;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card = createCard({
-                    cardID: 123,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: status,
                 });
 
-                const globalCardList: CardList = {123: card};
+                const globalCardList: CardList = {card1: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.isFeedConnectionBroken).toBe(false);
             });
 
             it('should NOT detect broken connection when lastScrapeResult is undefined', () => {
-                const workspaceAccountID = 11111111;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card = createCard({
-                    cardID: 123,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: undefined,
                 });
 
-                const globalCardList: CardList = {123: card};
+                const globalCardList: CardList = {card1: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.isFeedConnectionBroken).toBe(false);
             });
@@ -121,120 +159,123 @@ describe('CardFeedErrors Derived Value', () => {
 
         describe('processing cards from workspace cards collection', () => {
             it('should process cards from allWorkspaceCards', () => {
-                const workspaceAccountID = 22222222;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card = createCard({
-                    cardID: 456,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card2,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 401, // Broken connection
                 });
 
                 const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
-                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE}`]: createWorkspaceCardsList({456: card}),
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: createWorkspaceCardsList({card2: card}),
                 };
 
-                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.isFeedConnectionBroken).toBe(true);
-                expect(result.cardsWithBrokenFeedConnection['456']).toEqual(card);
+                expect(result.cardsWithBrokenFeedConnection[CARD_IDS.card2]).toEqual(card);
             });
 
             it('should filter out inactive cards (closed, deactivated, suspended)', () => {
-                const workspaceAccountID = 33333333;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const closedCard = createCard({
-                    cardID: 1,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     state: CONST.EXPENSIFY_CARD.STATE.CLOSED,
                     lastScrapeResult: 403,
                 });
 
                 const deactivatedCard = createCard({
-                    cardID: 2,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card2,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     state: CONST.EXPENSIFY_CARD.STATE.STATE_DEACTIVATED,
                     lastScrapeResult: 403,
                 });
 
                 const suspendedCard = createCard({
-                    cardID: 3,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card3,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     state: CONST.EXPENSIFY_CARD.STATE.STATE_SUSPENDED,
                     lastScrapeResult: 403,
                 });
 
                 const activeCard = createCard({
-                    cardID: 4,
-                    bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card4,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     state: CONST.EXPENSIFY_CARD.STATE.OPEN,
                     lastScrapeResult: 403,
                 });
 
                 const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
-                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE}`]: createWorkspaceCardsList({
-                        1: closedCard,
-                        2: deactivatedCard,
-                        3: suspendedCard,
-                        4: activeCard,
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: createWorkspaceCardsList({
+                        card1: closedCard,
+                        card2: deactivatedCard,
+                        card3: suspendedCard,
+                        card4: activeCard,
                     }),
                 };
 
-                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 // Only the active card should be processed and detected as broken
-                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('4');
-                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('1');
-                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('2');
-                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('3');
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card4));
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card1));
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card2));
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card3));
             });
         });
 
         describe('failed card assignments detection', () => {
             it('should detect failed card assignments', () => {
-                const workspaceAccountID = 44444444;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 789,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card3,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {789: card};
+                const globalCardList: CardList = {[CARD_IDS.card3]: card};
 
                 const failedAssignments: OnyxCollection<FailedCompanyCardAssignments> = {
-                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`]: {
-                        someCardNumber: {
-                            errors: {error1: 'Failed to assign card'},
+                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: {
+                        [String(CARD_IDS.card3)]: {
+                            errors: {error: 'Failed to assign card'},
+                            errorFields: undefined,
+                            pendingAction: undefined,
+                            domainOrWorkspaceAccountID: cardFeed.workspaceAccountID,
+                            feed: cardFeed.feedNameWithDomainID,
+                            cardName: card.cardName ?? '',
+                            cardNumber: card.lastFourPAN ?? '',
                         },
                     },
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.hasFailedCardAssignment).toBe(true);
                 expect(result.shouldShowRBR).toBe(true);
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasFailedCardAssignments).toBe(true);
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasFailedCardAssignments).toBe(true);
             });
 
             it('should NOT detect failed card assignments when empty', () => {
-                const workspaceAccountID = 44444444;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 789,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card4,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {789: card};
+                const globalCardList: CardList = {[CARD_IDS.card4]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.hasFailedCardAssignment).toBe(false);
             });
@@ -242,23 +283,21 @@ describe('CardFeedErrors Derived Value', () => {
 
         describe('feed errors detection', () => {
             it('should detect feed errors from cardFeeds', () => {
-                const workspaceAccountID = 55555555;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD];
 
                 const card = createCard({
-                    cardID: 101,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {101: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
                 const cardFeeds: OnyxCollection<CardFeeds> = {
-                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
                         settings: {
                             companyCards: {
-                                [feedName]: {
+                                [cardFeed.feedNameWithDomainID]: {
                                     pending: false,
                                     errors: {
                                         feedError: 'Connection failed',
@@ -269,37 +308,35 @@ describe('CardFeedErrors Derived Value', () => {
                     },
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
                 expect(result.hasFeedErrors).toBe(true);
                 expect(result.shouldShowRBR).toBe(true);
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasFeedErrors).toBe(true);
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.feedErrors).toEqual({feedError: 'Connection failed'});
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasFeedErrors).toBe(true);
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.feedErrors).toEqual({feedError: 'Connection failed'});
             });
         });
 
         describe('workspace errors detection', () => {
             it('should detect workspace errors', () => {
-                const workspaceAccountID = 66666666;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.BREX;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 202,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card2,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {202: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
                 const cardFeeds: OnyxCollection<CardFeeds> = {
-                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
                         errors: {
                             workspaceError: 'Workspace connection issue',
                         },
                         settings: {
                             companyCards: {
-                                [feedName]: {
+                                [cardFeed.feedNameWithDomainID]: {
                                     pending: false,
                                 },
                             },
@@ -307,33 +344,31 @@ describe('CardFeedErrors Derived Value', () => {
                     },
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
                 expect(result.hasWorkspaceErrors).toBe(true);
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
             });
         });
 
         describe('card-level errors detection', () => {
             it('should detect card errors', () => {
-                const workspaceAccountID = 77777777;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 303,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     errors: {
                         cardError: 'Card sync failed',
                     },
                 });
 
-                const globalCardList: CardList = {303: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['303']).toEqual({
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.cardErrors[CARD_IDS.card1]).toEqual({
                     errors: {cardError: 'Card sync failed'},
                     errorFields: undefined,
                     pendingAction: undefined,
@@ -341,188 +376,174 @@ describe('CardFeedErrors Derived Value', () => {
             });
 
             it('should detect card errorFields', () => {
-                const workspaceAccountID = 77777777;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 304,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     errorFields: {
                         cardName: {error: 'Invalid card name'},
                     },
                 });
 
-                const globalCardList: CardList = {304: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT     );
 
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['304']?.errorFields).toEqual({
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.cardErrors[CARD_IDS.card1]?.errorFields).toEqual({
                     cardName: {error: 'Invalid card name'},
                 });
             });
 
             it('should detect card pendingAction', () => {
-                const workspaceAccountID = 77777777;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 305,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
                 });
 
-                const globalCardList: CardList = {305: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.cardFeedErrors[feedNameWithDomainID]?.cardErrors['305']?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE);
+                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.cardErrors[CARD_IDS.card1]?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE);
             });
         });
 
         describe('RBR mappings', () => {
             it('should set rbrWorkspaceAccountIDMapping correctly', () => {
-                const workspaceAccountID = 88888888;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.WELLS_FARGO;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 404,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 403, // Broken connection
                 });
 
-                const globalCardList: CardList = {404: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[workspaceAccountID]).toBe(true);
+                expect(result.rbrWorkspaceAccountIDMapping[cardFeed.workspaceAccountID]).toBe(true);
             });
 
             it('should set rbrFeedNameWithDomainIDMapping correctly', () => {
-                const workspaceAccountID = 99999999;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CITIBANK;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 505,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 403, // Broken connection
                 });
 
-                const globalCardList: CardList = {505: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrFeedNameWithDomainIDMapping[feedNameWithDomainID]).toBe(true);
+                expect(result.rbrFeedNameWithDomainIDMapping[cardFeed.feedNameWithDomainID]).toBe(true);
             });
 
             it('should not set RBR when no errors exist', () => {
-                const workspaceAccountID = 10101010;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.BANK_OF_AMERICA;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 606,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 200, // Success
                 });
 
-                const globalCardList: CardList = {606: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[workspaceAccountID]).toBe(false);
-                expect(result.rbrFeedNameWithDomainIDMapping[feedNameWithDomainID]).toBe(false);
+                expect(result.rbrWorkspaceAccountIDMapping[cardFeed.workspaceAccountID]).toBe(false);
+                expect(result.rbrFeedNameWithDomainIDMapping[cardFeed.feedNameWithDomainID]).toBe(false);
                 expect(result.shouldShowRBR).toBe(false);
             });
         });
 
         describe('multiple feeds and workspaces', () => {
             it('should handle multiple feeds across different workspaces', () => {
-                const workspace1 = 11111111;
-                const workspace2 = 22222222;
-                const feed1 = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
-                const feed2 = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE;
-
+                const cardFeed1 = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT];
+                const cardFeed2 = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
                 const card1 = createCard({
-                    cardID: 1,
-                    bank: feed1,
-                    fundID: workspace1.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed1.feedName,
+                    fundID: String(cardFeed1.workspaceAccountID),
                     lastScrapeResult: 403, // Broken
                 });
 
                 const card2 = createCard({
-                    cardID: 2,
-                    bank: feed2,
-                    fundID: workspace2.toString(),
+                    cardID: CARD_IDS.card2,
+                    bank: cardFeed2.feedName,
+                    fundID: String(cardFeed2.workspaceAccountID),
                     lastScrapeResult: 200, // OK
                 });
 
-                const globalCardList: CardList = {1: card1, 2: card2};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card1, [CARD_IDS.card2]: card2};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[workspace1]).toBe(true);
-                expect(result.rbrWorkspaceAccountIDMapping[workspace2]).toBe(false);
+                expect(result.rbrWorkspaceAccountIDMapping[cardFeed1.workspaceAccountID]).toBe(true);
+                expect(result.rbrWorkspaceAccountIDMapping[cardFeed2.workspaceAccountID]).toBe(false);
                 expect(result.isFeedConnectionBroken).toBe(true);
-                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('1');
-                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty('2');
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card1));
+                expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card2));
             });
 
             it('should aggregate errors from both globalCardList and workspaceCards', () => {
-                const workspaceAccountID = 33333333;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT];
 
                 const globalCard = createCard({
-                    cardID: 1,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 403,
                 });
 
                 const workspaceCard = createCard({
-                    cardID: 2,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card2,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 401,
                 });
 
-                const globalCardList: CardList = {1: globalCard};
+                const globalCardList: CardList = {[CARD_IDS.card1]: globalCard};
                 const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
-                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feedName}`]: createWorkspaceCardsList({2: workspaceCard}),
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: createWorkspaceCardsList({[CARD_IDS.card2]: workspaceCard}),
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, allWorkspaceCards, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, allWorkspaceCards, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('1');
-                expect(result.cardsWithBrokenFeedConnection).toHaveProperty('2');
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card1));
+                expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card2));
             });
         });
 
         describe('shouldShowRBR computation', () => {
             it('should return true when hasFeedErrors is true', () => {
-                const workspaceAccountID = 12121212;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.VISA;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT];
 
                 const card = createCard({
-                    cardID: 1,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {1: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
                 const cardFeeds: OnyxCollection<CardFeeds> = {
-                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`]: {
+                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
                         settings: {
                             companyCards: {
-                                [feedName]: {
+                                [cardFeed.feedNameWithDomainID]: {
                                     pending: false,
                                     errors: {error: 'Feed error'},
                                 },
@@ -531,87 +552,74 @@ describe('CardFeedErrors Derived Value', () => {
                     },
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
                 expect(result.shouldShowRBR).toBe(true);
             });
 
             it('should return true when hasFailedCardAssignment is true', () => {
-                const workspaceAccountID = 13131313;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD;
-                const feedNameWithDomainID = `${feedName}#${workspaceAccountID}`;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD];
 
                 const card = createCard({
-                    cardID: 1,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                 });
 
-                const globalCardList: CardList = {1: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
                 const failedAssignments: OnyxCollection<FailedCompanyCardAssignments> = {
-                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`]: {
-                        cardNumber: {errors: {error: 'Assignment failed'}},
+                    [`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: {
+                        [String(CARD_IDS.card1)]: {
+                            errors: {error: 'Assignment failed'},
+                            errorFields: undefined,
+                            pendingAction: undefined,
+                            domainOrWorkspaceAccountID: cardFeed.workspaceAccountID,
+                            feed: cardFeed.feedNameWithDomainID,
+                            cardName: card.cardName ?? '',
+                            cardNumber: card.lastFourPAN ?? '',
+                        },
                     },
                 };
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.shouldShowRBR).toBe(true);
             });
 
             it('should return true when isFeedConnectionBroken is true', () => {
-                const workspaceAccountID = 14141414;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 1,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 403,
                 });
 
-                const globalCardList: CardList = {1: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.shouldShowRBR).toBe(true);
             });
 
             it('should return false when no errors exist', () => {
-                const workspaceAccountID = 15151515;
-                const feedName = CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE;
+                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
-                    cardID: 1,
-                    bank: feedName,
-                    fundID: workspaceAccountID.toString(),
+                    cardID: CARD_IDS.card1,
+                    bank: cardFeed.feedName,
+                    fundID: String(cardFeed.workspaceAccountID),
                     lastScrapeResult: 200,
                 });
 
-                const globalCardList: CardList = {1: card};
+                const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}]);
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT );
 
                 expect(result.shouldShowRBR).toBe(false);
             });
         });
     });
-
-    describe('dependencies', () => {
-        it('should have correct dependencies defined', () => {
-            expect(cardFeedErrorsConfig.dependencies).toEqual([
-                ONYXKEYS.CARD_LIST,
-                ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST,
-                ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS,
-                ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER,
-            ]);
-        });
-    });
-
-    describe('key', () => {
-        it('should have correct key defined', () => {
-            expect(cardFeedErrorsConfig.key).toBe(ONYXKEYS.DERIVED.CARD_FEED_ERRORS);
-        });
-    });
 });

From cb8e1f25889c968f5f49f17318f231f70220f15b Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Sun, 18 Jan 2026 21:58:41 +0000
Subject: [PATCH 082/122] fix: broken test

---
 tests/unit/CardFeedErrorsDerivedValueTest.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index c1bc07721f41..3ff889327880 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -297,7 +297,7 @@ describe('CardFeedErrors Derived Value', () => {
                     [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
                         settings: {
                             companyCards: {
-                                [cardFeed.feedNameWithDomainID]: {
+                                [cardFeed.feedName]: {
                                     pending: false,
                                     errors: {
                                         feedError: 'Connection failed',
@@ -336,7 +336,7 @@ describe('CardFeedErrors Derived Value', () => {
                         },
                         settings: {
                             companyCards: {
-                                [cardFeed.feedNameWithDomainID]: {
+                                [cardFeed.feedName]: {
                                     pending: false,
                                 },
                             },
@@ -543,7 +543,7 @@ describe('CardFeedErrors Derived Value', () => {
                     [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
                         settings: {
                             companyCards: {
-                                [cardFeed.feedNameWithDomainID]: {
+                                [cardFeed.feedName]: {
                                     pending: false,
                                     errors: {error: 'Feed error'},
                                 },

From e7d1bd23dd010f48e061c6aa3686aa4dc8a9cf00 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 15:17:02 +0000
Subject: [PATCH 083/122] fix: check for NaN for `workspaceAccountID` in
 `cardFeedErrors` derived value

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 239513921f4f..181c153dcbb4 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -27,6 +27,11 @@ export default createOnyxDerivedValueConfig({
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CompanyCardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
+
+            if (Number.isNaN(workspaceAccountID)) {
+                return;
+            }
+
             const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
 
             const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];

From c5ec8f2bad94ff485a800c89eba53c1a10f6d872 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 15:17:15 +0000
Subject: [PATCH 084/122] fix: `useAccountTabIndicatorStatusTest` to use
 derived value

---
 .../unit/useAccountTabIndicatorStatusTest.ts  | 64 +++++++++++++------
 1 file changed, 43 insertions(+), 21 deletions(-)

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 0127f6e3d603..255160fc8a28 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -6,9 +6,22 @@ import useAccountTabIndicatorStatus from '@hooks/useAccountTabIndicatorStatus';
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
-const getMockForStatus = (status: string) =>
+const userID = 'johndoe12@expensify.com';
+
+const brokenCardFeed = {
+    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+    workspaceAccountID: 12345,
+};
+
+const cardFeedErrorTestCaseNames = {
+    employee: 'has card feed error if employee (non-admin)',
+    admin: 'has no card feed error if admin',
+} as const
+
+const getMockForStatus = ({status, name}: TestCase) =>
     ({
         [ONYXKEYS.BANK_ACCOUNT_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -42,9 +55,9 @@ const getMockForStatus = (status: string) =>
         },
         [ONYXKEYS.LOGIN_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
-            'johndoe12@expensify.com': {
+            [userID]: {
                 partnerName: 'John Doe',
-                partnerUserID: 'johndoe12@expensify.com',
+                partnerUserID: userID,
                 validatedDate: status !== CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO ? new Date().toISOString() : undefined,
                 errorFields:
                     status === CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR
@@ -82,27 +95,29 @@ const getMockForStatus = (status: string) =>
                       }
                     : undefined,
         },
+        [ONYXKEYS.SESSION]: {
+            email: userID,
+        },
         [`${ONYXKEYS.CARD_LIST}`]: {
-            // eslint-disable-next-line @typescript-eslint/naming-convention
-            card123: {
+            card1: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS ? 403 : 200,
-            },
-            // eslint-disable-next-line @typescript-eslint/naming-convention
-            card456: {
-                bank: 'ANOTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS ? 403 : 200,
+                lastScrapeResult: name === cardFeedErrorTestCaseNames.admin || name === cardFeedErrorTestCaseNames.employee  ? 403 : 200,
+                fundID: String(brokenCardFeed.workspaceAccountID),
             },
         },
-        [ONYXKEYS.SESSION]: {
-            email: 'johndoe12@expensify.com',
+        [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
+            id: '1',
+            name: 'Workspace 1',
+            owner: name === cardFeedErrorTestCaseNames.admin ? userID : 'otheruser@expensify.com',
+            role: name === cardFeedErrorTestCaseNames.admin ? 'admin' : 'user',
+            workspaceAccountID: brokenCardFeed.workspaceAccountID,
         },
     }) as unknown as OnyxMultiSetInput;
 
 type TestCase = {
     name: string;
     indicatorColor: string;
-    status: string;
+    status: string | undefined;
 };
 
 const TEST_CASES: TestCase[] = [
@@ -131,11 +146,6 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.danger,
         status: CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS,
     },
-    {
-        name: 'has card feed errors',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
-    },
     {
         name: 'has phone number error',
         indicatorColor: defaultTheme.danger,
@@ -146,6 +156,16 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO,
     },
+    {
+        name: cardFeedErrorTestCaseNames.employee,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    },
+    {
+        name: cardFeedErrorTestCaseNames.admin,
+        indicatorColor: defaultTheme.success,
+        status: undefined,
+    },
 ];
 
 describe('useAccountTabIndicatorStatus', () => {
@@ -153,12 +173,13 @@ describe('useAccountTabIndicatorStatus', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
+        initOnyxDerivedValues();
     });
 
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
             await act(async () => {
-                await Onyx.multiSet(getMockForStatus(testCase.status));
+                await Onyx.multiSet(getMockForStatus(testCase));
                 await waitForBatchedUpdatesWithAct();
             });
         });
@@ -375,7 +396,8 @@ describe('useAccountTabIndicatorStatus', () => {
                 await Onyx.multiSet({
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
-                            bank: 'Chase',
+                            bank: brokenCardFeed.feedName,
+                            fundID: String(brokenCardFeed.workspaceAccountID),
                             lastScrapeResult: 403,
                         },
                     },

From 0f8baeda3e1925e2b67d5315169b822acf89eb8c Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 15:28:26 +0000
Subject: [PATCH 085/122] test: add `useWorksapceTabIndicatorStatusTest`

---
 .../useWorkspacesTabIndicatorStatusTest.ts    | 459 ++++++++++++++++++
 1 file changed, 459 insertions(+)
 create mode 100644 tests/unit/useWorkspacesTabIndicatorStatusTest.ts

diff --git a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
new file mode 100644
index 000000000000..fc2bbd736e5d
--- /dev/null
+++ b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
@@ -0,0 +1,459 @@
+import {act, renderHook} from '@testing-library/react-native';
+import type {OnyxMultiSetInput} from 'react-native-onyx';
+import Onyx from 'react-native-onyx';
+import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorStatus';
+// eslint-disable-next-line no-restricted-imports
+import {defaultTheme} from '@styles/theme';
+import CONST from '@src/CONST';
+import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
+import ONYXKEYS from '@src/ONYXKEYS';
+import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
+
+const userID = 'admin@expensify.com';
+const otherUserID = 'employee@example.com';
+
+const WORKSPACE = {
+    policyID: '1',
+    workspaceAccountID: 12345,
+    policyName: 'Test Workspace',
+};
+
+const CARD_FEED = {
+    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+    workspaceAccountID: WORKSPACE.workspaceAccountID,
+};
+
+type TestCase = {
+    name: string;
+    indicatorColor: string;
+    status: string | undefined;
+};
+
+const TEST_CASE_NAMES = {
+    hasPolicyErrors: 'has policy errors',
+    hasCustomUnitsError: 'has custom units error',
+    hasEmployeeListError: 'has employee list error',
+    hasSyncErrors: 'has sync errors',
+    hasQBOExportError: 'has QBO export error',
+    hasUberCredentialsError: 'has Uber credentials error',
+    hasAdminCardFeedErrors: 'has admin card feed errors',
+    noErrors: 'no errors',
+} as const;
+
+const getMockForStatus = ({name}: TestCase) =>
+    ({
+        [ONYXKEYS.SESSION]: {
+            email: userID,
+        },
+        [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+            id: WORKSPACE.policyID,
+            name: WORKSPACE.policyName,
+            owner: userID,
+            role: 'admin',
+            workspaceAccountID: WORKSPACE.workspaceAccountID,
+            // Policy errors
+            errors: name === TEST_CASE_NAMES.hasPolicyErrors ? {policyError: 'Something went wrong'} : undefined,
+            errorFields: undefined,
+            // Custom units errors
+            customUnits: name === TEST_CASE_NAMES.hasCustomUnitsError ? {errors: {customUnitError: 'Invalid custom unit'}} : undefined,
+            // Employee list errors
+            employeeList:
+                name === TEST_CASE_NAMES.hasEmployeeListError
+                    ? {
+                          otherUserID: {
+                              email: otherUserID,
+                              errors: {employeeError: 'Employee error'},
+                          },
+                      }
+                    : undefined,
+            // Sync errors - use connections with sync errors
+            // hasSynchronizationErrorMessage requires: !isSyncInProgress && lastSync exists && isSuccessful === false && errorDate exists
+            connections: {
+                ...(name === TEST_CASE_NAMES.hasSyncErrors ? {
+                    quickbooksOnline: {
+                        lastSync: {
+                            errorMessage: 'Sync failed',
+                            isSuccessful: false,
+                            errorDate: new Date().toISOString(),
+                        },
+                    },
+                } : {}),
+                ...(name === TEST_CASE_NAMES.hasQBOExportError ? {
+                    quickbooksOnline: {
+                        config: {
+                            reimbursableExpensesExportDestination: 'VENDOR_BILL',
+                            reimbursableExpensesAccount: undefined,
+                        },
+                    },
+                } : {}),
+            },
+            // Uber credentials error
+            receiptPartners:
+                name === TEST_CASE_NAMES.hasUberCredentialsError
+                    ? {
+                          uber: {
+                              error: 'Invalid Uber credentials',
+                          },
+                      }
+                    : undefined,
+        },
+        // Card feed errors for admin
+        [`${ONYXKEYS.CARD_LIST}`]:
+            name === TEST_CASE_NAMES.hasAdminCardFeedErrors
+                ? {
+                      card1: {
+                          cardID: 1,
+                          bank: CARD_FEED.feedName,
+                          lastScrapeResult: 403, // Broken connection
+                          fundID: String(CARD_FEED.workspaceAccountID),
+                      },
+                  }
+                : {},
+        // Sync progress - not in progress
+        [`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${WORKSPACE.policyID}` as const]:
+            name === TEST_CASE_NAMES.hasSyncErrors
+                ? {
+                      stageInProgress: null,
+                      connectionName: 'quickbooksOnline',
+                  }
+                : undefined,
+    }) as unknown as OnyxMultiSetInput;
+
+const TEST_CASES: TestCase[] = [
+    {
+        name: TEST_CASE_NAMES.hasPolicyErrors,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS,
+    },
+    {
+        name: TEST_CASE_NAMES.hasCustomUnitsError,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR,
+    },
+    {
+        name: TEST_CASE_NAMES.hasEmployeeListError,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR,
+    },
+    {
+        name: TEST_CASE_NAMES.hasSyncErrors,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS,
+    },
+    {
+        name: TEST_CASE_NAMES.hasQBOExportError,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR,
+    },
+    {
+        name: TEST_CASE_NAMES.hasUberCredentialsError,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR,
+    },
+    {
+        name: TEST_CASE_NAMES.hasAdminCardFeedErrors,
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+    },
+];
+
+describe('useWorkspacesTabIndicatorStatus', () => {
+    beforeAll(() => {
+        Onyx.init({
+            keys: ONYXKEYS,
+        });
+        initOnyxDerivedValues();
+    });
+
+    describe.each(TEST_CASES)('$name', (testCase) => {
+        beforeAll(async () => {
+            await act(async () => {
+                await Onyx.multiSet(getMockForStatus(testCase));
+                await waitForBatchedUpdatesWithAct();
+            });
+        });
+
+        it('returns correct indicatorColor', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {indicatorColor} = result.current;
+            expect(indicatorColor).toBe(testCase.indicatorColor);
+        });
+
+        it('returns correct status', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status} = result.current;
+            expect(status).toBe(testCase.status);
+        });
+
+        it('returns policyIDWithErrors', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {policyIDWithErrors} = result.current;
+            expect(policyIDWithErrors).toBe(WORKSPACE.policyID);
+        });
+    });
+
+    describe('no errors', () => {
+        beforeAll(async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: userID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'admin',
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+        });
+
+        it('returns undefined status when no errors exist', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status} = result.current;
+            expect(status).toBeUndefined();
+        });
+
+        it('returns success color when no errors exist', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {indicatorColor} = result.current;
+            expect(indicatorColor).toBe(defaultTheme.success);
+        });
+
+        it('returns undefined policyIDWithErrors when no errors exist', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {policyIDWithErrors} = result.current;
+            expect(policyIDWithErrors).toBeUndefined();
+        });
+    });
+
+    describe('non-admin user', () => {
+        beforeAll(async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: otherUserID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'user', // Non-admin role
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                        // Policy errors should NOT show for non-admin
+                        errors: {policyError: 'Something went wrong'},
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+        });
+
+        it('does not show policy errors for non-admin', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status} = result.current;
+            // Policy errors require admin role
+            expect(status).toBeUndefined();
+        });
+    });
+
+    describe('card feed errors - admin vs non-admin', () => {
+        beforeEach(async () => {
+            await Onyx.clear();
+            await waitForBatchedUpdatesWithAct();
+        });
+
+        it('shows card feed errors for admin user', async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: userID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'admin',
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {
+                        card1: {
+                            cardID: 1,
+                            bank: CARD_FEED.feedName,
+                            lastScrapeResult: 403, // Broken connection
+                            fundID: String(CARD_FEED.workspaceAccountID),
+                        },
+                    },
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status, indicatorColor} = result.current;
+
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS);
+            expect(indicatorColor).toBe(defaultTheme.danger);
+        });
+
+        it('does NOT show card feed errors in workspaces tab for non-admin user', async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: otherUserID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'user', // Non-admin
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {
+                        card1: {
+                            cardID: 1,
+                            bank: CARD_FEED.feedName,
+                            lastScrapeResult: 403, // Broken connection
+                            fundID: String(CARD_FEED.workspaceAccountID),
+                        },
+                    },
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status} = result.current;
+
+            // Non-admin should NOT see policy admin card feed errors in workspaces tab
+            // (they see it in account tab as HAS_EMPLOYEE_CARD_FEED_ERRORS instead)
+            expect(status).not.toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS);
+        });
+    });
+
+    describe('multiple policies with errors', () => {
+        const SECOND_WORKSPACE = {
+            policyID: '2',
+            workspaceAccountID: 67890,
+            policyName: 'Second Workspace',
+        };
+
+        beforeAll(async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: userID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'admin',
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                        // No errors on first policy
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${SECOND_WORKSPACE.policyID}` as const]: {
+                        id: SECOND_WORKSPACE.policyID,
+                        name: SECOND_WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'admin',
+                        workspaceAccountID: SECOND_WORKSPACE.workspaceAccountID,
+                        // Errors on second policy
+                        errors: {policyError: 'Something went wrong'},
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+        });
+
+        it('returns error status when at least one policy has errors', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status, indicatorColor} = result.current;
+
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS);
+            expect(indicatorColor).toBe(defaultTheme.danger);
+        });
+
+        it('returns the policyID of the policy with errors', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {policyIDWithErrors} = result.current;
+
+            expect(policyIDWithErrors).toBe(SECOND_WORKSPACE.policyID);
+        });
+    });
+
+    describe('missing data', () => {
+        beforeAll(async () => {
+            // Clear all Onyx data first to ensure clean state
+            await Onyx.clear();
+            await waitForBatchedUpdatesWithAct();
+        });
+
+        it('handles missing data gracefully', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status, indicatorColor, policyIDWithErrors} = result.current;
+
+            expect(status).toBeUndefined();
+            expect(indicatorColor).toBe(defaultTheme.success);
+            expect(policyIDWithErrors).toBeUndefined();
+        });
+    });
+
+    describe('error priority', () => {
+        beforeAll(async () => {
+            await act(async () => {
+                await Onyx.multiSet({
+                    [ONYXKEYS.SESSION]: {
+                        email: userID,
+                    },
+                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
+                        id: WORKSPACE.policyID,
+                        name: WORKSPACE.policyName,
+                        owner: userID,
+                        role: 'admin',
+                        workspaceAccountID: WORKSPACE.workspaceAccountID,
+                        // Multiple errors at once
+                        errors: {policyError: 'Policy error'},
+                        customUnits: {errors: {customUnitError: 'Custom unit error'}},
+                        employeeList: {
+                            [otherUserID]: {
+                                email: otherUserID,
+                                errors: {employeeError: 'Employee error'},
+                            },
+                        },
+                    },
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                } as unknown as OnyxMultiSetInput);
+                await waitForBatchedUpdatesWithAct();
+            });
+        });
+
+        it('returns the first error found based on check order', async () => {
+            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
+            await waitForBatchedUpdatesWithAct();
+            const {status} = result.current;
+
+            // Based on the order in useNavigationTabBarIndicatorChecks:
+            // 1. HAS_POLICY_ERRORS
+            // 2. HAS_CUSTOM_UNITS_ERROR
+            // 3. HAS_EMPLOYEE_LIST_ERROR
+            // etc.
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS);
+        });
+    });
+});

From 045a3325e8c478aa329d2ac3e00d9aa2c5462bd7 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 15:36:33 +0000
Subject: [PATCH 086/122] test: differentiate between employee and admin

---
 .../useWorkspacesTabIndicatorStatusTest.ts    | 107 ++++--------------
 1 file changed, 25 insertions(+), 82 deletions(-)

diff --git a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
index fc2bbd736e5d..9c00eac56e04 100644
--- a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
+++ b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
@@ -29,6 +29,11 @@ type TestCase = {
     status: string | undefined;
 };
 
+const cardFeedErrorTestCaseNames = {
+    admin: 'has card feed error if admin',
+    employee: 'has no card feed error if employee (non-admin)',
+} as const;
+
 const TEST_CASE_NAMES = {
     hasPolicyErrors: 'has policy errors',
     hasCustomUnitsError: 'has custom units error',
@@ -36,20 +41,21 @@ const TEST_CASE_NAMES = {
     hasSyncErrors: 'has sync errors',
     hasQBOExportError: 'has QBO export error',
     hasUberCredentialsError: 'has Uber credentials error',
-    hasAdminCardFeedErrors: 'has admin card feed errors',
+    hasAdminCardFeedErrors: cardFeedErrorTestCaseNames.admin,
+    hasNoCardFeedErrorsForEmployee: cardFeedErrorTestCaseNames.employee,
     noErrors: 'no errors',
 } as const;
 
 const getMockForStatus = ({name}: TestCase) =>
     ({
         [ONYXKEYS.SESSION]: {
-            email: userID,
+            email: name === cardFeedErrorTestCaseNames.employee ? otherUserID : userID,
         },
         [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
             id: WORKSPACE.policyID,
             name: WORKSPACE.policyName,
-            owner: userID,
-            role: 'admin',
+            owner: name === cardFeedErrorTestCaseNames.employee ? userID : userID,
+            role: name === cardFeedErrorTestCaseNames.employee ? 'user' : 'admin',
             workspaceAccountID: WORKSPACE.workspaceAccountID,
             // Policy errors
             errors: name === TEST_CASE_NAMES.hasPolicyErrors ? {policyError: 'Something went wrong'} : undefined,
@@ -97,9 +103,10 @@ const getMockForStatus = ({name}: TestCase) =>
                       }
                     : undefined,
         },
-        // Card feed errors for admin
+        // Card feed errors - both admin and employee test cases have broken card connection
+        // Admin sees HAS_POLICY_ADMIN_CARD_FEED_ERRORS, employee does NOT see it in workspaces tab
         [`${ONYXKEYS.CARD_LIST}`]:
-            name === TEST_CASE_NAMES.hasAdminCardFeedErrors
+            name === cardFeedErrorTestCaseNames.admin || name === cardFeedErrorTestCaseNames.employee
                 ? {
                       card1: {
                           cardID: 1,
@@ -151,10 +158,15 @@ const TEST_CASES: TestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR,
     },
     {
-        name: TEST_CASE_NAMES.hasAdminCardFeedErrors,
+        name: cardFeedErrorTestCaseNames.admin,
         indicatorColor: defaultTheme.danger,
         status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
     },
+    {
+        name: cardFeedErrorTestCaseNames.employee,
+        indicatorColor: defaultTheme.success,
+        status: undefined,
+    },
 ];
 
 describe('useWorkspacesTabIndicatorStatus', () => {
@@ -191,7 +203,12 @@ describe('useWorkspacesTabIndicatorStatus', () => {
             const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
             await waitForBatchedUpdatesWithAct();
             const {policyIDWithErrors} = result.current;
-            expect(policyIDWithErrors).toBe(WORKSPACE.policyID);
+            // Employee test case has no policy errors visible (card feed errors don't show for employees in workspaces tab)
+            if (testCase.name === cardFeedErrorTestCaseNames.employee) {
+                expect(policyIDWithErrors).toBeUndefined();
+            } else {
+                expect(policyIDWithErrors).toBe(WORKSPACE.policyID);
+            }
         });
     });
 
@@ -268,80 +285,6 @@ describe('useWorkspacesTabIndicatorStatus', () => {
         });
     });
 
-    describe('card feed errors - admin vs non-admin', () => {
-        beforeEach(async () => {
-            await Onyx.clear();
-            await waitForBatchedUpdatesWithAct();
-        });
-
-        it('shows card feed errors for admin user', async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: userID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'admin',
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {
-                        card1: {
-                            cardID: 1,
-                            bank: CARD_FEED.feedName,
-                            lastScrapeResult: 403, // Broken connection
-                            fundID: String(CARD_FEED.workspaceAccountID),
-                        },
-                    },
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status, indicatorColor} = result.current;
-
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS);
-            expect(indicatorColor).toBe(defaultTheme.danger);
-        });
-
-        it('does NOT show card feed errors in workspaces tab for non-admin user', async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: otherUserID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'user', // Non-admin
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {
-                        card1: {
-                            cardID: 1,
-                            bank: CARD_FEED.feedName,
-                            lastScrapeResult: 403, // Broken connection
-                            fundID: String(CARD_FEED.workspaceAccountID),
-                        },
-                    },
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status} = result.current;
-
-            // Non-admin should NOT see policy admin card feed errors in workspaces tab
-            // (they see it in account tab as HAS_EMPLOYEE_CARD_FEED_ERRORS instead)
-            expect(status).not.toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS);
-        });
-    });
-
     describe('multiple policies with errors', () => {
         const SECOND_WORKSPACE = {
             policyID: '2',

From 914c5a84edc6e12a2972860e64c040b010081c74 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 15:46:42 +0000
Subject: [PATCH 087/122] refactor: rename workspace account id/feed name
 mappings in `cardFeedErrors`

---
 src/hooks/useCardFeedErrors.ts                   |  4 ++--
 src/hooks/usePoliciesWithCardFeedErrors.ts       |  4 ++--
 .../OnyxDerived/configs/cardFeedErrors.ts        | 12 ++++++------
 src/pages/settings/Wallet/PaymentMethodList.tsx  |  6 +++---
 src/pages/workspace/WorkspaceInitialPage.tsx     |  6 ++----
 .../WorkspaceCompanyCardFeedSelectorPage.tsx     |  4 ++--
 src/types/onyx/DerivedValues.ts                  |  4 ++--
 tests/unit/CardFeedErrorsDerivedValueTest.ts     | 16 ++++++++--------
 8 files changed, 27 insertions(+), 29 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 50e96bd7c469..63fcec8977bd 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -6,8 +6,8 @@ const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     cardFeedErrors: {},
     cardsWithBrokenFeedConnection: {},
     shouldShowRBR: false,
-    rbrWorkspaceAccountIDMapping: {},
-    rbrFeedNameWithDomainIDMapping: {},
+    shouldShowRbrForWorkspaceAccountID: {},
+    shouldShowRbrForFeedNameWithDomainID: {},
     isFeedConnectionBroken: false,
     hasFeedErrors: false,
     hasWorkspaceErrors: false,
diff --git a/src/hooks/usePoliciesWithCardFeedErrors.ts b/src/hooks/usePoliciesWithCardFeedErrors.ts
index 2388fcd26ed0..f83402bbf153 100644
--- a/src/hooks/usePoliciesWithCardFeedErrors.ts
+++ b/src/hooks/usePoliciesWithCardFeedErrors.ts
@@ -18,10 +18,10 @@ function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
     // those should be cleaned out before doing any error checking
     const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
-    const {rbrWorkspaceAccountIDMapping} = useCardFeedErrors();
+    const {shouldShowRbrForWorkspaceAccountID} = useCardFeedErrors();
     let hasCardFeedErrors = false;
     const policiesWithCardFeedErrors: Policy[] = [];
-    for (const [workspaceAccountID, hasErrors] of Object.entries(rbrWorkspaceAccountIDMapping)) {
+    for (const [workspaceAccountID, hasErrors] of Object.entries(shouldShowRbrForWorkspaceAccountID)) {
         if (!hasErrors) {
             continue;
         }
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 181c153dcbb4..d4b3dd194702 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -12,8 +12,8 @@ export default createOnyxDerivedValueConfig({
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
         const cardFeedErrors: CardFeedErrorsObject = {};
-        const rbrWorkspaceAccountIDMapping: Record<number, boolean> = {};
-        const rbrFeedNameWithDomainIDMapping: Record<string, boolean> = {};
+        const shouldShowRbrForWorkspaceAccountID: Record<number, boolean> = {};
+        const shouldShowRbrForFeedNameWithDomainID: Record<string, boolean> = {};
         let isSomeFeedConnectionBroken = false;
         let hasSomeFeedErrors = false;
         let hasSomeWorkspaceErrors = false;
@@ -87,9 +87,9 @@ export default createOnyxDerivedValueConfig({
                 hasSomeFailedCardAssignment = true;
             }
 
-            rbrWorkspaceAccountIDMapping[workspaceAccountID] = rbrWorkspaceAccountIDMapping[workspaceAccountID] || shouldShowRBR;
+            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
 
-            rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID] || shouldShowRBR;
+            shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {
@@ -118,8 +118,8 @@ export default createOnyxDerivedValueConfig({
             cardsWithBrokenFeedConnection,
 
             // Mappings of whether to show the RBR for each workspace account ID and per feed name with domain ID
-            rbrWorkspaceAccountIDMapping,
-            rbrFeedNameWithDomainIDMapping,
+            shouldShowRbrForWorkspaceAccountID,
+            shouldShowRbrForFeedNameWithDomainID,
 
             // Whether any of the feeds has one of the below errors
             shouldShowRBR,
diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 4f6c588950e8..932c419b4908 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -183,7 +183,7 @@ function PaymentMethodList({
     // Temporarily disabled because P2P debit cards are disabled.
     // const [fundList = getEmptyObject<FundList>()] = useOnyx(ONYXKEYS.FUND_LIST);
 
-    const {rbrFeedNameWithDomainIDMapping} = useCardFeedErrors();
+    const {shouldShowRbrForFeedNameWithDomainID} = useCardFeedErrors();
 
     const filteredPaymentMethods = useMemo(() => {
         if (shouldShowAssignedCards) {
@@ -201,7 +201,7 @@ function PaymentMethodList({
                 let shouldShowRBR = false;
                 if (card.fundID) {
                     const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeedWithNumber, card.fundID);
-                    shouldShowRBR = rbrFeedNameWithDomainIDMapping[feedNameWithDomainID];
+                    shouldShowRBR = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID];
                 } else {
                     shouldShowRBR = true;
                 }
@@ -395,7 +395,7 @@ function PaymentMethodList({
         cardList,
         illustrations,
         companyCardFeedIcons,
-        rbrFeedNameWithDomainIDMapping,
+        shouldShowRbrForFeedNameWithDomainID,
         privatePersonalDetails,
         onPress,
         shouldShowRightIcon,
diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx
index 596d2c31fe8b..9ddae90da784 100644
--- a/src/pages/workspace/WorkspaceInitialPage.tsx
+++ b/src/pages/workspace/WorkspaceInitialPage.tsx
@@ -198,10 +198,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
 
     const [highlightedFeature, setHighlightedFeature] = useState<string | undefined>(undefined);
 
-    const {cardFeedErrors} = useCardFeedErrors();
-    const policyCardFeedErrors = Object.entries(cardFeedErrors ?? {}).filter(([key]) => key.includes(String(workspaceAccountID)));
-
-    const shouldShowRBR = policyCardFeedErrors.some(([, feedErrors]) => feedErrors.shouldShowRBR);
+    const {shouldShowRbrForWorkspaceAccountID} = useCardFeedErrors();
+    const shouldShowRBR = shouldShowRbrForWorkspaceAccountID[workspaceAccountID];
 
     const workspaceMenuItems: WorkspaceMenuItem[] = useMemo(() => {
         const protectedMenuItems: WorkspaceMenuItem[] = [];
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 0b3a3aec0949..5536c7266c30 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -56,14 +56,14 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
     const icons = useMemoizedLazyExpensifyIcons(['Plus']);
 
     const {companyCardFeeds, feedName: selectedFeedName} = useCompanyCards({policyID});
-    const {rbrFeedNameWithDomainIDMapping} = useCardFeedErrors();
+    const {shouldShowRbrForFeedNameWithDomainID} = useCardFeedErrors();
 
     const feeds: CardFeedListItem[] = (Object.entries(companyCardFeeds ?? {}) as Array<[CompanyCardFeedWithDomainID, CombinedCardFeed]>).map(([feedName, feedSettings]) => {
         const plaidUrl = getPlaidInstitutionIconUrl(feedSettings.feed);
         const domain = allDomains?.[`${ONYXKEYS.COLLECTION.DOMAIN}${feedSettings.domainID}`];
         const domainName = domain?.email ? Str.extractEmailDomain(domain.email) : undefined;
 
-        const shouldShowRBR = rbrFeedNameWithDomainIDMapping[feedName];
+        const shouldShowRBR = shouldShowRbrForFeedNameWithDomainID[feedName];
 
         return {
             value: feedName,
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 8ec990162a97..04d3bf9e503e 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -164,11 +164,11 @@ type CardFeedErrors = {
     /**
      * Whether to show the RBR for each workspace account ID.
      */
-    rbrWorkspaceAccountIDMapping: Record<number, boolean>;
+    shouldShowRbrForWorkspaceAccountID: Record<number, boolean>;
     /**
      * Whether to show the RBR for each feed name with domain ID.
      */
-    rbrFeedNameWithDomainIDMapping: Record<string, boolean>;
+    shouldShowRbrForFeedNameWithDomainID: Record<string, boolean>;
     /**
      * Whether some feed connection is broken.
      */
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index 3ff889327880..26253bfc026d 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -415,7 +415,7 @@ describe('CardFeedErrors Derived Value', () => {
         });
 
         describe('RBR mappings', () => {
-            it('should set rbrWorkspaceAccountIDMapping correctly', () => {
+            it('should set shouldShowRbrForWorkspaceAccountID correctly', () => {
                 const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
@@ -429,10 +429,10 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[cardFeed.workspaceAccountID]).toBe(true);
+                expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed.workspaceAccountID]).toBe(true);
             });
 
-            it('should set rbrFeedNameWithDomainIDMapping correctly', () => {
+            it('should set shouldShowRbrForFeedNameWithDomainID correctly', () => {
                 const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
 
                 const card = createCard({
@@ -446,7 +446,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrFeedNameWithDomainIDMapping[cardFeed.feedNameWithDomainID]).toBe(true);
+                expect(result.shouldShowRbrForFeedNameWithDomainID[cardFeed.feedNameWithDomainID]).toBe(true);
             });
 
             it('should not set RBR when no errors exist', () => {
@@ -463,8 +463,8 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[cardFeed.workspaceAccountID]).toBe(false);
-                expect(result.rbrFeedNameWithDomainIDMapping[cardFeed.feedNameWithDomainID]).toBe(false);
+                expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed.workspaceAccountID]).toBe(false);
+                expect(result.shouldShowRbrForFeedNameWithDomainID[cardFeed.feedNameWithDomainID]).toBe(false);
                 expect(result.shouldShowRBR).toBe(false);
             });
         });
@@ -491,8 +491,8 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.rbrWorkspaceAccountIDMapping[cardFeed1.workspaceAccountID]).toBe(true);
-                expect(result.rbrWorkspaceAccountIDMapping[cardFeed2.workspaceAccountID]).toBe(false);
+                expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed1.workspaceAccountID]).toBe(true);
+                expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed2.workspaceAccountID]).toBe(false);
                 expect(result.isFeedConnectionBroken).toBe(true);
                 expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card1));
                 expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card2));

From 665e14f1d8763fd7a3d94b580e422a0312be6b12 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 16:29:43 +0000
Subject: [PATCH 088/122] refactor: further split up card feed error states by
 company cards/Expensify cards

---
 src/hooks/useCardFeedErrors.ts                |  18 ++-
 src/hooks/useUpdateFeedBrokenConnection.ts    |   2 +-
 src/libs/CardUtils.ts                         |   6 +-
 .../OnyxDerived/configs/cardFeedErrors.ts     | 136 ++++++++++++------
 src/pages/settings/InitialSettingsPage.tsx    |   2 +-
 src/types/onyx/Card.ts                        |   4 +-
 src/types/onyx/CardFeeds.ts                   |   4 +-
 src/types/onyx/DerivedValues.ts               |  54 +++----
 tests/unit/CardFeedErrorsDerivedValueTest.ts  |  50 +++----
 9 files changed, 170 insertions(+), 106 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 63fcec8977bd..ca189e241836 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -1,17 +1,23 @@
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {CardFeedErrors} from '@src/types/onyx/DerivedValues';
+import type {CardFeedErrors, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import useOnyx from './useOnyx';
 
+const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
+    shouldShowRBR: false,
+    isFeedConnectionBroken: false,
+    hasFeedErrors: false,
+    hasWorkspaceErrors: false,
+    hasFailedCardAssignments: false,
+};
+
 const DEFAULT_CARD_FEED_ERRORS: CardFeedErrors = {
     cardFeedErrors: {},
     cardsWithBrokenFeedConnection: {},
-    shouldShowRBR: false,
     shouldShowRbrForWorkspaceAccountID: {},
     shouldShowRbrForFeedNameWithDomainID: {},
-    isFeedConnectionBroken: false,
-    hasFeedErrors: false,
-    hasWorkspaceErrors: false,
-    hasFailedCardAssignment: false,
+    all: DEFAULT_CARD_FEED_ERROR_STATE,
+    companyCards: DEFAULT_CARD_FEED_ERROR_STATE,
+    expensifyCard: DEFAULT_CARD_FEED_ERROR_STATE,
 };
 
 function useCardFeedErrors(): CardFeedErrors {
diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts
index 23ce0b486c96..494d2e9f6f95 100644
--- a/src/hooks/useUpdateFeedBrokenConnection.ts
+++ b/src/hooks/useUpdateFeedBrokenConnection.ts
@@ -13,7 +13,7 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI
     const companyFeeds = getCompanyFeeds(cardFeeds);
     const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
     const domainOrWorkspaceAccountID = feed ? getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[feed]) : CONST.DEFAULT_NUMBER_ID;
-    const {isFeedConnectionBroken, cardsWithBrokenFeedConnection} = useCardFeedErrors();
+    const {all: {isFeedConnectionBroken}, cardsWithBrokenFeedConnection} = useCardFeedErrors();
 
     const [brokenCardId, firstCardWithBrokenFeedConnection] = Object.entries(cardsWithBrokenFeedConnection).at(0) ?? [];
 
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index c7692cc71ace..b0ad1ce9b81c 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -23,7 +23,7 @@ import type {
     WorkspaceCardsList,
 } from '@src/types/onyx';
 import type {FilteredCardList} from '@src/types/onyx/Card';
-import type {CardFeedData, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CardFeedData, CardFeedWithDomainID, CardFeedWithNumber, 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
@@ -557,11 +557,11 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry<CompanyCardFeedWithDomainID
     return isValidLastFeed ? lastSelectedFeed : defaultFeed;
 }
 
-function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithNumber, domainID: number | string): CompanyCardFeedWithDomainID {
+function getCompanyCardFeedWithDomainID(feedName: CardFeedWithNumber, domainID: number | string): CardFeedWithDomainID {
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
-function splitCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithDomainID | undefined): {feedName: CompanyCardFeed | undefined; domainID: number | undefined} {
+function splitCompanyCardFeedWithDomainID(feedName: CardFeedWithDomainID | undefined): {feedName: CompanyCardFeed | undefined; domainID: number | undefined} {
     if (!feedName) {
         return {feedName: undefined, domainID: undefined};
     }
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index d4b3dd194702..255d0cf3dd98 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -3,9 +3,35 @@ import {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBro
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
-import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
-import type {CardErrors, CardFeedErrorsObject} from '@src/types/onyx/DerivedValues';
+import type {CardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import CONST from '@src/CONST';
+
+const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
+    shouldShowRBR: false,
+    hasFailedCardAssignments: false,
+    hasWorkspaceErrors: false,
+    hasFeedErrors: false,
+    isFeedConnectionBroken: false,
+};
+
+function getShouldShowRBR(state: Partial<CardFeedErrorState>): boolean {
+    if (state.hasFeedErrors) {
+        return true;
+    }
+    if (state.hasWorkspaceErrors) {
+        return true;
+    }
+    if (state.hasFailedCardAssignments) {
+        return true;
+    }
+    if (state.isFeedConnectionBroken) {
+        return true;
+    }
+
+    return false;
+}
 
 export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
@@ -14,10 +40,10 @@ export default createOnyxDerivedValueConfig({
         const cardFeedErrors: CardFeedErrorsObject = {};
         const shouldShowRbrForWorkspaceAccountID: Record<number, boolean> = {};
         const shouldShowRbrForFeedNameWithDomainID: Record<string, boolean> = {};
-        let isSomeFeedConnectionBroken = false;
-        let hasSomeFeedErrors = false;
-        let hasSomeWorkspaceErrors = false;
-        let hasSomeFailedCardAssignment = false;
+
+        const allFeedsState: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
+        const companyCardFeedsState: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
+        const expensifyCardFeedStates: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
 
         const cardsWithBrokenFeedConnection: Record<string, Card> = {};
 
@@ -25,23 +51,24 @@ export default createOnyxDerivedValueConfig({
         const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
 
         function addErrorsForCard(card: Card) {
-            const bankName = card.bank as CompanyCardFeedWithNumber;
+            const bankName = card.bank as CardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
 
+            const isExpensifyCard = bankName === CONST.EXPENSIFY_CARD.BANK;
+
             if (Number.isNaN(workspaceAccountID)) {
                 return;
             }
 
-            const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
-
-            const selectedFeed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
-            const feedErrors = selectedFeed?.errors
+            const currentFeedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
+            const currentFeed = combinedCompanyCardFeeds?.[currentFeedNameWithDomainID];
+            const currentFeedErrors = currentFeed?.errors
 
             const hasFailedCardAssignments = !isEmptyObject(
-                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
+                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${currentFeedNameWithDomainID}`],
             );
 
-            let cardErrors: Record<string, CardErrors> = cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {};
+            let cardErrors: Record<string, CardErrors> = cardFeedErrors[currentFeedNameWithDomainID]?.cardErrors ?? {};
             const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
 
             if (hasCardErrors) {
@@ -55,41 +82,66 @@ export default createOnyxDerivedValueConfig({
                 };
             }
 
-            const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
-            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false;
-            const isFeedConnectionBroken = isCardConnectionBroken(card);
-            const shouldShowRBR = hasFailedCardAssignments || hasFeedErrors || isFeedConnectionBroken;
+            const currentFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
+                isFeedConnectionBroken: isCardConnectionBroken(card),
+                hasFeedErrors: currentFeedNameWithDomainID ? !!currentFeedErrors : false,
+                hasWorkspaceErrors: !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors,
+                hasFailedCardAssignments,
+            };
+
+            const shouldShowRbrForCurrentFeed = getShouldShowRBR(currentFeedState)
 
-            cardFeedErrors[feedNameWithDomainID] = {
+            cardFeedErrors[currentFeedNameWithDomainID] = {
+                ...currentFeedState,
+                shouldShowRBR: shouldShowRbrForCurrentFeed,
                 cardErrors,
-                feedErrors,
-                shouldShowRBR,
-                hasWorkspaceErrors,
-                hasFeedErrors,
-                isFeedConnectionBroken,
-                hasFailedCardAssignments,
+                feedErrors: currentFeedErrors,
             };
 
-            if (isFeedConnectionBroken) {
-                isSomeFeedConnectionBroken = true;
+            if (currentFeedState.isFeedConnectionBroken) {
+                allFeedsState.isFeedConnectionBroken = true;
                 cardsWithBrokenFeedConnection[card.cardID] = card;
+
+                if (isExpensifyCard) {
+                    expensifyCardFeedStates.isFeedConnectionBroken = true;
+                } else {
+                    companyCardFeedsState.isFeedConnectionBroken = true;
+                }
             }
 
-            if (hasFeedErrors) {
-                hasSomeFeedErrors = true;
+            if (currentFeedState.hasFeedErrors) {
+                allFeedsState.hasFeedErrors = true;
+
+                if (isExpensifyCard) {
+                    expensifyCardFeedStates.hasFeedErrors = true;
+                } else {
+                    companyCardFeedsState.hasFeedErrors = true;
+                }
             }
 
-            if (hasWorkspaceErrors) {
-                hasSomeWorkspaceErrors = true;
+            if (currentFeedState.hasWorkspaceErrors) {
+                allFeedsState.hasWorkspaceErrors = true;
+
+                if (isExpensifyCard) {
+                    expensifyCardFeedStates.hasWorkspaceErrors = true;
+                } else {
+                    companyCardFeedsState.hasWorkspaceErrors = true;
+                }
             }
 
-            if (hasFailedCardAssignments) {
-                hasSomeFailedCardAssignment = true;
+            if (currentFeedState.hasFailedCardAssignments) {
+                allFeedsState.hasFailedCardAssignments = true;
+
+                if (isExpensifyCard) {
+                    expensifyCardFeedStates.hasFailedCardAssignments = true;
+                } else {
+                    companyCardFeedsState.hasFailedCardAssignments = true;
+                }
             }
 
-            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
+            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRbrForCurrentFeed;
 
-            shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
+            shouldShowRbrForFeedNameWithDomainID[currentFeedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[currentFeedNameWithDomainID] || shouldShowRbrForCurrentFeed;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {
@@ -110,7 +162,9 @@ export default createOnyxDerivedValueConfig({
             }
         }
 
-        const shouldShowRBR = hasSomeFeedErrors || hasSomeFailedCardAssignment || isSomeFeedConnectionBroken;
+        allFeedsState.shouldShowRBR = getShouldShowRBR(allFeedsState);
+        companyCardFeedsState.shouldShowRBR = getShouldShowRBR(companyCardFeedsState);
+        expensifyCardFeedStates.shouldShowRBR = getShouldShowRBR(expensifyCardFeedStates);
 
         return {
             // The errors of all card feeds.
@@ -122,11 +176,13 @@ export default createOnyxDerivedValueConfig({
             shouldShowRbrForFeedNameWithDomainID,
 
             // Whether any of the feeds has one of the below errors
-            shouldShowRBR,
-            isFeedConnectionBroken: isSomeFeedConnectionBroken,
-            hasFeedErrors: hasSomeFeedErrors,
-            hasWorkspaceErrors: hasSomeWorkspaceErrors,
-            hasFailedCardAssignment: hasSomeFailedCardAssignment,
+            all: allFeedsState,
+
+            // Whether any of the company cards has one of the below errors
+            companyCards: companyCardFeedsState,
+
+            // Whether any of the expensify cards has one of the below errors
+            expensifyCard: expensifyCardFeedStates,
         };
     },
 });
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index ce1595199b66..e88736174272 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -145,7 +145,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const shouldDisplayLHB = !shouldUseNarrowLayout;
 
-    const {shouldShowRBR} = useCardFeedErrors();
+    const {all: {shouldShowRBR}} = useCardFeedErrors();
 
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index 05e10fe6518f..85b3ec1fbd54 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -1,6 +1,6 @@
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
-import type {CompanyCardFeedWithDomainID} from './CardFeeds';
+import type {CardFeedWithDomainID} from './CardFeeds';
 import type * as OnyxCommon from './OnyxCommon';
 import type PersonalDetails from './PersonalDetails';
 
@@ -298,7 +298,7 @@ type FailedCompanyCardAssignment = {
     domainOrWorkspaceAccountID: number;
 
     /** The name of the feed */
-    feed: CompanyCardFeedWithDomainID;
+    feed: CardFeedWithDomainID;
 
     /** Cardholder personal details */
     cardholder?: PersonalDetails;
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 650b0a5c4bf4..3ce0c2c844bc 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -150,7 +150,7 @@ type DirectCardFeedData = OnyxCommon.OnyxValueWithOfflineFeedback<{
 type CardFeedData = CustomCardFeedData | DirectCardFeedData;
 
 /** Both custom and direct company feeds */
-type CompanyFeeds = Partial<Record<CompanyCardFeed, CardFeedData>>;
+type CompanyFeeds = Partial<Record<CardFeed, CardFeedData>>;
 
 /** Custom feed names */
 type CompanyCardNicknames = Partial<Record<CompanyCardFeedWithNumber, string>>;
@@ -283,7 +283,7 @@ type CombinedCardFeed = CustomCardFeedData &
     };
 
 /** Card feeds combined by domain ID into one object */
-type CombinedCardFeeds = Record<CompanyCardFeedWithDomainID, CombinedCardFeed>;
+type CombinedCardFeeds = Record<CardFeedWithDomainID, CombinedCardFeed>;
 
 export default CardFeeds;
 export type {
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 04d3bf9e503e..7e9202ea7093 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -91,18 +91,9 @@ type CardErrors = {
 };
 
 /**
- * The errors of a card feed.
+ * The state of card feed errors.
  */
-type FeedErrors = {
-    /**
-     * The errors of the feed.
-     */
-    feedErrors?: Errors;
-    /**
-     * The errors of all cards for a specific feed within a workspace/domain.
-     */
-    cardErrors: Record<string, CardErrors>;
-
+type CardFeedErrorState ={
     /**
      * Whether to show the RBR for a specific feed within a workspace/domain.
      * This will be true, if any of the below conditions are true:
@@ -113,7 +104,7 @@ type FeedErrors = {
      */
     shouldShowRBR: boolean;
     /**
-     *
+     * Whether some failed card assignments.
      */
     hasFailedCardAssignments: boolean;
     /**
@@ -128,6 +119,20 @@ type FeedErrors = {
      * Whether some feed connection is broken.
      */
     isFeedConnectionBroken: boolean;
+}
+
+/**
+ * The errors of a card feed.
+ */
+type FeedErrors = CardFeedErrorState & {
+    /**
+     * The errors of the feed.
+     */
+    feedErrors?: Errors;
+    /**
+     * The errors of all cards for a specific feed within a workspace/domain.
+     */
+    cardErrors: Record<string, CardErrors>;
 };
 
 /**
@@ -145,8 +150,9 @@ type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
  */
 type CardFeedErrorsObject = Record<CardFeedWithDomainID, FeedErrors>;
 
+
 /**
- *
+ * The errors of card feeds.
  */
 type CardFeedErrors = {
     /**
@@ -157,10 +163,6 @@ type CardFeedErrors = {
      * The cards with a broken feed connection.
      */
     cardsWithBrokenFeedConnection: Record<string, Card>;
-    /**
-     * Whether to show the RBR for all feeds.
-     */
-    shouldShowRBR: boolean;
     /**
      * Whether to show the RBR for each workspace account ID.
      */
@@ -169,22 +171,19 @@ type CardFeedErrors = {
      * Whether to show the RBR for each feed name with domain ID.
      */
     shouldShowRbrForFeedNameWithDomainID: Record<string, boolean>;
+
     /**
-     * Whether some feed connection is broken.
-     */
-    isFeedConnectionBroken: boolean;
-    /**
-     * Whether some feed has errors.
+     * The errors of all card feeds.
      */
-    hasFeedErrors: boolean;
+    all: CardFeedErrorState;
     /**
-     * Whether some workspace has errors.
+     * The errors of company cards.
      */
-    hasWorkspaceErrors: boolean;
+    companyCards: CardFeedErrorState;
     /**
-     * Whether some failed card assignments.
+     * The errors of expensify card.
      */
-    hasFailedCardAssignment: boolean;
+    expensifyCard: CardFeedErrorState;
 };
 
 /**
@@ -203,6 +202,7 @@ export type {
     AllCardFeedErrorsMap,
     CardFeedErrorsObject,
     FeedErrors,
+    CardFeedErrorState,
     CardFeedErrors,
     CardErrors,
 };
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index 26253bfc026d..eb5cbf488fa0 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -74,18 +74,20 @@ describe('CardFeedErrors Derived Value', () => {
 
             expect(result.cardFeedErrors).toEqual({});
             expect(result.cardsWithBrokenFeedConnection).toEqual({});
-            expect(result.shouldShowRBR).toBe(false);
-            expect(result.isFeedConnectionBroken).toBe(false);
-            expect(result.hasFeedErrors).toBe(false);
-            expect(result.hasWorkspaceErrors).toBe(false);
-            expect(result.hasFailedCardAssignment).toBe(false);
+            expect(result.shouldShowRbrForWorkspaceAccountID).toEqual({});
+            expect(result.shouldShowRbrForFeedNameWithDomainID).toEqual({});
+            expect(result.all.shouldShowRBR).toBe(false);
+            expect(result.all.isFeedConnectionBroken).toBe(false);
+            expect(result.all.hasFeedErrors).toBe(false);
+            expect(result.all.hasWorkspaceErrors).toBe(false);
+            expect(result.all.hasFailedCardAssignments).toBe(false);
         });
 
         it('should return empty errors when all inputs are undefined', () => {
             const result = cardFeedErrorsConfig.compute([undefined, undefined, undefined, undefined], DERIVED_VALUE_CONTEXT);
 
             expect(result.cardFeedErrors).toEqual({});
-            expect(result.shouldShowRBR).toBe(false);
+            expect(result.all.shouldShowRBR).toBe(false);
         });
 
         describe('broken feed connection detection', () => {
@@ -102,8 +104,8 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.isFeedConnectionBroken).toBe(true);
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.isFeedConnectionBroken).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
                 expect(result.cardsWithBrokenFeedConnection[CARD_IDS.card1]).toEqual(card);
             });
 
@@ -120,7 +122,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.isFeedConnectionBroken).toBe(false);
+                expect(result.all.isFeedConnectionBroken).toBe(false);
                 expect(result.cardsWithBrokenFeedConnection).toEqual({});
             });
 
@@ -137,7 +139,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.isFeedConnectionBroken).toBe(false);
+                expect(result.all.isFeedConnectionBroken).toBe(false);
             });
 
             it('should NOT detect broken connection when lastScrapeResult is undefined', () => {
@@ -153,7 +155,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.isFeedConnectionBroken).toBe(false);
+                expect(result.all.isFeedConnectionBroken).toBe(false);
             });
         });
 
@@ -173,7 +175,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([{}, allWorkspaceCards, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.isFeedConnectionBroken).toBe(true);
+                expect(result.all.isFeedConnectionBroken).toBe(true);
                 expect(result.cardsWithBrokenFeedConnection[CARD_IDS.card2]).toEqual(card);
             });
 
@@ -259,8 +261,8 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.hasFailedCardAssignment).toBe(true);
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.hasFailedCardAssignments  ).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasFailedCardAssignments).toBe(true);
             });
 
@@ -277,7 +279,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.hasFailedCardAssignment).toBe(false);
+                expect(result.all.hasFailedCardAssignments).toBe(false);
             });
         });
 
@@ -310,8 +312,8 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
-                expect(result.hasFeedErrors).toBe(true);
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.hasFeedErrors).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasFeedErrors).toBe(true);
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.feedErrors).toEqual({feedError: 'Connection failed'});
             });
@@ -346,7 +348,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
-                expect(result.hasWorkspaceErrors).toBe(true);
+                expect(result.all.hasWorkspaceErrors).toBe(true);
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
             });
         });
@@ -465,7 +467,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed.workspaceAccountID]).toBe(false);
                 expect(result.shouldShowRbrForFeedNameWithDomainID[cardFeed.feedNameWithDomainID]).toBe(false);
-                expect(result.shouldShowRBR).toBe(false);
+                expect(result.all.shouldShowRBR).toBe(false);
             });
         });
 
@@ -493,7 +495,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed1.workspaceAccountID]).toBe(true);
                 expect(result.shouldShowRbrForWorkspaceAccountID[cardFeed2.workspaceAccountID]).toBe(false);
-                expect(result.isFeedConnectionBroken).toBe(true);
+                expect(result.all.isFeedConnectionBroken).toBe(true);
                 expect(result.cardsWithBrokenFeedConnection).toHaveProperty(String(CARD_IDS.card1));
                 expect(result.cardsWithBrokenFeedConnection).not.toHaveProperty(String(CARD_IDS.card2));
             });
@@ -554,7 +556,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
 
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
             });
 
             it('should return true when hasFailedCardAssignment is true', () => {
@@ -584,7 +586,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
             });
 
             it('should return true when isFeedConnectionBroken is true', () => {
@@ -601,7 +603,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.shouldShowRBR).toBe(true);
+                expect(result.all.shouldShowRBR).toBe(true);
             });
 
             it('should return false when no errors exist', () => {
@@ -618,7 +620,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT );
 
-                expect(result.shouldShowRBR).toBe(false);
+                expect(result.all.shouldShowRBR).toBe(false);
             });
         });
     });

From 7b8afa850d59964350191f4fecb43a6efc38de34 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 16:30:07 +0000
Subject: [PATCH 089/122] refactor: remove unnecessary template string

---
 tests/unit/useAccountTabIndicatorStatusTest.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 255160fc8a28..3f5c3c38da85 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -209,7 +209,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                    [ONYXKEYS.CARD_LIST]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },

From 8cbd2c89c169dbc145f66cd5f640dbfe4eed8a5f Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 16:30:29 +0000
Subject: [PATCH 090/122] test: add missing `fundID` in cards list mock data

---
 tests/unit/useAccountTabIndicatorStatusTest.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 3f5c3c38da85..e5eafe1caf10 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -419,6 +419,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
                             bank: CONST.EXPENSIFY_CARD.BANK,
+                            fundID: String(brokenCardFeed.workspaceAccountID),
                             lastScrapeResult: 403,
                         },
                     },
@@ -439,6 +440,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
                             bank: 'Chase',
+                            fundID: String(brokenCardFeed.workspaceAccountID),
                             lastScrapeResult: 200,
                         },
                     },

From 4e8140c32675f91719537412d1c44dacd7dac588 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 16:30:40 +0000
Subject: [PATCH 091/122] refactor: make comment more concise

---
 src/hooks/useNavigationTabBarIndicatorChecks.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
index cfaa954fce74..a221b919f894 100644
--- a/src/hooks/useNavigationTabBarIndicatorChecks.ts
+++ b/src/hooks/useNavigationTabBarIndicatorChecks.ts
@@ -58,7 +58,7 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
         [CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
     };
 
-    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
+    // All the error & info-checking methods are put into an array. This is so that using _.some() will return
     // early as soon as the first error / info condition is returned. This makes the checks very efficient since
     // we only care if a single error / info condition exists anywhere.
     const accountChecks: Partial<Record<IndicatorStatus, boolean>> = {

From ece9311e6b711d2963c5f00426907000d9f13291 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 17:04:30 +0000
Subject: [PATCH 092/122] refactor: simplify if blocks

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 255d0cf3dd98..eabf5afe7e2a 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -26,11 +26,10 @@ function getShouldShowRBR(state: Partial<CardFeedErrorState>): boolean {
     if (state.hasFailedCardAssignments) {
         return true;
     }
-    if (state.isFeedConnectionBroken) {
-        return true;
-    }
 
-    return false;
+    return !!state.isFeedConnectionBroken;
+
+
 }
 
 export default createOnyxDerivedValueConfig({

From 1778b2552be8bbc259f8fd437422748e17106c23 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 17:04:42 +0000
Subject: [PATCH 093/122] fix: create new objects

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index eabf5afe7e2a..cc7695ca585a 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -40,9 +40,9 @@ export default createOnyxDerivedValueConfig({
         const shouldShowRbrForWorkspaceAccountID: Record<number, boolean> = {};
         const shouldShowRbrForFeedNameWithDomainID: Record<string, boolean> = {};
 
-        const allFeedsState: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
-        const companyCardFeedsState: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
-        const expensifyCardFeedStates: CardFeedErrorState = DEFAULT_CARD_FEED_ERROR_STATE
+        const allFeedsState: CardFeedErrorState = {...DEFAULT_CARD_FEED_ERROR_STATE};
+        const companyCardFeedsState: CardFeedErrorState = {...DEFAULT_CARD_FEED_ERROR_STATE};
+        const expensifyCardFeedStates: CardFeedErrorState = {...DEFAULT_CARD_FEED_ERROR_STATE};
 
         const cardsWithBrokenFeedConnection: Record<string, Card> = {};
 

From d73baadc200bcaf3e870e02e3c6ce2ba57f75476 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 17:05:11 +0000
Subject: [PATCH 094/122] fix: remove unnecessary template strings

---
 .../unit/useAccountTabIndicatorStatusTest.ts  | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index e5eafe1caf10..54c2410d65ca 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -10,6 +10,7 @@ import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
 const userID = 'johndoe12@expensify.com';
+const POLICY_ONYX_KEY = `${ONYXKEYS.COLLECTION.POLICY}1` as const
 
 const brokenCardFeed = {
     feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
@@ -98,14 +99,14 @@ const getMockForStatus = ({status, name}: TestCase) =>
         [ONYXKEYS.SESSION]: {
             email: userID,
         },
-        [`${ONYXKEYS.CARD_LIST}`]: {
+        [ONYXKEYS.CARD_LIST]: {
             card1: {
                 bank: 'OTHER_BANK',
                 lastScrapeResult: name === cardFeedErrorTestCaseNames.admin || name === cardFeedErrorTestCaseNames.employee  ? 403 : 200,
                 fundID: String(brokenCardFeed.workspaceAccountID),
             },
         },
-        [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
+        [POLICY_ONYX_KEY]: {
             id: '1',
             name: 'Workspace 1',
             owner: name === cardFeedErrorTestCaseNames.admin ? userID : 'otheruser@expensify.com',
@@ -248,7 +249,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                    [ONYXKEYS.CARD_LIST]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -288,7 +289,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                    [ONYXKEYS.CARD_LIST]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -335,7 +336,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     },
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
+                    [ONYXKEYS.CARD_LIST]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -369,7 +370,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: null,
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: null,
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: null,
-                    [`${ONYXKEYS.CARD_LIST}`]: null,
+                    [ONYXKEYS.CARD_LIST]: null,
                     [ONYXKEYS.SESSION]: null,
                 } as unknown as OnyxMultiSetInput);
                 await waitForBatchedUpdatesWithAct();
@@ -394,7 +395,7 @@ describe('useAccountTabIndicatorStatus', () => {
         it('shows error for third party card with broken connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [`${ONYXKEYS.CARD_LIST}`]: {
+                    [ONYXKEYS.CARD_LIST]: {
                         card1: {
                             bank: brokenCardFeed.feedName,
                             fundID: String(brokenCardFeed.workspaceAccountID),
@@ -416,7 +417,7 @@ describe('useAccountTabIndicatorStatus', () => {
         it('does not show error for Expensify Card with broken connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [`${ONYXKEYS.CARD_LIST}`]: {
+                    [ONYXKEYS.CARD_LIST]: {
                         card1: {
                             bank: CONST.EXPENSIFY_CARD.BANK,
                             fundID: String(brokenCardFeed.workspaceAccountID),
@@ -437,7 +438,7 @@ describe('useAccountTabIndicatorStatus', () => {
         it('does not show error for third party card with good connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [`${ONYXKEYS.CARD_LIST}`]: {
+                    [ONYXKEYS.CARD_LIST]: {
                         card1: {
                             bank: 'Chase',
                             fundID: String(brokenCardFeed.workspaceAccountID),

From 4344585f4f23dd0eb4af08ba3144076bbeff2f85 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 17:05:30 +0000
Subject: [PATCH 095/122] fix: don't show account indicator for expensify card

---
 src/hooks/useNavigationTabBarIndicatorChecks.ts |  6 ++++--
 src/hooks/usePoliciesWithCardFeedErrors.ts      | 11 +----------
 2 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
index a221b919f894..1fe042ea800f 100644
--- a/src/hooks/useNavigationTabBarIndicatorChecks.ts
+++ b/src/hooks/useNavigationTabBarIndicatorChecks.ts
@@ -12,6 +12,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import type {Policy} from '@src/types/onyx';
 import useOnyx from './useOnyx';
 import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
+import useCardFeedErrors from './useCardFeedErrors';
 
 type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
 
@@ -44,7 +45,8 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
     // those should be cleaned out before doing any error checking
     const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
-    const {policiesWithCardFeedErrors, isPolicyAdmin, hasCardFeedErrors} = usePoliciesWithCardFeedErrors();
+    const {companyCards: {shouldShowRBR: hasCompanyCardFeedErrors}} = useCardFeedErrors();
+    const {policiesWithCardFeedErrors, isPolicyAdmin} = usePoliciesWithCardFeedErrors();
 
     const policyChecks: Partial<Record<IndicatorStatus, Policy | undefined>> = {
         [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: cleanPolicies.find(shouldShowPolicyError),
@@ -77,7 +79,7 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
         // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
         [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
         [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin && hasCardFeedErrors,
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin ? hasCompanyCardFeedErrors : false,
     };
 
     const infoChecks: Partial<Record<IndicatorStatus, boolean>> = {
diff --git a/src/hooks/usePoliciesWithCardFeedErrors.ts b/src/hooks/usePoliciesWithCardFeedErrors.ts
index f83402bbf153..abe424e94f21 100644
--- a/src/hooks/usePoliciesWithCardFeedErrors.ts
+++ b/src/hooks/usePoliciesWithCardFeedErrors.ts
@@ -4,13 +4,7 @@ import type {Policy} from '@src/types/onyx';
 import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
 
-type PoliciesWithCardFeedErrors = {
-    policiesWithCardFeedErrors: Policy[];
-    hasCardFeedErrors: boolean;
-    isPolicyAdmin: boolean;
-};
-
-function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
+function usePoliciesWithCardFeedErrors() {
     const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
     const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
 
@@ -19,13 +13,11 @@ function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
     const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
     const {shouldShowRbrForWorkspaceAccountID} = useCardFeedErrors();
-    let hasCardFeedErrors = false;
     const policiesWithCardFeedErrors: Policy[] = [];
     for (const [workspaceAccountID, hasErrors] of Object.entries(shouldShowRbrForWorkspaceAccountID)) {
         if (!hasErrors) {
             continue;
         }
-        hasCardFeedErrors = true;
 
         const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
         if (!policyWithCardFeedErrors) {
@@ -37,7 +29,6 @@ function usePoliciesWithCardFeedErrors(): PoliciesWithCardFeedErrors {
 
     return {
         policiesWithCardFeedErrors,
-        hasCardFeedErrors,
         isPolicyAdmin: policiesWithCardFeedErrors.some((policy) => isPolicyAdmin(policy, session?.email)),
     };
 }

From 4838469b741dc939a39fcb0068dddd81da57cb36 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 17:57:00 +0000
Subject: [PATCH 096/122] fix: tests

---
 .../unit/useAccountTabIndicatorStatusTest.ts  | 52 +++++++-------
 tests/unit/useIndicatorStatusTest.tsx         | 70 +++++++++++++------
 .../useWorkspacesTabIndicatorStatusTest.ts    | 54 +++++++-------
 tests/utils/IndicatorTestUtils.ts             |  9 +++
 4 files changed, 107 insertions(+), 78 deletions(-)
 create mode 100644 tests/utils/IndicatorTestUtils.ts

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 54c2410d65ca..c07ce918ed8d 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -7,22 +7,32 @@ import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
+import type { IndicatorTestCase } from 'tests/utils/IndicatorTestUtils';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
 const userID = 'johndoe12@expensify.com';
-const POLICY_ONYX_KEY = `${ONYXKEYS.COLLECTION.POLICY}1` as const
 
 const brokenCardFeed = {
     feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
     workspaceAccountID: 12345,
-};
+  };
 
-const cardFeedErrorTestCaseNames = {
-    employee: 'has card feed error if employee (non-admin)',
-    admin: 'has no card feed error if admin',
-} as const
 
-const getMockForStatus = ({status, name}: TestCase) =>
+const accountCardFeedTestCases= {
+    admin:{
+      name: 'has no account card feed error if admin',
+      indicatorColor: defaultTheme.success,
+      status: undefined,
+    },
+    employee:{
+    name: 'has account card feed error if employee (non-admin)',
+    indicatorColor: defaultTheme.danger,
+    status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+  },
+  } as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
+
+
+const getMockForStatus = ({status, name}: IndicatorTestCase) =>
     ({
         [ONYXKEYS.BANK_ACCOUNT_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -102,26 +112,20 @@ const getMockForStatus = ({status, name}: TestCase) =>
         [ONYXKEYS.CARD_LIST]: {
             card1: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: name === cardFeedErrorTestCaseNames.admin || name === cardFeedErrorTestCaseNames.employee  ? 403 : 200,
+                lastScrapeResult: name === accountCardFeedTestCases.admin.name || name === accountCardFeedTestCases.employee.name  ? 403 : 200,
                 fundID: String(brokenCardFeed.workspaceAccountID),
             },
         },
-        [POLICY_ONYX_KEY]: {
+        [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
             id: '1',
             name: 'Workspace 1',
-            owner: name === cardFeedErrorTestCaseNames.admin ? userID : 'otheruser@expensify.com',
-            role: name === cardFeedErrorTestCaseNames.admin ? 'admin' : 'user',
+            owner: name === accountCardFeedTestCases.admin.name ? userID : 'otheruser@expensify.com',
+            role: name === accountCardFeedTestCases.admin.name ? 'admin' : 'user',
             workspaceAccountID: brokenCardFeed.workspaceAccountID,
         },
     }) as unknown as OnyxMultiSetInput;
 
-type TestCase = {
-    name: string;
-    indicatorColor: string;
-    status: string | undefined;
-};
-
-const TEST_CASES: TestCase[] = [
+const TEST_CASES: IndicatorTestCase[] = [
     {
         name: 'has user wallet errors',
         indicatorColor: defaultTheme.danger,
@@ -157,16 +161,8 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO,
     },
-    {
-        name: cardFeedErrorTestCaseNames.employee,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
-    },
-    {
-        name: cardFeedErrorTestCaseNames.admin,
-        indicatorColor: defaultTheme.success,
-        status: undefined,
-    },
+    accountCardFeedTestCases.employee,
+    accountCardFeedTestCases.admin,
 ];
 
 describe('useAccountTabIndicatorStatus', () => {
diff --git a/tests/unit/useIndicatorStatusTest.tsx b/tests/unit/useIndicatorStatusTest.tsx
index e25242628d86..671c85054a8a 100644
--- a/tests/unit/useIndicatorStatusTest.tsx
+++ b/tests/unit/useIndicatorStatusTest.tsx
@@ -2,20 +2,45 @@ import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
-import type {IndicatorStatus} from '@hooks/useNavigationTabBarIndicatorChecks';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import initWithOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
+import type { IndicatorTestCase } from '../utils/IndicatorTestUtils';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 
-const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
+const userID = 'johndoe12@expensify.com';
+const otherUserID = 'otheruser@expensify.com';
+
+const brokenCardFeed = {
+    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+    workspaceAccountID: 12345,
+  };
+
+  const cardFeedErrorTestCases= {
+    admin:{
+      name: 'has policy card feed error if admin',
+      indicatorColor: defaultTheme.danger,
+      status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+      policyIDWithErrors: '1',
+    },
+    employee:{
+      name: 'has account card feed error if employee (non-admin)',
+      indicatorColor: defaultTheme.danger,
+      status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    },
+  } as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
+
+
+const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =>
     ({
         [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
             id: '1',
             name: 'Workspace 1',
-            owner: 'johndoe12@expensify.com',
+            owner: isAdmin ? userID : otherUserID,
             role: isAdmin ? 'admin' : 'user',
+            workspaceAccountID: brokenCardFeed.workspaceAccountID,
             customUnits:
                 status === CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR
                     ? {
@@ -28,7 +53,7 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}2` as const]: {
             id: '2',
             name: 'Workspace 2',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'user',
             errors:
                 status === CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS
@@ -40,12 +65,12 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}3` as const]: {
             id: '3',
             name: 'Workspace 3',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'user',
             employeeList: {
                 // eslint-disable-next-line @typescript-eslint/naming-convention
-                'johndoe12@expensify.com': {
-                    email: 'johndoe12@expensify.com',
+                [userID]: {
+                    email: userID,
                     errors:
                         status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR
                             ? {
@@ -58,7 +83,7 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}4` as const]: {
             id: '4',
             name: 'Workspace 4',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'auditor',
             connections:
                 status === CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS
@@ -103,9 +128,9 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         },
         [ONYXKEYS.LOGIN_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
-            'johndoe12@expensify.com': {
+            [userID]: {
                 partnerName: 'John Doe',
-                partnerUserID: 'johndoe12@expensify.com',
+                partnerUserID: userID,
                 validatedDate: status !== CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO ? new Date().toISOString() : undefined,
                 errorFields:
                     status === CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR
@@ -136,8 +161,14 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
                 cardID: 123456,
                 bank: CONST.EXPENSIFY_CARD.BANK,
                 accountID: 123,
+                fundID: String(brokenCardFeed.workspaceAccountID),
                 state: status === CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO ? CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED : CONST.EXPENSIFY_CARD.STATE.OPEN,
             },
+            card1: {
+                bank: 'OTHER_BANK',
+                lastScrapeResult: (name === cardFeedErrorTestCases.admin.name || name === cardFeedErrorTestCases.employee.name)  ? 403 : 200,
+                fundID: String(brokenCardFeed.workspaceAccountID),
+            },
         },
         [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {
             legalFirstName: 'John',
@@ -152,14 +183,7 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         },
     }) as OnyxMultiSetInput;
 
-type TestCase = {
-    name: string;
-    indicatorColor: string;
-    status: IndicatorStatus;
-    policyIDWithErrors?: string;
-};
-
-const TEST_CASES: TestCase[] = [
+const TEST_CASES: IndicatorTestCase[] = [
     {
         name: 'has policy errors',
         indicatorColor: defaultTheme.danger,
@@ -238,9 +262,10 @@ const TEST_CASES: TestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO,
         policyIDWithErrors: undefined,
     },
+cardFeedErrorTestCases.admin,
 ];
 
-const TEST_CASES_NON_ADMIN: TestCase[] = [
+const TEST_CASES_NON_ADMIN: IndicatorTestCase[] = [
     {
         name: 'has custom units error but not an admin so no RBR',
         indicatorColor: defaultTheme.success,
@@ -261,6 +286,7 @@ const TEST_CASES_NON_ADMIN: TestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS,
     },
+    cardFeedErrorTestCases.employee,
 ];
 
 describe('useIndicatorStatusTest', () => {
@@ -268,10 +294,12 @@ describe('useIndicatorStatusTest', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
+        initWithOnyxDerivedValues();
     });
+
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase.status));
+            await Onyx.multiSet(getMockForStatus(testCase, true));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {
@@ -301,7 +329,7 @@ describe('useIndicatorStatusTest', () => {
     });
     describe.each(TEST_CASES_NON_ADMIN)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase.status, false));
+            await Onyx.multiSet(getMockForStatus(testCase, false));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {
diff --git a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
index 9c00eac56e04..25a537f96589 100644
--- a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
+++ b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
@@ -7,8 +7,22 @@ import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
 import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
+import type { IndicatorTestCase } from 'tests/utils/IndicatorTestUtils';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
+const workspaceCardFeedTestCases= {
+    admin:{
+      name: 'has workspace card feed error if admin',
+      indicatorColor: defaultTheme.danger,
+      status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+  },
+  employee:{
+      name: 'has no workspace card feed error if employee (non-admin)',
+      indicatorColor: defaultTheme.success,
+      status: undefined,
+  }} as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
+
+
 const userID = 'admin@expensify.com';
 const otherUserID = 'employee@example.com';
 
@@ -23,16 +37,6 @@ const CARD_FEED = {
     workspaceAccountID: WORKSPACE.workspaceAccountID,
 };
 
-type TestCase = {
-    name: string;
-    indicatorColor: string;
-    status: string | undefined;
-};
-
-const cardFeedErrorTestCaseNames = {
-    admin: 'has card feed error if admin',
-    employee: 'has no card feed error if employee (non-admin)',
-} as const;
 
 const TEST_CASE_NAMES = {
     hasPolicyErrors: 'has policy errors',
@@ -41,21 +45,21 @@ const TEST_CASE_NAMES = {
     hasSyncErrors: 'has sync errors',
     hasQBOExportError: 'has QBO export error',
     hasUberCredentialsError: 'has Uber credentials error',
-    hasAdminCardFeedErrors: cardFeedErrorTestCaseNames.admin,
-    hasNoCardFeedErrorsForEmployee: cardFeedErrorTestCaseNames.employee,
+    hasAdminCardFeedErrors: workspaceCardFeedTestCases.admin.name,
+    hasNoCardFeedErrorsForEmployee: workspaceCardFeedTestCases.employee.name,
     noErrors: 'no errors',
 } as const;
 
-const getMockForStatus = ({name}: TestCase) =>
+const getMockForStatus = ({name}: IndicatorTestCase) =>
     ({
         [ONYXKEYS.SESSION]: {
-            email: name === cardFeedErrorTestCaseNames.employee ? otherUserID : userID,
+            email: name === workspaceCardFeedTestCases.employee.name ? otherUserID : userID,
         },
         [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
             id: WORKSPACE.policyID,
             name: WORKSPACE.policyName,
-            owner: name === cardFeedErrorTestCaseNames.employee ? userID : userID,
-            role: name === cardFeedErrorTestCaseNames.employee ? 'user' : 'admin',
+            owner: name === workspaceCardFeedTestCases.employee.name ? userID : userID,
+            role: name === workspaceCardFeedTestCases.employee.name ? 'user' : 'admin',
             workspaceAccountID: WORKSPACE.workspaceAccountID,
             // Policy errors
             errors: name === TEST_CASE_NAMES.hasPolicyErrors ? {policyError: 'Something went wrong'} : undefined,
@@ -106,7 +110,7 @@ const getMockForStatus = ({name}: TestCase) =>
         // Card feed errors - both admin and employee test cases have broken card connection
         // Admin sees HAS_POLICY_ADMIN_CARD_FEED_ERRORS, employee does NOT see it in workspaces tab
         [`${ONYXKEYS.CARD_LIST}`]:
-            name === cardFeedErrorTestCaseNames.admin || name === cardFeedErrorTestCaseNames.employee
+            name === workspaceCardFeedTestCases.admin.name || name === workspaceCardFeedTestCases.employee.name
                 ? {
                       card1: {
                           cardID: 1,
@@ -126,7 +130,7 @@ const getMockForStatus = ({name}: TestCase) =>
                 : undefined,
     }) as unknown as OnyxMultiSetInput;
 
-const TEST_CASES: TestCase[] = [
+const TEST_CASES: IndicatorTestCase[] = [
     {
         name: TEST_CASE_NAMES.hasPolicyErrors,
         indicatorColor: defaultTheme.danger,
@@ -157,16 +161,8 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.danger,
         status: CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR,
     },
-    {
-        name: cardFeedErrorTestCaseNames.admin,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
-    },
-    {
-        name: cardFeedErrorTestCaseNames.employee,
-        indicatorColor: defaultTheme.success,
-        status: undefined,
-    },
+    workspaceCardFeedTestCases.admin,
+    workspaceCardFeedTestCases.employee,
 ];
 
 describe('useWorkspacesTabIndicatorStatus', () => {
@@ -204,7 +200,7 @@ describe('useWorkspacesTabIndicatorStatus', () => {
             await waitForBatchedUpdatesWithAct();
             const {policyIDWithErrors} = result.current;
             // Employee test case has no policy errors visible (card feed errors don't show for employees in workspaces tab)
-            if (testCase.name === cardFeedErrorTestCaseNames.employee) {
+            if (testCase.name === workspaceCardFeedTestCases.employee.name) {
                 expect(policyIDWithErrors).toBeUndefined();
             } else {
                 expect(policyIDWithErrors).toBe(WORKSPACE.policyID);
diff --git a/tests/utils/IndicatorTestUtils.ts b/tests/utils/IndicatorTestUtils.ts
new file mode 100644
index 000000000000..ba5c8be0bfbf
--- /dev/null
+++ b/tests/utils/IndicatorTestUtils.ts
@@ -0,0 +1,9 @@
+type IndicatorTestCase = {
+  name: string;
+  indicatorColor: string;
+  status: string | undefined;
+  policyIDWithErrors?: string;
+};
+
+// eslint-disable-next-line import/prefer-default-export
+export type { IndicatorTestCase };

From 223cc2dda926469b882e179a372b260785e2d183 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 18:23:48 +0000
Subject: [PATCH 097/122] fix: TS

---
 src/libs/CardUtils.ts                                  | 4 ++--
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index b0ad1ce9b81c..41f0e39e8ec6 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -23,7 +23,7 @@ import type {
     WorkspaceCardsList,
 } from '@src/types/onyx';
 import type {FilteredCardList} from '@src/types/onyx/Card';
-import type {CardFeedData, CardFeedWithDomainID, CardFeedWithNumber, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CardFeedData, CardFeedWithDomainID, 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
@@ -557,7 +557,7 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry<CompanyCardFeedWithDomainID
     return isValidLastFeed ? lastSelectedFeed : defaultFeed;
 }
 
-function getCompanyCardFeedWithDomainID(feedName: CardFeedWithNumber, domainID: number | string): CardFeedWithDomainID {
+function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithNumber, domainID: number | string): CompanyCardFeedWithDomainID {
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index cc7695ca585a..d52d68a75696 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -3,7 +3,7 @@ import {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBro
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
-import type {CardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CardFeedWithNumber, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import CONST from '@src/CONST';
@@ -59,7 +59,7 @@ export default createOnyxDerivedValueConfig({
                 return;
             }
 
-            const currentFeedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName, workspaceAccountID);
+            const currentFeedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
             const currentFeed = combinedCompanyCardFeeds?.[currentFeedNameWithDomainID];
             const currentFeedErrors = currentFeed?.errors
 

From f29a4834665ed550d150907179774ad4244418a1 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 18:32:12 +0000
Subject: [PATCH 098/122] fix: prettier

---
 src/hooks/useAssignCard.ts                    | 12 +---
 .../useNavigationTabBarIndicatorChecks.ts     |  6 +-
 src/hooks/useUpdateFeedBrokenConnection.ts    |  7 +-
 src/libs/PolicyUtils.ts                       |  3 +-
 .../OnyxDerived/configs/cardFeedErrors.ts     |  8 +--
 src/pages/settings/InitialSettingsPage.tsx    |  4 +-
 ...orkspaceCompanyCardsTableHeaderButtons.tsx |  2 +-
 src/types/onyx/DerivedValues.ts               |  5 +-
 tests/unit/CardFeedErrorsDerivedValueTest.ts  | 35 ++++++----
 .../unit/useAccountTabIndicatorStatusTest.ts  | 32 +++++----
 tests/unit/useIndicatorStatusTest.tsx         | 33 +++++-----
 .../useWorkspacesTabIndicatorStatusTest.ts    | 65 ++++++++++---------
 tests/utils/IndicatorTestUtils.ts             | 10 +--
 13 files changed, 113 insertions(+), 109 deletions(-)

diff --git a/src/hooks/useAssignCard.ts b/src/hooks/useAssignCard.ts
index 863b619e7277..c264bc8c5cbf 100644
--- a/src/hooks/useAssignCard.ts
+++ b/src/hooks/useAssignCard.ts
@@ -1,15 +1,7 @@
 import {useContext, useRef} from 'react';
 import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
 import {importPlaidAccounts} from '@libs/actions/Plaid';
-import {
-    getCompanyCardFeed,
-    getCompanyFeeds,
-    getDomainOrWorkspaceAccountID,
-    getPlaidCountry,
-    getPlaidInstitutionId,
-    isCustomFeed,
-    isSelectedFeedExpired,
-} from '@libs/CardUtils';
+import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID, getPlaidCountry, getPlaidInstitutionId, isCustomFeed, isSelectedFeedExpired} from '@libs/CardUtils';
 import Navigation from '@libs/Navigation/Navigation';
 import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
 import {getDomainNameForPolicy, isDeletedPolicyEmployee} from '@libs/PolicyUtils';
@@ -20,13 +12,13 @@ import ROUTES from '@src/ROUTES';
 import type {CompanyCardFeedWithDomainID, CurrencyList} from '@src/types/onyx';
 import type {AssignCardData, AssignCardStep} from '@src/types/onyx/AssignCard';
 import {getEmptyObject} from '@src/types/utils/EmptyObject';
+import useCardFeedErrors from './useCardFeedErrors';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
 import useIsAllowedToIssueCompanyCard from './useIsAllowedToIssueCompanyCard';
 import useNetwork from './useNetwork';
 import useOnyx from './useOnyx';
 import usePolicy from './usePolicy';
-import useCardFeedErrors from './useCardFeedErrors';
 
 type UseAssignCardProps = {
     /** The currently selected card feed */
diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
index 1fe042ea800f..c75ed15b927a 100644
--- a/src/hooks/useNavigationTabBarIndicatorChecks.ts
+++ b/src/hooks/useNavigationTabBarIndicatorChecks.ts
@@ -10,9 +10,9 @@ import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Policy} from '@src/types/onyx';
+import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
 import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
-import useCardFeedErrors from './useCardFeedErrors';
 
 type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
 
@@ -45,7 +45,9 @@ function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
     // those should be cleaned out before doing any error checking
     const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
-    const {companyCards: {shouldShowRBR: hasCompanyCardFeedErrors}} = useCardFeedErrors();
+    const {
+        companyCards: {shouldShowRBR: hasCompanyCardFeedErrors},
+    } = useCardFeedErrors();
     const {policiesWithCardFeedErrors, isPolicyAdmin} = usePoliciesWithCardFeedErrors();
 
     const policyChecks: Partial<Record<IndicatorStatus, Policy | undefined>> = {
diff --git a/src/hooks/useUpdateFeedBrokenConnection.ts b/src/hooks/useUpdateFeedBrokenConnection.ts
index 494d2e9f6f95..f56c4ce2ef1c 100644
--- a/src/hooks/useUpdateFeedBrokenConnection.ts
+++ b/src/hooks/useUpdateFeedBrokenConnection.ts
@@ -3,9 +3,9 @@ import {getCompanyCardFeed, getCompanyFeeds, getDomainOrWorkspaceAccountID} from
 import {updateWorkspaceCompanyCard} from '@userActions/CompanyCards';
 import CONST from '@src/CONST';
 import type {CompanyCardFeedWithDomainID} from '@src/types/onyx';
+import useCardFeedErrors from './useCardFeedErrors';
 import useCardFeeds from './useCardFeeds';
 import usePolicy from './usePolicy';
-import useCardFeedErrors from './useCardFeedErrors';
 
 export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyID?: string; feed?: CompanyCardFeedWithDomainID}) {
     const policy = usePolicy(policyID);
@@ -13,7 +13,10 @@ export default function useUpdateFeedBrokenConnection({policyID, feed}: {policyI
     const companyFeeds = getCompanyFeeds(cardFeeds);
     const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
     const domainOrWorkspaceAccountID = feed ? getDomainOrWorkspaceAccountID(workspaceAccountID, companyFeeds[feed]) : CONST.DEFAULT_NUMBER_ID;
-    const {all: {isFeedConnectionBroken}, cardsWithBrokenFeedConnection} = useCardFeedErrors();
+    const {
+        all: {isFeedConnectionBroken},
+        cardsWithBrokenFeedConnection,
+    } = useCardFeedErrors();
 
     const [brokenCardId, firstCardWithBrokenFeedConnection] = Object.entries(cardsWithBrokenFeedConnection).at(0) ?? [];
 
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index ec87953bcd93..0411bad2ccf1 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -110,7 +110,8 @@ function getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(policies:
 /**
  * Checks if the current user is an admin of the policy.
  */
-const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string, shouldCheckGlobalPolicyRole = true): boolean => getPolicyRole(policy, login, shouldCheckGlobalPolicyRole) === CONST.POLICY.ROLE.ADMIN;
+const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string, shouldCheckGlobalPolicyRole = true): boolean =>
+    getPolicyRole(policy, login, shouldCheckGlobalPolicyRole) === CONST.POLICY.ROLE.ADMIN;
 
 /**
  * Checks if we have any errors stored within the policy?.employeeList. Determines whether we should show a red brick road error or not.
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index d52d68a75696..0198c91fadc5 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,12 +1,12 @@
 import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
 import {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
 import type {CardFeedWithNumber, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import CONST from '@src/CONST';
 
 const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
     shouldShowRBR: false,
@@ -28,8 +28,6 @@ function getShouldShowRBR(state: Partial<CardFeedErrorState>): boolean {
     }
 
     return !!state.isFeedConnectionBroken;
-
-
 }
 
 export default createOnyxDerivedValueConfig({
@@ -61,7 +59,7 @@ export default createOnyxDerivedValueConfig({
 
             const currentFeedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
             const currentFeed = combinedCompanyCardFeeds?.[currentFeedNameWithDomainID];
-            const currentFeedErrors = currentFeed?.errors
+            const currentFeedErrors = currentFeed?.errors;
 
             const hasFailedCardAssignments = !isEmptyObject(
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${currentFeedNameWithDomainID}`],
@@ -88,7 +86,7 @@ export default createOnyxDerivedValueConfig({
                 hasFailedCardAssignments,
             };
 
-            const shouldShowRbrForCurrentFeed = getShouldShowRBR(currentFeedState)
+            const shouldShowRbrForCurrentFeed = getShouldShowRBR(currentFeedState);
 
             cardFeedErrors[currentFeedNameWithDomainID] = {
                 ...currentFeedState,
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index e88736174272..6db603ec06f1 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -145,7 +145,9 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
 
     const shouldDisplayLHB = !shouldUseNarrowLayout;
 
-    const {all: {shouldShowRBR}} = useCardFeedErrors();
+    const {
+        all: {shouldShowRBR},
+    } = useCardFeedErrors();
 
     const hasPendingCardAction = hasPendingExpensifyCardAction(allCards, privatePersonalDetails);
     const walletBrickRoadIndicator = useMemo(() => {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index 82574040c11c..c7db188d7e62 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -60,7 +60,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
 
     const workspaceAccountID = useWorkspaceAccountID(policyID);
     const feed = getCompanyCardFeed(feedName);
-const feedWithDomainID = getCompanyCardFeedWithDomainID(feed, workspaceAccountID);
+    const feedWithDomainID = getCompanyCardFeedWithDomainID(feed, workspaceAccountID);
 
     const formattedFeedName = feedName ? getCustomOrFormattedFeedName(feed, cardFeeds?.[feedName]?.customFeedName) : undefined;
     const isCommercialFeed = isCustomFeed(feed);
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 7e9202ea7093..f75f9148e1a3 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -93,7 +93,7 @@ type CardErrors = {
 /**
  * The state of card feed errors.
  */
-type CardFeedErrorState ={
+type CardFeedErrorState = {
     /**
      * Whether to show the RBR for a specific feed within a workspace/domain.
      * This will be true, if any of the below conditions are true:
@@ -119,7 +119,7 @@ type CardFeedErrorState ={
      * Whether some feed connection is broken.
      */
     isFeedConnectionBroken: boolean;
-}
+};
 
 /**
  * The errors of a card feed.
@@ -150,7 +150,6 @@ type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
  */
 type CardFeedErrorsObject = Record<CardFeedWithDomainID, FeedErrors>;
 
-
 /**
  * The errors of card feeds.
  */
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index eb5cbf488fa0..e32036123e08 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -1,11 +1,11 @@
 import type {OnyxCollection} from 'react-native-onyx';
+import type {DerivedValueContext} from '@libs/actions/OnyxDerived/types';
+import {getCompanyCardFeedWithDomainID} from '@libs/CardUtils';
 import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
 import cardFeedErrorsConfig from '@src/libs/actions/OnyxDerived/configs/cardFeedErrors';
+import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, FailedCompanyCardAssignments, WorkspaceCardsList} from '@src/types/onyx';
-import type { DerivedValueContext } from '@libs/actions/OnyxDerived/types';
-import { getCompanyCardFeedWithDomainID } from '@libs/CardUtils';
-import type { CardFeed, CardFeedWithDomainID } from '@src/types/onyx/CardFeeds';
+import type {CardFeed, CardFeedWithDomainID} from '@src/types/onyx/CardFeeds';
 
 const DERIVED_VALUE_CONTEXT: DerivedValueContext<typeof cardFeedErrorsConfig.key, typeof cardFeedErrorsConfig.dependencies> = {
     currentValue: undefined,
@@ -28,11 +28,17 @@ const CARD_FEEDS = {
         workspaceAccountID: 66666666,
         feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT,
         feedNameWithDomainID: getCompanyCardFeedWithDomainID(CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT, 66666666),
-}} as const satisfies Partial<Record<CardFeed, {
-    workspaceAccountID: number;
-    feedName: CardFeed;
-    feedNameWithDomainID: CardFeedWithDomainID;
-}>>;
+    },
+} as const satisfies Partial<
+    Record<
+        CardFeed,
+        {
+            workspaceAccountID: number;
+            feedName: CardFeed;
+            feedNameWithDomainID: CardFeedWithDomainID;
+        }
+    >
+>;
 
 const CARD_IDS = {
     card1: 1,
@@ -41,7 +47,6 @@ const CARD_IDS = {
     card4: 4,
 };
 
-
 // Helper to create a card with specified properties
 function createCard(overrides: Partial<Card> = {}): Card {
     return {
@@ -261,7 +266,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, {}, failedAssignments, {}], DERIVED_VALUE_CONTEXT);
 
-                expect(result.all.hasFailedCardAssignments  ).toBe(true);
+                expect(result.all.hasFailedCardAssignments).toBe(true);
                 expect(result.all.shouldShowRBR).toBe(true);
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasFailedCardAssignments).toBe(true);
             });
@@ -391,7 +396,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT     );
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.cardErrors[CARD_IDS.card1]?.errorFields).toEqual({
                     cardName: {error: 'Invalid card name'},
@@ -519,7 +524,9 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const globalCardList: CardList = {[CARD_IDS.card1]: globalCard};
                 const allWorkspaceCards: OnyxCollection<WorkspaceCardsList> = {
-                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: createWorkspaceCardsList({[CARD_IDS.card2]: workspaceCard}),
+                    [`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${cardFeed.workspaceAccountID}_${cardFeed.feedNameWithDomainID}`]: createWorkspaceCardsList({
+                        [CARD_IDS.card2]: workspaceCard,
+                    }),
                 };
 
                 const result = cardFeedErrorsConfig.compute([globalCardList, allWorkspaceCards, {}, {}], DERIVED_VALUE_CONTEXT);
@@ -618,7 +625,7 @@ describe('CardFeedErrors Derived Value', () => {
 
                 const globalCardList: CardList = {[CARD_IDS.card1]: card};
 
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT );
+                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, {}], DERIVED_VALUE_CONTEXT);
 
                 expect(result.all.shouldShowRBR).toBe(false);
             });
diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index c07ce918ed8d..831559db0d14 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -1,13 +1,13 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
+import type {IndicatorTestCase} from 'tests/utils/IndicatorTestUtils';
 import useAccountTabIndicatorStatus from '@hooks/useAccountTabIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
 import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
-import type { IndicatorTestCase } from 'tests/utils/IndicatorTestUtils';
+import ONYXKEYS from '@src/ONYXKEYS';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
 const userID = 'johndoe12@expensify.com';
@@ -15,22 +15,20 @@ const userID = 'johndoe12@expensify.com';
 const brokenCardFeed = {
     feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
     workspaceAccountID: 12345,
-  };
+};
 
-
-const accountCardFeedTestCases= {
-    admin:{
-      name: 'has no account card feed error if admin',
-      indicatorColor: defaultTheme.success,
-      status: undefined,
+const accountCardFeedTestCases = {
+    admin: {
+        name: 'has no account card feed error if admin',
+        indicatorColor: defaultTheme.success,
+        status: undefined,
     },
-    employee:{
-    name: 'has account card feed error if employee (non-admin)',
-    indicatorColor: defaultTheme.danger,
-    status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
-  },
-  } as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
-
+    employee: {
+        name: 'has account card feed error if employee (non-admin)',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    },
+} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
 
 const getMockForStatus = ({status, name}: IndicatorTestCase) =>
     ({
@@ -112,7 +110,7 @@ const getMockForStatus = ({status, name}: IndicatorTestCase) =>
         [ONYXKEYS.CARD_LIST]: {
             card1: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: name === accountCardFeedTestCases.admin.name || name === accountCardFeedTestCases.employee.name  ? 403 : 200,
+                lastScrapeResult: name === accountCardFeedTestCases.admin.name || name === accountCardFeedTestCases.employee.name ? 403 : 200,
                 fundID: String(brokenCardFeed.workspaceAccountID),
             },
         },
diff --git a/tests/unit/useIndicatorStatusTest.tsx b/tests/unit/useIndicatorStatusTest.tsx
index 671c85054a8a..56ec28c69a1c 100644
--- a/tests/unit/useIndicatorStatusTest.tsx
+++ b/tests/unit/useIndicatorStatusTest.tsx
@@ -5,9 +5,9 @@ import useIndicatorStatus from '@hooks/useIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
 import initWithOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
-import type { IndicatorTestCase } from '../utils/IndicatorTestUtils';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {IndicatorTestCase} from '../utils/IndicatorTestUtils';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 
 const userID = 'johndoe12@expensify.com';
@@ -16,22 +16,21 @@ const otherUserID = 'otheruser@expensify.com';
 const brokenCardFeed = {
     feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
     workspaceAccountID: 12345,
-  };
+};
 
-  const cardFeedErrorTestCases= {
-    admin:{
-      name: 'has policy card feed error if admin',
-      indicatorColor: defaultTheme.danger,
-      status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
-      policyIDWithErrors: '1',
+const cardFeedErrorTestCases = {
+    admin: {
+        name: 'has policy card feed error if admin',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+        policyIDWithErrors: '1',
     },
-    employee:{
-      name: 'has account card feed error if employee (non-admin)',
-      indicatorColor: defaultTheme.danger,
-      status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    employee: {
+        name: 'has account card feed error if employee (non-admin)',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
     },
-  } as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
-
+} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
 
 const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =>
     ({
@@ -166,7 +165,7 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
             },
             card1: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: (name === cardFeedErrorTestCases.admin.name || name === cardFeedErrorTestCases.employee.name)  ? 403 : 200,
+                lastScrapeResult: name === cardFeedErrorTestCases.admin.name || name === cardFeedErrorTestCases.employee.name ? 403 : 200,
                 fundID: String(brokenCardFeed.workspaceAccountID),
             },
         },
@@ -262,7 +261,7 @@ const TEST_CASES: IndicatorTestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO,
         policyIDWithErrors: undefined,
     },
-cardFeedErrorTestCases.admin,
+    cardFeedErrorTestCases.admin,
 ];
 
 const TEST_CASES_NON_ADMIN: IndicatorTestCase[] = [
diff --git a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
index 25a537f96589..402950014fc6 100644
--- a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
+++ b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
@@ -1,27 +1,27 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
+import type {IndicatorTestCase} from 'tests/utils/IndicatorTestUtils';
 import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
 import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type { IndicatorTestCase } from 'tests/utils/IndicatorTestUtils';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
-const workspaceCardFeedTestCases= {
-    admin:{
-      name: 'has workspace card feed error if admin',
-      indicatorColor: defaultTheme.danger,
-      status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
-  },
-  employee:{
-      name: 'has no workspace card feed error if employee (non-admin)',
-      indicatorColor: defaultTheme.success,
-      status: undefined,
-  }} as const satisfies  Record<'admin' | 'employee', IndicatorTestCase>
-
+const workspaceCardFeedTestCases = {
+    admin: {
+        name: 'has workspace card feed error if admin',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+    },
+    employee: {
+        name: 'has no workspace card feed error if employee (non-admin)',
+        indicatorColor: defaultTheme.success,
+        status: undefined,
+    },
+} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
 
 const userID = 'admin@expensify.com';
 const otherUserID = 'employee@example.com';
@@ -37,7 +37,6 @@ const CARD_FEED = {
     workspaceAccountID: WORKSPACE.workspaceAccountID,
 };
 
-
 const TEST_CASE_NAMES = {
     hasPolicyErrors: 'has policy errors',
     hasCustomUnitsError: 'has custom units error',
@@ -79,23 +78,27 @@ const getMockForStatus = ({name}: IndicatorTestCase) =>
             // Sync errors - use connections with sync errors
             // hasSynchronizationErrorMessage requires: !isSyncInProgress && lastSync exists && isSuccessful === false && errorDate exists
             connections: {
-                ...(name === TEST_CASE_NAMES.hasSyncErrors ? {
-                    quickbooksOnline: {
-                        lastSync: {
-                            errorMessage: 'Sync failed',
-                            isSuccessful: false,
-                            errorDate: new Date().toISOString(),
-                        },
-                    },
-                } : {}),
-                ...(name === TEST_CASE_NAMES.hasQBOExportError ? {
-                    quickbooksOnline: {
-                        config: {
-                            reimbursableExpensesExportDestination: 'VENDOR_BILL',
-                            reimbursableExpensesAccount: undefined,
-                        },
-                    },
-                } : {}),
+                ...(name === TEST_CASE_NAMES.hasSyncErrors
+                    ? {
+                          quickbooksOnline: {
+                              lastSync: {
+                                  errorMessage: 'Sync failed',
+                                  isSuccessful: false,
+                                  errorDate: new Date().toISOString(),
+                              },
+                          },
+                      }
+                    : {}),
+                ...(name === TEST_CASE_NAMES.hasQBOExportError
+                    ? {
+                          quickbooksOnline: {
+                              config: {
+                                  reimbursableExpensesExportDestination: 'VENDOR_BILL',
+                                  reimbursableExpensesAccount: undefined,
+                              },
+                          },
+                      }
+                    : {}),
             },
             // Uber credentials error
             receiptPartners:
diff --git a/tests/utils/IndicatorTestUtils.ts b/tests/utils/IndicatorTestUtils.ts
index ba5c8be0bfbf..45f071dd796d 100644
--- a/tests/utils/IndicatorTestUtils.ts
+++ b/tests/utils/IndicatorTestUtils.ts
@@ -1,9 +1,9 @@
 type IndicatorTestCase = {
-  name: string;
-  indicatorColor: string;
-  status: string | undefined;
-  policyIDWithErrors?: string;
+    name: string;
+    indicatorColor: string;
+    status: string | undefined;
+    policyIDWithErrors?: string;
 };
 
 // eslint-disable-next-line import/prefer-default-export
-export type { IndicatorTestCase };
+export type {IndicatorTestCase};

From a66f05a0395bfa51ac2e4d6b51eb8328d0744ed7 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 19:46:46 +0000
Subject: [PATCH 099/122] refactor: update previous card feed error state

---
 .../OnyxDerived/configs/cardFeedErrors.ts     | 47 ++++++++++---------
 1 file changed, 26 insertions(+), 21 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 0198c91fadc5..064cacea9442 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -57,15 +57,15 @@ export default createOnyxDerivedValueConfig({
                 return;
             }
 
-            const currentFeedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
-            const currentFeed = combinedCompanyCardFeeds?.[currentFeedNameWithDomainID];
-            const currentFeedErrors = currentFeed?.errors;
+            const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
+            const feed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
+            const feedErrors = feed?.errors;
 
             const hasFailedCardAssignments = !isEmptyObject(
-                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${currentFeedNameWithDomainID}`],
+                failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
             );
 
-            let cardErrors: Record<string, CardErrors> = cardFeedErrors[currentFeedNameWithDomainID]?.cardErrors ?? {};
+            let cardErrors: Record<string, CardErrors> = cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {};
             const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
 
             if (hasCardErrors) {
@@ -79,23 +79,28 @@ export default createOnyxDerivedValueConfig({
                 };
             }
 
-            const currentFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
-                isFeedConnectionBroken: isCardConnectionBroken(card),
-                hasFeedErrors: currentFeedNameWithDomainID ? !!currentFeedErrors : false,
-                hasWorkspaceErrors: !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors,
-                hasFailedCardAssignments,
+            const isFeedConnectionBroken = isCardConnectionBroken(card)
+            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false
+            const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
+
+            const previousFeedState = cardFeedErrors[feedNameWithDomainID] ?? DEFAULT_CARD_FEED_ERROR_STATE;
+            const newFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
+                isFeedConnectionBroken: isFeedConnectionBroken || previousFeedState.isFeedConnectionBroken,
+                hasFeedErrors: hasFeedErrors || previousFeedState.hasFeedErrors,
+                hasWorkspaceErrors: hasWorkspaceErrors || previousFeedState.hasWorkspaceErrors,
+                hasFailedCardAssignments: hasFailedCardAssignments || previousFeedState.hasFailedCardAssignments,
             };
 
-            const shouldShowRbrForCurrentFeed = getShouldShowRBR(currentFeedState);
+            const shouldShowRBR = getShouldShowRBR(newFeedState);
 
-            cardFeedErrors[currentFeedNameWithDomainID] = {
-                ...currentFeedState,
-                shouldShowRBR: shouldShowRbrForCurrentFeed,
+            cardFeedErrors[feedNameWithDomainID] = {
+                ...newFeedState,
+                shouldShowRBR,
                 cardErrors,
-                feedErrors: currentFeedErrors,
+                feedErrors,
             };
 
-            if (currentFeedState.isFeedConnectionBroken) {
+            if (newFeedState.isFeedConnectionBroken) {
                 allFeedsState.isFeedConnectionBroken = true;
                 cardsWithBrokenFeedConnection[card.cardID] = card;
 
@@ -106,7 +111,7 @@ export default createOnyxDerivedValueConfig({
                 }
             }
 
-            if (currentFeedState.hasFeedErrors) {
+            if (newFeedState.hasFeedErrors) {
                 allFeedsState.hasFeedErrors = true;
 
                 if (isExpensifyCard) {
@@ -116,7 +121,7 @@ export default createOnyxDerivedValueConfig({
                 }
             }
 
-            if (currentFeedState.hasWorkspaceErrors) {
+            if (newFeedState.hasWorkspaceErrors) {
                 allFeedsState.hasWorkspaceErrors = true;
 
                 if (isExpensifyCard) {
@@ -126,7 +131,7 @@ export default createOnyxDerivedValueConfig({
                 }
             }
 
-            if (currentFeedState.hasFailedCardAssignments) {
+            if (newFeedState.hasFailedCardAssignments) {
                 allFeedsState.hasFailedCardAssignments = true;
 
                 if (isExpensifyCard) {
@@ -136,9 +141,9 @@ export default createOnyxDerivedValueConfig({
                 }
             }
 
-            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRbrForCurrentFeed;
+            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
 
-            shouldShowRbrForFeedNameWithDomainID[currentFeedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[currentFeedNameWithDomainID] || shouldShowRbrForCurrentFeed;
+            shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {

From e6bb3a7e663c7e8d340c9bbc4f55791cc1e57618 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 19:54:02 +0000
Subject: [PATCH 100/122] fix: simplify code and maintain existing card feed
 and card errors

---
 .../OnyxDerived/configs/cardFeedErrors.ts     | 90 ++++++++-----------
 1 file changed, 35 insertions(+), 55 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 064cacea9442..79a14e9bb08b 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -7,6 +7,7 @@ import type {Card} from '@src/types/onyx';
 import type {CardFeedWithNumber, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import type { Errors } from '@src/types/onyx/OnyxCommon';
 
 const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
     shouldShowRBR: false,
@@ -34,6 +35,9 @@ export default createOnyxDerivedValueConfig({
     key: ONYXKEYS.DERIVED.CARD_FEED_ERRORS,
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
+        const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
+        const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
+
         const cardFeedErrors: CardFeedErrorsObject = {};
         const shouldShowRbrForWorkspaceAccountID: Record<number, boolean> = {};
         const shouldShowRbrForFeedNameWithDomainID: Record<string, boolean> = {};
@@ -44,9 +48,6 @@ export default createOnyxDerivedValueConfig({
 
         const cardsWithBrokenFeedConnection: Record<string, Card> = {};
 
-        const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
-        const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
-
         function addErrorsForCard(card: Card) {
             const bankName = card.bank as CardFeedWithNumber;
             const workspaceAccountID = Number(card.fundID);
@@ -58,40 +59,43 @@ export default createOnyxDerivedValueConfig({
             }
 
             const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
+            const previousFeedErrors = cardFeedErrors[feedNameWithDomainID] ?? DEFAULT_CARD_FEED_ERROR_STATE;
+
             const feed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
-            const feedErrors = feed?.errors;
+            const feedErrors = {
+                ...previousFeedErrors.feedErrors,
+                ...feed?.errors,
+            } as Errors;
 
             const hasFailedCardAssignments = !isEmptyObject(
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
             );
 
-            let cardErrors: Record<string, CardErrors> = cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {};
             const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
-
-            if (hasCardErrors) {
-                cardErrors = {
-                    ...cardErrors,
+            const cardErrors = {
+                ...previousFeedErrors.cardErrors,
+                ...cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {},
+                ...(hasCardErrors ? {
                     [card.cardID]: {
                         errors: card.errors,
                         errorFields: card.errorFields,
                         pendingAction: card.pendingAction,
                     },
-                };
-            }
+                } : {}),
+            } as Record<string, CardErrors>;
 
             const isFeedConnectionBroken = isCardConnectionBroken(card)
             const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false
             const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
 
-            const previousFeedState = cardFeedErrors[feedNameWithDomainID] ?? DEFAULT_CARD_FEED_ERROR_STATE;
             const newFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
-                isFeedConnectionBroken: isFeedConnectionBroken || previousFeedState.isFeedConnectionBroken,
-                hasFeedErrors: hasFeedErrors || previousFeedState.hasFeedErrors,
-                hasWorkspaceErrors: hasWorkspaceErrors || previousFeedState.hasWorkspaceErrors,
-                hasFailedCardAssignments: hasFailedCardAssignments || previousFeedState.hasFailedCardAssignments,
+                isFeedConnectionBroken: isFeedConnectionBroken || previousFeedErrors.isFeedConnectionBroken,
+                hasFeedErrors: hasFeedErrors || previousFeedErrors.hasFeedErrors,
+                hasWorkspaceErrors: hasWorkspaceErrors || previousFeedErrors.hasWorkspaceErrors,
+                hasFailedCardAssignments: hasFailedCardAssignments || previousFeedErrors.hasFailedCardAssignments,
             };
 
-            const shouldShowRBR = getShouldShowRBR(newFeedState);
+            const shouldShowRBR = getShouldShowRBR(newFeedState) || previousFeedErrors.shouldShowRBR;
 
             cardFeedErrors[feedNameWithDomainID] = {
                 ...newFeedState,
@@ -100,50 +104,26 @@ export default createOnyxDerivedValueConfig({
                 feedErrors,
             };
 
-            if (newFeedState.isFeedConnectionBroken) {
-                allFeedsState.isFeedConnectionBroken = true;
+            // Track cards with broken feed connection
+            if (isFeedConnectionBroken) {
                 cardsWithBrokenFeedConnection[card.cardID] = card;
-
-                if (isExpensifyCard) {
-                    expensifyCardFeedStates.isFeedConnectionBroken = true;
-                } else {
-                    companyCardFeedsState.isFeedConnectionBroken = true;
-                }
-            }
-
-            if (newFeedState.hasFeedErrors) {
-                allFeedsState.hasFeedErrors = true;
-
-                if (isExpensifyCard) {
-                    expensifyCardFeedStates.hasFeedErrors = true;
-                } else {
-                    companyCardFeedsState.hasFeedErrors = true;
-                }
             }
 
-            if (newFeedState.hasWorkspaceErrors) {
-                allFeedsState.hasWorkspaceErrors = true;
+            // Update aggregate states - once true, always stays true
+            const cardTypeState = isExpensifyCard ? expensifyCardFeedStates : companyCardFeedsState;
 
-                if (isExpensifyCard) {
-                    expensifyCardFeedStates.hasWorkspaceErrors = true;
-                } else {
-                    companyCardFeedsState.hasWorkspaceErrors = true;
-                }
-            }
-
-            if (newFeedState.hasFailedCardAssignments) {
-                allFeedsState.hasFailedCardAssignments = true;
-
-                if (isExpensifyCard) {
-                    expensifyCardFeedStates.hasFailedCardAssignments = true;
-                } else {
-                    companyCardFeedsState.hasFailedCardAssignments = true;
-                }
-            }
+            allFeedsState.isFeedConnectionBroken ||= newFeedState.isFeedConnectionBroken;
+            allFeedsState.hasFeedErrors ||= newFeedState.hasFeedErrors;
+            allFeedsState.hasWorkspaceErrors ||= newFeedState.hasWorkspaceErrors;
+            allFeedsState.hasFailedCardAssignments ||= newFeedState.hasFailedCardAssignments;
 
-            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] = shouldShowRbrForWorkspaceAccountID[workspaceAccountID] || shouldShowRBR;
+            cardTypeState.isFeedConnectionBroken ||= newFeedState.isFeedConnectionBroken;
+            cardTypeState.hasFeedErrors ||= newFeedState.hasFeedErrors;
+            cardTypeState.hasWorkspaceErrors ||= newFeedState.hasWorkspaceErrors;
+            cardTypeState.hasFailedCardAssignments ||= newFeedState.hasFailedCardAssignments;
 
-            shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] || shouldShowRBR;
+            shouldShowRbrForWorkspaceAccountID[workspaceAccountID] ||= shouldShowRBR;
+            shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID] ||= shouldShowRBR;
         }
 
         for (const card of Object.values(globalCardList ?? {})) {

From 416b4c4cb1a2fc0e65d8d26b18810ae49826cf82 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 19:58:43 +0000
Subject: [PATCH 101/122] fix: invalid return type for
 `workspaceCardFeedsStatus`

---
 src/hooks/useCompanyCards.ts                               | 4 ++--
 .../companyCards/WorkspaceCompanyCardsTable/index.tsx      | 7 ++++---
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 579e39517b52..18cf6ef789c5 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -5,7 +5,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
-import type {CardFeedsStatus, CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatusByDomainID, CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
@@ -27,7 +27,7 @@ type UseCompanyCardsResult = Partial<{
     assignedCards: CardList;
     cardNames: string[];
     allCardFeeds: CombinedCardFeeds;
-    workspaceCardFeedsStatus: CardFeedsStatus;
+    workspaceCardFeedsStatus: CardFeedsStatusByDomainID;
     companyCardFeeds: CompanyFeeds;
     selectedFeed: CombinedCardFeed;
 }> & {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index 81627e01a2e0..b4993d2a13ee 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -57,7 +57,7 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         feedName,
         cardList,
         assignedCards,
-        workspaceCardFeedsStatus: cardFeedsStatus,
+        workspaceCardFeedsStatus,
         cardNames,
         cardFeedType,
         selectedFeed,
@@ -76,7 +76,8 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
     const hasNoAssignedCard = Object.keys(assignedCards ?? {}).length === 0;
     const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || isLoadingOnyxValue(lastSelectedFeedMetadata);
 
-    const isFeedsError = !isEmptyObject(cardFeedsStatus?.errors);
+    const workspaceCardFeedsErrors = workspaceCardFeedsStatus?.[domainOrWorkspaceAccountID]?.errors;
+    const isFeedsError = !isEmptyObject(workspaceCardFeedsErrors);
 
     const isLoadingCards = cardFeedType === 'directFeed' ? selectedFeed?.accountList === undefined : isLoadingOnyxValue(cardListMetadata) || cardList === undefined;
 
@@ -315,7 +316,7 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
 
             {isFeedsError && (
                 <ErrorMessageRow
-                    errors={cardFeedsStatus?.errors}
+                    errors={workspaceCardFeedsErrors}
                     errorRowStyles={[styles.mh5]}
                 />
             )}

From aeaa98a22e6fbbc6452f2de2a47c78045593cebe Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 20:01:08 +0000
Subject: [PATCH 102/122] fix: prettier

---
 .../OnyxDerived/configs/cardFeedErrors.ts     | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 79a14e9bb08b..a0cf5c0ff132 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -6,8 +6,8 @@ import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
 import type {CardFeedWithNumber, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
+import type {Errors} from '@src/types/onyx/OnyxCommon';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import type { Errors } from '@src/types/onyx/OnyxCommon';
 
 const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
     shouldShowRBR: false,
@@ -74,18 +74,20 @@ export default createOnyxDerivedValueConfig({
             const hasCardErrors = !isEmptyObject(card.errors) || !isEmptyObject(card.errorFields) || card.pendingAction;
             const cardErrors = {
                 ...previousFeedErrors.cardErrors,
-                ...cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {},
-                ...(hasCardErrors ? {
-                    [card.cardID]: {
-                        errors: card.errors,
-                        errorFields: card.errorFields,
-                        pendingAction: card.pendingAction,
-                    },
-                } : {}),
+                ...(cardFeedErrors[feedNameWithDomainID]?.cardErrors ?? {}),
+                ...(hasCardErrors
+                    ? {
+                          [card.cardID]: {
+                              errors: card.errors,
+                              errorFields: card.errorFields,
+                              pendingAction: card.pendingAction,
+                          },
+                      }
+                    : {}),
             } as Record<string, CardErrors>;
 
-            const isFeedConnectionBroken = isCardConnectionBroken(card)
-            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false
+            const isFeedConnectionBroken = isCardConnectionBroken(card);
+            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false;
             const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
 
             const newFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {

From db3748afa66ee1f1b9dca2f1b4882490a6e9a0a4 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 19 Jan 2026 22:46:51 +0000
Subject: [PATCH 103/122] refactor: extract card feed loading error logic into
 separate PR

https://github.com/Expensify/App/pull/79928
---
 src/hooks/useCardFeeds.tsx                    |  9 ++--
 src/hooks/useCompanyCards.ts                  |  6 +--
 src/languages/en.ts                           |  3 --
 src/languages/es.ts                           |  3 --
 src/libs/actions/CompanyCards.ts              | 34 +--------------
 .../WorkspaceCompanyCardsTable/index.tsx      | 42 +++++++------------
 src/types/onyx/CardFeeds.ts                   | 14 +------
 7 files changed, 21 insertions(+), 90 deletions(-)

diff --git a/src/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 6695383c2ff6..473b938fcc9f 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -1,8 +1,7 @@
 import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
-import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
+import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeedWithDomainID} from '@src/types/onyx';
-import type {CardFeedsStatus} from '@src/types/onyx/CardFeeds';
 import useOnyx from './useOnyx';
 import useWorkspaceAccountID from './useWorkspaceAccountID';
 
@@ -20,7 +19,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): [CombinedCardFeeds | undefined, ResultMetadata<OnyxCollection<CardFeeds>>, CardFeeds | undefined, CardFeedsStatus] => {
+const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata<OnyxCollection<CardFeeds>>, 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}`];
@@ -32,9 +31,7 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
         workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds, shouldIncludeFeedPredicate);
     }
 
-    const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(allFeeds);
-
-    return [workspaceFeeds, allFeedsResult, defaultFeed, workspaceCardFeedsStatus];
+    return [workspaceFeeds, allFeedsResult, defaultFeed];
 };
 
 export default useCardFeeds;
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 18cf6ef789c5..278683d063a5 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -5,7 +5,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
-import type {CardFeedsStatusByDomainID, CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
@@ -27,7 +27,6 @@ type UseCompanyCardsResult = Partial<{
     assignedCards: CardList;
     cardNames: string[];
     allCardFeeds: CombinedCardFeeds;
-    workspaceCardFeedsStatus: CardFeedsStatusByDomainID;
     companyCardFeeds: CompanyFeeds;
     selectedFeed: CombinedCardFeed;
 }> & {
@@ -49,7 +48,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
     const policyIDKey = policyID || CONST.DEFAULT_MISSING_ID;
 
     const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyIDKey}`, {canBeMissing: true});
-    const [allCardFeeds, allCardFeedsMetadata, , workspaceCardFeedsStatus] = useCardFeeds(policyID);
+    const [allCardFeeds, allCardFeedsMetadata, ] = useCardFeeds(policyID);
 
     const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);
     const bankName = feedName ? getCompanyCardFeed(feedName) : undefined;
@@ -87,7 +86,6 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
 
     return {
         allCardFeeds,
-        workspaceCardFeedsStatus,
         feedName,
         companyCardFeeds,
         cardList,
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 2c280b057449..a7eafd113b55 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4692,9 +4692,6 @@ const translations = {
         companyCards: {
             addCards: 'Add cards',
             selectCards: 'Select cards',
-            error: {
-                feedsCouldNotBeLoaded: 'An error occurred while loading the feeds. Please try again or contact your administrator.',
-            },
             addNewCard: {
                 other: 'Other',
                 cardProviders: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 08a1dfa27c2d..da14daedbbbd 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4437,9 +4437,6 @@ ${amount} para ${merchant} - ${date}`,
         companyCards: {
             addCards: 'Añadir tarjetas',
             selectCards: 'Seleccionar tarjetas',
-            error: {
-                feedsCouldNotBeLoaded: 'Ocurrió un error al cargar los feeds. Por favor, inténtelo de nuevo o contacte a su administrador.',
-            },
             addNewCard: {
                 other: 'Otros',
                 cardProviders: {
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index f1a31cbad656..46f2448a2c7d 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -842,39 +842,7 @@ function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, f
         feed,
     };
 
-    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
-            value: {
-                isLoading: true,
-            },
-        },
-    ];
-
-    const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
-            value: {
-                isLoading: false,
-                errors: null,
-            },
-        },
-    ];
-
-    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER>> = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
-            value: {
-                isLoading: false,
-                errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.error.feedsCouldNotBeLoaded'),
-            },
-        },
-    ];
-
-    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {optimisticData, successData, failureData});
+    API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
 }
 
 function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index b4993d2a13ee..fe3276c8aaee 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -2,7 +2,6 @@ import type {ListRenderItemInfo} from '@shopify/flash-list';
 import React, {useEffect, useRef, useState} from 'react';
 import {View} from 'react-native';
 import CardFeedIcon from '@components/CardFeedIcon';
-import ErrorMessageRow from '@components/ErrorMessageRow';
 import ScrollView from '@components/ScrollView';
 import TableRowSkeleton from '@components/Skeletons/TableRowSkeleton';
 import Table from '@components/Table';
@@ -22,7 +21,6 @@ import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
-import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import WorkspaceCompanyCardsTableHeaderButtons from './WorkspaceCompanyCardsTableHeaderButtons';
 import WorkspaceCompanyCardTableItem from './WorkspaceCompanyCardsTableItem';
@@ -57,7 +55,6 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         feedName,
         cardList,
         assignedCards,
-        workspaceCardFeedsStatus,
         cardNames,
         cardFeedType,
         selectedFeed,
@@ -76,14 +73,11 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
     const hasNoAssignedCard = Object.keys(assignedCards ?? {}).length === 0;
     const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || isLoadingOnyxValue(lastSelectedFeedMetadata);
 
-    const workspaceCardFeedsErrors = workspaceCardFeedsStatus?.[domainOrWorkspaceAccountID]?.errors;
-    const isFeedsError = !isEmptyObject(workspaceCardFeedsErrors);
-
     const isLoadingCards = cardFeedType === 'directFeed' ? selectedFeed?.accountList === undefined : isLoadingOnyxValue(cardListMetadata) || cardList === undefined;
 
     const isLoadingPage = !isOffline && (isLoadingFeed || isLoadingOnyxValue(personalDetailsMetadata));
-    const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed && !isFeedsError;
-    const showTableControls = showCards && !!selectedFeed && !isLoadingCards && !isFeedsError;
+    const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed;
+    const showTableControls = showCards && !!selectedFeed && !isLoadingCards;
 
     const isGB = countryByIp === CONST.COUNTRY.GB;
     const shouldShowGBDisclaimer = isGB && (isNoFeed || hasNoAssignedCard);
@@ -112,26 +106,25 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
         },
     ];
 
-    const cardsData: WorkspaceCompanyCardTableItemData[] =
-        isLoadingCards || isFeedsError
-            ? []
-            : (cardNames?.map((cardName) => {
-                  const assignedCardPredicate = (card: Card) => (isDirectCardFeed ? card.cardName === cardName : isMaskedCardNumberEqual(card.cardName, cardName));
+    const cardsData: WorkspaceCompanyCardTableItemData[] = isLoadingCards
+        ? []
+        : (cardNames?.map((cardName) => {
+              const assignedCardPredicate = (card: Card) => (isDirectCardFeed ? card.cardName === cardName : isMaskedCardNumberEqual(card.cardName, cardName));
 
-                  const assignedCard = Object.values(assignedCards ?? {}).find(assignedCardPredicate);
+              const assignedCard = Object.values(assignedCards ?? {}).find(assignedCardPredicate);
 
-                  const failedCompanyCardAssignment = failedCompanyCardAssignments?.[cardName];
+              const failedCompanyCardAssignment = failedCompanyCardAssignments?.[cardName];
 
-                  const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
+              const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
 
-                  const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined;
+              const customCardName = assignedCard?.cardID ? customCardNames?.[assignedCard.cardID] : undefined;
 
-                  const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
+              const isCardDeleted = assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
 
-                  const isAssigned = !!assignedCard;
+              const isAssigned = !!assignedCard;
 
-                  return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder, failedCompanyCardAssignment};
-              }) ?? []);
+              return {cardName, customCardName, isCardDeleted, isAssigned, assignedCard, cardholder, failedCompanyCardAssignment};
+          }) ?? []);
 
     const keyExtractor = (item: WorkspaceCompanyCardTableItemData, index: number) => `${item.cardName}_${index}`;
 
@@ -314,13 +307,6 @@ function WorkspaceCompanyCardsTable({policyID, domainOrWorkspaceAccountID, compa
                 </ScrollView>
             )}
 
-            {isFeedsError && (
-                <ErrorMessageRow
-                    errors={workspaceCardFeedsErrors}
-                    errorRowStyles={[styles.mh5]}
-                />
-            )}
-
             {showCards && (
                 <>
                     {!shouldUseNarrowTableLayout && !isLoadingFeed && <Table.Header />}
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 3ce0c2c844bc..1d517c1defdb 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -171,21 +171,11 @@ type DomainSettings = {
         /** Email to primary contact from the domain */
         technicalContactEmail?: string;
     };
-};
 
-/** Card feeds status */
-type CardFeedsStatus = {
     /** Whether we are loading the data via the API */
     isLoading?: boolean;
-
-    /** Collection of errors coming from BE */
-    errors?: OnyxCommon.Errors;
 };
 
-/**
- * Collection of card feeds status by domain ID
- */
-type CardFeedsStatusByDomainID = Record<number, CardFeedsStatus>;
 
 /** Card feeds model, including domain settings */
 type CardFeeds = {
@@ -206,7 +196,7 @@ type CardFeeds = {
         /** Whether to use the technical contact's billing card */
         useTechnicalContactBillingCard?: boolean;
     };
-} & CardFeedsStatus &
+} &
     DomainSettings;
 
 /** Data required to be sent to add a new card */
@@ -300,8 +290,6 @@ export type {
     DirectCardFeedData,
     CardFeedProvider,
     CardFeedData,
-    CardFeedsStatus,
-    CardFeedsStatusByDomainID,
     CompanyFeeds,
     CustomCardFeedData,
     CompanyCardNicknames,

From 6571593f55d0053a47b496eb1edfd79c0f2ea0e0 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 14:56:13 +0000
Subject: [PATCH 104/122] refactor: extract indicator status changes into
 https://github.com/Expensify/App/pull/79987

---
 src/components/Navigation/DebugTabView.tsx    |   2 +-
 src/hooks/useAccountTabIndicatorStatus.ts     |  72 +++-
 src/hooks/useIndicatorStatus.ts               |  93 +++-
 .../useNavigationTabBarIndicatorChecks.ts     | 117 -----
 src/hooks/useWorkspacesTabIndicatorStatus.ts  |  43 +-
 .../unit/useAccountTabIndicatorStatusTest.ts  |  93 ++--
 tests/unit/useIndicatorStatusTest.tsx         |  69 +--
 .../useWorkspacesTabIndicatorStatusTest.ts    | 401 ------------------
 tests/utils/IndicatorTestUtils.ts             |   9 -
 9 files changed, 249 insertions(+), 650 deletions(-)
 delete mode 100644 src/hooks/useNavigationTabBarIndicatorChecks.ts
 delete mode 100644 tests/unit/useWorkspacesTabIndicatorStatusTest.ts
 delete mode 100644 tests/utils/IndicatorTestUtils.ts

diff --git a/src/components/Navigation/DebugTabView.tsx b/src/components/Navigation/DebugTabView.tsx
index 2af72a9b1107..19248e166673 100644
--- a/src/components/Navigation/DebugTabView.tsx
+++ b/src/components/Navigation/DebugTabView.tsx
@@ -7,9 +7,9 @@ import Button from '@components/Button';
 import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
 import Text from '@components/Text';
+import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
 import useLocalize from '@hooks/useLocalize';
-import type {IndicatorStatus} from '@hooks/useNavigationTabBarIndicatorChecks';
 import useOnyx from '@hooks/useOnyx';
 import {useSidebarOrderedReports} from '@hooks/useSidebarOrderedReports';
 import useStyleUtils from '@hooks/useStyleUtils';
diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index 480a814f41b9..ee3835b2fc12 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -1,19 +1,79 @@
-import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
-import type {IndicatorStatus} from './useNavigationTabBarIndicatorChecks';
+import type {ValueOf} from 'type-fest';
+import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
+import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
+import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
+import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import useOnyx from './useOnyx';
 import useTheme from './useTheme';
 
+type AccountTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
+
 type AccountTabIndicatorStatusResult = {
     indicatorColor: string;
-    status: IndicatorStatus | undefined;
+    status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
 };
 
 function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const theme = useTheme();
+    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
+    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
+    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
+    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
+    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
+    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
+    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
+    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
+    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
+    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
+    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
+    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
+    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
+    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
+    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
+    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
+    // we only care if a single error / info condition exists anywhere.
+    const errorChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
+        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
+        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
+        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
+        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
+        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+    };
+
+    const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
+        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+        [CONST.INDICATOR_STATUS.HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO]: hasPartiallySetupBankAccount(bankAccountList),
+    };
 
-    const {accountStatus, infoStatus} = useNavigationTabBarIndicatorChecks();
+    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
+    const [info] = Object.entries(infoChecking).find(([, value]) => value) ?? [];
 
-    const status = accountStatus ?? infoStatus;
-    const indicatorColor = accountStatus ? theme.danger : theme.success;
+    const status = (error ?? info) as AccountTabIndicatorStatus | undefined;
+    const indicatorColor = error ? theme.danger : theme.success;
 
     return {indicatorColor, status};
 }
diff --git a/src/hooks/useIndicatorStatus.ts b/src/hooks/useIndicatorStatus.ts
index 3d5fc0021f53..17b3cf6ba191 100644
--- a/src/hooks/useIndicatorStatus.ts
+++ b/src/hooks/useIndicatorStatus.ts
@@ -1,8 +1,19 @@
+import {useMemo} from 'react';
 import type {ValueOf} from 'type-fest';
-import type CONST from '@src/CONST';
-import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
+import {isConnectionInProgress} from '@libs/actions/connections';
+import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
+import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
+import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
+import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
+import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import useOnyx from './useOnyx';
 import useTheme from './useTheme';
 
+type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
+
 type IndicatorStatusResult = {
     indicatorColor: string;
     status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
@@ -11,14 +22,84 @@ type IndicatorStatusResult = {
 
 function useIndicatorStatus(): IndicatorStatusResult {
     const theme = useTheme();
+    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
+    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
+    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
+    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
+    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
+    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
+    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
+    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
+    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
+    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
+    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
+    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
+    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
+    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
+    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
+    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
+
+    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+    // those should be cleaned out before doing any error checking
+    const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
+
+    const policyErrors = {
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
+        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
+        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
+            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+        ),
+        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
+    };
 
-    const {accountStatus, infoStatus, policyStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
+    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
+    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
+    // we only care if a single error / info condition exists anywhere.
+    const errorChecking: Partial<Record<IndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
+        ...(Object.fromEntries(Object.entries(policyErrors).map(([error, policy]) => [error, !!policy])) as Record<keyof typeof policyErrors, boolean>),
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
+        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
+        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
+        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
+        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
+    };
 
-    const errorStatus = accountStatus ?? policyStatus;
-    const status = errorStatus ?? infoStatus;
-    const indicatorColor = errorStatus ? theme.danger : theme.success;
+    const infoChecking: Partial<Record<IndicatorStatus, boolean>> = {
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
+        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
+            stripeCustomerId,
+            retryBillingSuccessful,
+            billingDisputePending,
+            retryBillingFailed,
+            fundList,
+            billingStatus,
+        ),
+        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
+    };
+
+    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
+    const [info] = Object.entries(infoChecking).find(([, value]) => value) ?? [];
+
+    const status = (error ?? info) as IndicatorStatus | undefined;
+    const policyIDWithErrors = Object.values(policyErrors).find(Boolean)?.id;
+    const indicatorColor = error ? theme.danger : theme.success;
 
     return {indicatorColor, status, policyIDWithErrors};
 }
 
 export default useIndicatorStatus;
+
+export type {IndicatorStatus};
diff --git a/src/hooks/useNavigationTabBarIndicatorChecks.ts b/src/hooks/useNavigationTabBarIndicatorChecks.ts
deleted file mode 100644
index c75ed15b927a..000000000000
--- a/src/hooks/useNavigationTabBarIndicatorChecks.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import type {ValueOf} from 'type-fest';
-import {isConnectionInProgress} from '@libs/actions/connections';
-import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
-import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
-import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
-import {hasPendingExpensifyCardAction} from '@libs/CardUtils';
-import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
-import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
-import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import type {Policy} from '@src/types/onyx';
-import useCardFeedErrors from './useCardFeedErrors';
-import useOnyx from './useOnyx';
-import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
-
-type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
-
-type NavigationTabBarChecksResult = {
-    accountStatus: IndicatorStatus | undefined;
-    policyStatus: IndicatorStatus | undefined;
-    infoStatus: IndicatorStatus | undefined;
-    policyIDWithErrors: string | undefined;
-};
-
-function useNavigationTabBarIndicatorChecks(): NavigationTabBarChecksResult {
-    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
-    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
-    const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true});
-    const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true});
-    const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
-    const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true});
-    const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true});
-    const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
-    const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
-    const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
-    const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
-    const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
-    const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
-    const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
-    const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
-    const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
-
-    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
-    // those should be cleaned out before doing any error checking
-    const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
-
-    const {
-        companyCards: {shouldShowRBR: hasCompanyCardFeedErrors},
-    } = useCardFeedErrors();
-    const {policiesWithCardFeedErrors, isPolicyAdmin} = usePoliciesWithCardFeedErrors();
-
-    const policyChecks: Partial<Record<IndicatorStatus, Policy | undefined>> = {
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: cleanPolicies.find(shouldShowPolicyError),
-        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: cleanPolicies.find(shouldShowCustomUnitsError),
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: cleanPolicies.find(shouldShowEmployeeListError),
-        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: cleanPolicies.find((cleanPolicy) =>
-            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
-        ),
-        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
-        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
-        [CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
-    };
-
-    // All the error & info-checking methods are put into an array. This is so that using _.some() will return
-    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
-    // we only care if a single error / info condition exists anywhere.
-    const accountChecks: Partial<Record<IndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
-        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
-        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
-        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
-        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin ? hasCompanyCardFeedErrors : false,
-    };
-
-    const infoChecks: Partial<Record<IndicatorStatus, boolean>> = {
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
-        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
-        [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
-            stripeCustomerId,
-            retryBillingSuccessful,
-            billingDisputePending,
-            retryBillingFailed,
-            fundList,
-            billingStatus,
-        ),
-        [CONST.INDICATOR_STATUS.HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO]: hasPartiallySetupBankAccount(bankAccountList),
-    };
-
-    const [accountStatus] = Object.entries(accountChecks).find(([, value]) => value) ?? [];
-    const [policyStatus] = Object.entries(policyChecks).find(([, value]) => value) ?? [];
-    const [infoStatus] = Object.entries(infoChecks).find(([, value]) => value) ?? [];
-
-    const policyIDWithErrors = Object.values(policyChecks).find(Boolean)?.id;
-
-    return {
-        accountStatus: accountStatus as IndicatorStatus | undefined,
-        policyStatus: policyStatus as IndicatorStatus | undefined,
-        infoStatus: infoStatus as IndicatorStatus | undefined,
-        policyIDWithErrors,
-    };
-}
-
-export default useNavigationTabBarIndicatorChecks;
-
-export type {IndicatorStatus};
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index d5a705202a7c..5c5c56269d80 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -1,20 +1,51 @@
-import useNavigationTabBarIndicatorChecks from './useNavigationTabBarIndicatorChecks';
-import type {IndicatorStatus} from './useNavigationTabBarIndicatorChecks';
+import {useMemo} from 'react';
+import type {ValueOf} from 'type-fest';
+import {isConnectionInProgress} from '@libs/actions/connections';
+import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
+import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import useOnyx from './useOnyx';
 import useTheme from './useTheme';
 
+type WorkspacesTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
+
 type WorkspacesTabIndicatorStatusResult = {
     indicatorColor: string;
-    status: IndicatorStatus | undefined;
+    status: ValueOf<typeof CONST.INDICATOR_STATUS> | undefined;
     policyIDWithErrors: string | undefined;
 };
 
 function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     const theme = useTheme();
+    const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS, {canBeMissing: true});
+    const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
+    // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+    // those should be cleaned out before doing any error checking
+    const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
+    const policyErrors = {
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
+        [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: Object.values(cleanPolicies).find(shouldShowEmployeeListError),
+        [CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: Object.values(cleanPolicies).find((cleanPolicy) =>
+            shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
+        ),
+        [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
+        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+    };
+
+    // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
+    // early as soon as the first error / info condition is returned. This makes the checks very efficient since
+    // we only care if a single error / info condition exists anywhere.
+    const errorChecking: Partial<Record<WorkspacesTabIndicatorStatus, boolean>> = {
+        ...(Object.fromEntries(Object.entries(policyErrors).map(([error, policy]) => [error, !!policy])) as Record<keyof typeof policyErrors, boolean>),
+    };
 
-    const {policyStatus, policyIDWithErrors} = useNavigationTabBarIndicatorChecks();
+    const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
 
-    const status = policyStatus;
-    const indicatorColor = policyStatus ? theme.danger : theme.success;
+    const status = error as WorkspacesTabIndicatorStatus | undefined;
+    const policyIDWithErrors = Object.values(policyErrors).find(Boolean)?.id;
+    const indicatorColor = error ? theme.danger : theme.success;
 
     return {indicatorColor, status, policyIDWithErrors};
 }
diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index 831559db0d14..d5b118320db9 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -1,36 +1,14 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
-import type {IndicatorTestCase} from 'tests/utils/IndicatorTestUtils';
 import useAccountTabIndicatorStatus from '@hooks/useAccountTabIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
-import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
-const userID = 'johndoe12@expensify.com';
-
-const brokenCardFeed = {
-    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-    workspaceAccountID: 12345,
-};
-
-const accountCardFeedTestCases = {
-    admin: {
-        name: 'has no account card feed error if admin',
-        indicatorColor: defaultTheme.success,
-        status: undefined,
-    },
-    employee: {
-        name: 'has account card feed error if employee (non-admin)',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
-    },
-} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
-
-const getMockForStatus = ({status, name}: IndicatorTestCase) =>
+const getMockForStatus = (status: string) =>
     ({
         [ONYXKEYS.BANK_ACCOUNT_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -64,9 +42,9 @@ const getMockForStatus = ({status, name}: IndicatorTestCase) =>
         },
         [ONYXKEYS.LOGIN_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
-            [userID]: {
+            'johndoe12@expensify.com': {
                 partnerName: 'John Doe',
-                partnerUserID: userID,
+                partnerUserID: 'johndoe12@expensify.com',
                 validatedDate: status !== CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO ? new Date().toISOString() : undefined,
                 errorFields:
                     status === CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR
@@ -104,26 +82,30 @@ const getMockForStatus = ({status, name}: IndicatorTestCase) =>
                       }
                     : undefined,
         },
-        [ONYXKEYS.SESSION]: {
-            email: userID,
-        },
-        [ONYXKEYS.CARD_LIST]: {
-            card1: {
+        [`${ONYXKEYS.CARD_LIST}`]: {
+            // eslint-disable-next-line @typescript-eslint/naming-convention
+            card123: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: name === accountCardFeedTestCases.admin.name || name === accountCardFeedTestCases.employee.name ? 403 : 200,
-                fundID: String(brokenCardFeed.workspaceAccountID),
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
+            },
+            // eslint-disable-next-line @typescript-eslint/naming-convention
+            card456: {
+                bank: 'ANOTHER_BANK',
+                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
             },
         },
-        [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
-            id: '1',
-            name: 'Workspace 1',
-            owner: name === accountCardFeedTestCases.admin.name ? userID : 'otheruser@expensify.com',
-            role: name === accountCardFeedTestCases.admin.name ? 'admin' : 'user',
-            workspaceAccountID: brokenCardFeed.workspaceAccountID,
+        [ONYXKEYS.SESSION]: {
+            email: 'johndoe12@expensify.com',
         },
     }) as unknown as OnyxMultiSetInput;
 
-const TEST_CASES: IndicatorTestCase[] = [
+type TestCase = {
+    name: string;
+    indicatorColor: string;
+    status: string;
+};
+
+const TEST_CASES: TestCase[] = [
     {
         name: 'has user wallet errors',
         indicatorColor: defaultTheme.danger,
@@ -149,6 +131,11 @@ const TEST_CASES: IndicatorTestCase[] = [
         indicatorColor: defaultTheme.danger,
         status: CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS,
     },
+    {
+        name: 'has card connection error',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR,
+    },
     {
         name: 'has phone number error',
         indicatorColor: defaultTheme.danger,
@@ -159,8 +146,6 @@ const TEST_CASES: IndicatorTestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO,
     },
-    accountCardFeedTestCases.employee,
-    accountCardFeedTestCases.admin,
 ];
 
 describe('useAccountTabIndicatorStatus', () => {
@@ -168,13 +153,12 @@ describe('useAccountTabIndicatorStatus', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
-        initOnyxDerivedValues();
     });
 
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
             await act(async () => {
-                await Onyx.multiSet(getMockForStatus(testCase));
+                await Onyx.multiSet(getMockForStatus(testCase.status));
                 await waitForBatchedUpdatesWithAct();
             });
         });
@@ -204,7 +188,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [ONYXKEYS.CARD_LIST]: {},
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -243,7 +227,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [ONYXKEYS.CARD_LIST]: {},
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -283,7 +267,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: {},
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [ONYXKEYS.CARD_LIST]: {},
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -330,7 +314,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     },
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: {},
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {},
-                    [ONYXKEYS.CARD_LIST]: {},
+                    [`${ONYXKEYS.CARD_LIST}`]: {},
                     [ONYXKEYS.SESSION]: {
                         email: 'johndoe12@expensify.com',
                     },
@@ -364,7 +348,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [ONYXKEYS.LOGIN_LIST]: null,
                     [ONYXKEYS.REIMBURSEMENT_ACCOUNT]: null,
                     [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: null,
-                    [ONYXKEYS.CARD_LIST]: null,
+                    [`${ONYXKEYS.CARD_LIST}`]: null,
                     [ONYXKEYS.SESSION]: null,
                 } as unknown as OnyxMultiSetInput);
                 await waitForBatchedUpdatesWithAct();
@@ -389,10 +373,9 @@ describe('useAccountTabIndicatorStatus', () => {
         it('shows error for third party card with broken connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [ONYXKEYS.CARD_LIST]: {
+                    [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
-                            bank: brokenCardFeed.feedName,
-                            fundID: String(brokenCardFeed.workspaceAccountID),
+                            bank: 'Chase',
                             lastScrapeResult: 403,
                         },
                     },
@@ -404,17 +387,16 @@ describe('useAccountTabIndicatorStatus', () => {
             await waitForBatchedUpdatesWithAct();
             const {status, indicatorColor} = result.current;
 
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS);
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR);
             expect(indicatorColor).toBe(defaultTheme.danger);
         });
 
         it('does not show error for Expensify Card with broken connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [ONYXKEYS.CARD_LIST]: {
+                    [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
                             bank: CONST.EXPENSIFY_CARD.BANK,
-                            fundID: String(brokenCardFeed.workspaceAccountID),
                             lastScrapeResult: 403,
                         },
                     },
@@ -432,10 +414,9 @@ describe('useAccountTabIndicatorStatus', () => {
         it('does not show error for third party card with good connection', async () => {
             await act(async () => {
                 await Onyx.multiSet({
-                    [ONYXKEYS.CARD_LIST]: {
+                    [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
                             bank: 'Chase',
-                            fundID: String(brokenCardFeed.workspaceAccountID),
                             lastScrapeResult: 200,
                         },
                     },
diff --git a/tests/unit/useIndicatorStatusTest.tsx b/tests/unit/useIndicatorStatusTest.tsx
index 56ec28c69a1c..69f4ac51503a 100644
--- a/tests/unit/useIndicatorStatusTest.tsx
+++ b/tests/unit/useIndicatorStatusTest.tsx
@@ -1,45 +1,21 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
+import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
-import initWithOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {IndicatorTestCase} from '../utils/IndicatorTestUtils';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 
-const userID = 'johndoe12@expensify.com';
-const otherUserID = 'otheruser@expensify.com';
-
-const brokenCardFeed = {
-    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-    workspaceAccountID: 12345,
-};
-
-const cardFeedErrorTestCases = {
-    admin: {
-        name: 'has policy card feed error if admin',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
-        policyIDWithErrors: '1',
-    },
-    employee: {
-        name: 'has account card feed error if employee (non-admin)',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
-    },
-} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
-
-const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =>
+const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
     ({
         [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
             id: '1',
             name: 'Workspace 1',
-            owner: isAdmin ? userID : otherUserID,
+            owner: 'johndoe12@expensify.com',
             role: isAdmin ? 'admin' : 'user',
-            workspaceAccountID: brokenCardFeed.workspaceAccountID,
             customUnits:
                 status === CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR
                     ? {
@@ -52,7 +28,7 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
         [`${ONYXKEYS.COLLECTION.POLICY}2` as const]: {
             id: '2',
             name: 'Workspace 2',
-            owner: userID,
+            owner: 'johndoe12@expensify.com',
             role: isAdmin ? 'admin' : 'user',
             errors:
                 status === CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS
@@ -64,12 +40,12 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
         [`${ONYXKEYS.COLLECTION.POLICY}3` as const]: {
             id: '3',
             name: 'Workspace 3',
-            owner: userID,
+            owner: 'johndoe12@expensify.com',
             role: isAdmin ? 'admin' : 'user',
             employeeList: {
                 // eslint-disable-next-line @typescript-eslint/naming-convention
-                [userID]: {
-                    email: userID,
+                'johndoe12@expensify.com': {
+                    email: 'johndoe12@expensify.com',
                     errors:
                         status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR
                             ? {
@@ -82,7 +58,7 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
         [`${ONYXKEYS.COLLECTION.POLICY}4` as const]: {
             id: '4',
             name: 'Workspace 4',
-            owner: userID,
+            owner: 'johndoe12@expensify.com',
             role: isAdmin ? 'admin' : 'auditor',
             connections:
                 status === CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS
@@ -127,9 +103,9 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
         },
         [ONYXKEYS.LOGIN_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
-            [userID]: {
+            'johndoe12@expensify.com': {
                 partnerName: 'John Doe',
-                partnerUserID: userID,
+                partnerUserID: 'johndoe12@expensify.com',
                 validatedDate: status !== CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO ? new Date().toISOString() : undefined,
                 errorFields:
                     status === CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR
@@ -160,14 +136,8 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
                 cardID: 123456,
                 bank: CONST.EXPENSIFY_CARD.BANK,
                 accountID: 123,
-                fundID: String(brokenCardFeed.workspaceAccountID),
                 state: status === CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO ? CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED : CONST.EXPENSIFY_CARD.STATE.OPEN,
             },
-            card1: {
-                bank: 'OTHER_BANK',
-                lastScrapeResult: name === cardFeedErrorTestCases.admin.name || name === cardFeedErrorTestCases.employee.name ? 403 : 200,
-                fundID: String(brokenCardFeed.workspaceAccountID),
-            },
         },
         [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {
             legalFirstName: 'John',
@@ -182,7 +152,14 @@ const getMockForStatus = ({status, name}: IndicatorTestCase, isAdmin: boolean) =
         },
     }) as OnyxMultiSetInput;
 
-const TEST_CASES: IndicatorTestCase[] = [
+type TestCase = {
+    name: string;
+    indicatorColor: string;
+    status: IndicatorStatus;
+    policyIDWithErrors?: string;
+};
+
+const TEST_CASES: TestCase[] = [
     {
         name: 'has policy errors',
         indicatorColor: defaultTheme.danger,
@@ -261,10 +238,9 @@ const TEST_CASES: IndicatorTestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO,
         policyIDWithErrors: undefined,
     },
-    cardFeedErrorTestCases.admin,
 ];
 
-const TEST_CASES_NON_ADMIN: IndicatorTestCase[] = [
+const TEST_CASES_NON_ADMIN: TestCase[] = [
     {
         name: 'has custom units error but not an admin so no RBR',
         indicatorColor: defaultTheme.success,
@@ -285,7 +261,6 @@ const TEST_CASES_NON_ADMIN: IndicatorTestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS,
     },
-    cardFeedErrorTestCases.employee,
 ];
 
 describe('useIndicatorStatusTest', () => {
@@ -293,12 +268,10 @@ describe('useIndicatorStatusTest', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
-        initWithOnyxDerivedValues();
     });
-
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase, true));
+            await Onyx.multiSet(getMockForStatus(testCase.status));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {
@@ -328,7 +301,7 @@ describe('useIndicatorStatusTest', () => {
     });
     describe.each(TEST_CASES_NON_ADMIN)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase, false));
+            await Onyx.multiSet(getMockForStatus(testCase.status, false));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {
diff --git a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts b/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
deleted file mode 100644
index 402950014fc6..000000000000
--- a/tests/unit/useWorkspacesTabIndicatorStatusTest.ts
+++ /dev/null
@@ -1,401 +0,0 @@
-import {act, renderHook} from '@testing-library/react-native';
-import type {OnyxMultiSetInput} from 'react-native-onyx';
-import Onyx from 'react-native-onyx';
-import type {IndicatorTestCase} from 'tests/utils/IndicatorTestUtils';
-import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorStatus';
-// eslint-disable-next-line no-restricted-imports
-import {defaultTheme} from '@styles/theme';
-import CONST from '@src/CONST';
-import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
-import ONYXKEYS from '@src/ONYXKEYS';
-import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
-
-const workspaceCardFeedTestCases = {
-    admin: {
-        name: 'has workspace card feed error if admin',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
-    },
-    employee: {
-        name: 'has no workspace card feed error if employee (non-admin)',
-        indicatorColor: defaultTheme.success,
-        status: undefined,
-    },
-} as const satisfies Record<'admin' | 'employee', IndicatorTestCase>;
-
-const userID = 'admin@expensify.com';
-const otherUserID = 'employee@example.com';
-
-const WORKSPACE = {
-    policyID: '1',
-    workspaceAccountID: 12345,
-    policyName: 'Test Workspace',
-};
-
-const CARD_FEED = {
-    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
-    workspaceAccountID: WORKSPACE.workspaceAccountID,
-};
-
-const TEST_CASE_NAMES = {
-    hasPolicyErrors: 'has policy errors',
-    hasCustomUnitsError: 'has custom units error',
-    hasEmployeeListError: 'has employee list error',
-    hasSyncErrors: 'has sync errors',
-    hasQBOExportError: 'has QBO export error',
-    hasUberCredentialsError: 'has Uber credentials error',
-    hasAdminCardFeedErrors: workspaceCardFeedTestCases.admin.name,
-    hasNoCardFeedErrorsForEmployee: workspaceCardFeedTestCases.employee.name,
-    noErrors: 'no errors',
-} as const;
-
-const getMockForStatus = ({name}: IndicatorTestCase) =>
-    ({
-        [ONYXKEYS.SESSION]: {
-            email: name === workspaceCardFeedTestCases.employee.name ? otherUserID : userID,
-        },
-        [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-            id: WORKSPACE.policyID,
-            name: WORKSPACE.policyName,
-            owner: name === workspaceCardFeedTestCases.employee.name ? userID : userID,
-            role: name === workspaceCardFeedTestCases.employee.name ? 'user' : 'admin',
-            workspaceAccountID: WORKSPACE.workspaceAccountID,
-            // Policy errors
-            errors: name === TEST_CASE_NAMES.hasPolicyErrors ? {policyError: 'Something went wrong'} : undefined,
-            errorFields: undefined,
-            // Custom units errors
-            customUnits: name === TEST_CASE_NAMES.hasCustomUnitsError ? {errors: {customUnitError: 'Invalid custom unit'}} : undefined,
-            // Employee list errors
-            employeeList:
-                name === TEST_CASE_NAMES.hasEmployeeListError
-                    ? {
-                          otherUserID: {
-                              email: otherUserID,
-                              errors: {employeeError: 'Employee error'},
-                          },
-                      }
-                    : undefined,
-            // Sync errors - use connections with sync errors
-            // hasSynchronizationErrorMessage requires: !isSyncInProgress && lastSync exists && isSuccessful === false && errorDate exists
-            connections: {
-                ...(name === TEST_CASE_NAMES.hasSyncErrors
-                    ? {
-                          quickbooksOnline: {
-                              lastSync: {
-                                  errorMessage: 'Sync failed',
-                                  isSuccessful: false,
-                                  errorDate: new Date().toISOString(),
-                              },
-                          },
-                      }
-                    : {}),
-                ...(name === TEST_CASE_NAMES.hasQBOExportError
-                    ? {
-                          quickbooksOnline: {
-                              config: {
-                                  reimbursableExpensesExportDestination: 'VENDOR_BILL',
-                                  reimbursableExpensesAccount: undefined,
-                              },
-                          },
-                      }
-                    : {}),
-            },
-            // Uber credentials error
-            receiptPartners:
-                name === TEST_CASE_NAMES.hasUberCredentialsError
-                    ? {
-                          uber: {
-                              error: 'Invalid Uber credentials',
-                          },
-                      }
-                    : undefined,
-        },
-        // Card feed errors - both admin and employee test cases have broken card connection
-        // Admin sees HAS_POLICY_ADMIN_CARD_FEED_ERRORS, employee does NOT see it in workspaces tab
-        [`${ONYXKEYS.CARD_LIST}`]:
-            name === workspaceCardFeedTestCases.admin.name || name === workspaceCardFeedTestCases.employee.name
-                ? {
-                      card1: {
-                          cardID: 1,
-                          bank: CARD_FEED.feedName,
-                          lastScrapeResult: 403, // Broken connection
-                          fundID: String(CARD_FEED.workspaceAccountID),
-                      },
-                  }
-                : {},
-        // Sync progress - not in progress
-        [`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${WORKSPACE.policyID}` as const]:
-            name === TEST_CASE_NAMES.hasSyncErrors
-                ? {
-                      stageInProgress: null,
-                      connectionName: 'quickbooksOnline',
-                  }
-                : undefined,
-    }) as unknown as OnyxMultiSetInput;
-
-const TEST_CASES: IndicatorTestCase[] = [
-    {
-        name: TEST_CASE_NAMES.hasPolicyErrors,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS,
-    },
-    {
-        name: TEST_CASE_NAMES.hasCustomUnitsError,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR,
-    },
-    {
-        name: TEST_CASE_NAMES.hasEmployeeListError,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR,
-    },
-    {
-        name: TEST_CASE_NAMES.hasSyncErrors,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS,
-    },
-    {
-        name: TEST_CASE_NAMES.hasQBOExportError,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR,
-    },
-    {
-        name: TEST_CASE_NAMES.hasUberCredentialsError,
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR,
-    },
-    workspaceCardFeedTestCases.admin,
-    workspaceCardFeedTestCases.employee,
-];
-
-describe('useWorkspacesTabIndicatorStatus', () => {
-    beforeAll(() => {
-        Onyx.init({
-            keys: ONYXKEYS,
-        });
-        initOnyxDerivedValues();
-    });
-
-    describe.each(TEST_CASES)('$name', (testCase) => {
-        beforeAll(async () => {
-            await act(async () => {
-                await Onyx.multiSet(getMockForStatus(testCase));
-                await waitForBatchedUpdatesWithAct();
-            });
-        });
-
-        it('returns correct indicatorColor', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {indicatorColor} = result.current;
-            expect(indicatorColor).toBe(testCase.indicatorColor);
-        });
-
-        it('returns correct status', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status} = result.current;
-            expect(status).toBe(testCase.status);
-        });
-
-        it('returns policyIDWithErrors', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {policyIDWithErrors} = result.current;
-            // Employee test case has no policy errors visible (card feed errors don't show for employees in workspaces tab)
-            if (testCase.name === workspaceCardFeedTestCases.employee.name) {
-                expect(policyIDWithErrors).toBeUndefined();
-            } else {
-                expect(policyIDWithErrors).toBe(WORKSPACE.policyID);
-            }
-        });
-    });
-
-    describe('no errors', () => {
-        beforeAll(async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: userID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'admin',
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-        });
-
-        it('returns undefined status when no errors exist', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status} = result.current;
-            expect(status).toBeUndefined();
-        });
-
-        it('returns success color when no errors exist', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {indicatorColor} = result.current;
-            expect(indicatorColor).toBe(defaultTheme.success);
-        });
-
-        it('returns undefined policyIDWithErrors when no errors exist', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {policyIDWithErrors} = result.current;
-            expect(policyIDWithErrors).toBeUndefined();
-        });
-    });
-
-    describe('non-admin user', () => {
-        beforeAll(async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: otherUserID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'user', // Non-admin role
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                        // Policy errors should NOT show for non-admin
-                        errors: {policyError: 'Something went wrong'},
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-        });
-
-        it('does not show policy errors for non-admin', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status} = result.current;
-            // Policy errors require admin role
-            expect(status).toBeUndefined();
-        });
-    });
-
-    describe('multiple policies with errors', () => {
-        const SECOND_WORKSPACE = {
-            policyID: '2',
-            workspaceAccountID: 67890,
-            policyName: 'Second Workspace',
-        };
-
-        beforeAll(async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: userID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'admin',
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                        // No errors on first policy
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${SECOND_WORKSPACE.policyID}` as const]: {
-                        id: SECOND_WORKSPACE.policyID,
-                        name: SECOND_WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'admin',
-                        workspaceAccountID: SECOND_WORKSPACE.workspaceAccountID,
-                        // Errors on second policy
-                        errors: {policyError: 'Something went wrong'},
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-        });
-
-        it('returns error status when at least one policy has errors', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status, indicatorColor} = result.current;
-
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS);
-            expect(indicatorColor).toBe(defaultTheme.danger);
-        });
-
-        it('returns the policyID of the policy with errors', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {policyIDWithErrors} = result.current;
-
-            expect(policyIDWithErrors).toBe(SECOND_WORKSPACE.policyID);
-        });
-    });
-
-    describe('missing data', () => {
-        beforeAll(async () => {
-            // Clear all Onyx data first to ensure clean state
-            await Onyx.clear();
-            await waitForBatchedUpdatesWithAct();
-        });
-
-        it('handles missing data gracefully', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status, indicatorColor, policyIDWithErrors} = result.current;
-
-            expect(status).toBeUndefined();
-            expect(indicatorColor).toBe(defaultTheme.success);
-            expect(policyIDWithErrors).toBeUndefined();
-        });
-    });
-
-    describe('error priority', () => {
-        beforeAll(async () => {
-            await act(async () => {
-                await Onyx.multiSet({
-                    [ONYXKEYS.SESSION]: {
-                        email: userID,
-                    },
-                    [`${ONYXKEYS.COLLECTION.POLICY}${WORKSPACE.policyID}` as const]: {
-                        id: WORKSPACE.policyID,
-                        name: WORKSPACE.policyName,
-                        owner: userID,
-                        role: 'admin',
-                        workspaceAccountID: WORKSPACE.workspaceAccountID,
-                        // Multiple errors at once
-                        errors: {policyError: 'Policy error'},
-                        customUnits: {errors: {customUnitError: 'Custom unit error'}},
-                        employeeList: {
-                            [otherUserID]: {
-                                email: otherUserID,
-                                errors: {employeeError: 'Employee error'},
-                            },
-                        },
-                    },
-                    [`${ONYXKEYS.CARD_LIST}`]: {},
-                } as unknown as OnyxMultiSetInput);
-                await waitForBatchedUpdatesWithAct();
-            });
-        });
-
-        it('returns the first error found based on check order', async () => {
-            const {result} = renderHook(() => useWorkspacesTabIndicatorStatus());
-            await waitForBatchedUpdatesWithAct();
-            const {status} = result.current;
-
-            // Based on the order in useNavigationTabBarIndicatorChecks:
-            // 1. HAS_POLICY_ERRORS
-            // 2. HAS_CUSTOM_UNITS_ERROR
-            // 3. HAS_EMPLOYEE_LIST_ERROR
-            // etc.
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS);
-        });
-    });
-});
diff --git a/tests/utils/IndicatorTestUtils.ts b/tests/utils/IndicatorTestUtils.ts
deleted file mode 100644
index 45f071dd796d..000000000000
--- a/tests/utils/IndicatorTestUtils.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-type IndicatorTestCase = {
-    name: string;
-    indicatorColor: string;
-    status: string | undefined;
-    policyIDWithErrors?: string;
-};
-
-// eslint-disable-next-line import/prefer-default-export
-export type {IndicatorTestCase};

From 9432168114bccbd35eab086ab34d01a1352bce99 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 15:13:28 +0000
Subject: [PATCH 105/122] feat: add back card feed errors to indicator status
 hooks

---
 src/hooks/useAccountTabIndicatorStatus.ts    | 13 +++++++--
 src/hooks/useIndicatorStatus.ts              | 30 +++++++++++++-------
 src/hooks/useWorkspacesTabIndicatorStatus.ts |  5 ++++
 3 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/src/hooks/useAccountTabIndicatorStatus.ts b/src/hooks/useAccountTabIndicatorStatus.ts
index ee3835b2fc12..cf3598ff690d 100644
--- a/src/hooks/useAccountTabIndicatorStatus.ts
+++ b/src/hooks/useAccountTabIndicatorStatus.ts
@@ -1,12 +1,14 @@
 import type {ValueOf} from 'type-fest';
 import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
 import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
-import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {hasPendingExpensifyCardAction} from '@libs/CardUtils';
 import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
 import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
+import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
 import useTheme from './useTheme';
 
 type AccountTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
@@ -26,13 +28,18 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
     const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true});
     const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS, {canBeMissing: true});
     const [allCards] = useOnyx(`${ONYXKEYS.CARD_LIST}`, {canBeMissing: true});
-    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
     const [stripeCustomerId] = useOnyx(ONYXKEYS.NVP_PRIVATE_STRIPE_CUSTOMER_ID, {canBeMissing: true});
     const [retryBillingSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL, {canBeMissing: true});
     const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
     const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
     const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
+
+    const {
+        companyCards: {shouldShowRBR: hasCompanyCardFeedErrors},
+    } = useCardFeedErrors();
+    const {isPolicyAdmin} = usePoliciesWithCardFeedErrors();
+
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
     // early as soon as the first error / info condition is returned. This makes the checks very efficient since
     // we only care if a single error / info condition exists anywhere.
@@ -43,7 +50,6 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
         [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
         // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
         [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
-        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
         [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
         [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
             stripeCustomerId,
@@ -53,6 +59,7 @@ function useAccountTabIndicatorStatus(): AccountTabIndicatorStatusResult {
             fundList,
             billingStatus,
         ),
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin ? hasCompanyCardFeedErrors : false,
     };
 
     const infoChecking: Partial<Record<AccountTabIndicatorStatus, boolean>> = {
diff --git a/src/hooks/useIndicatorStatus.ts b/src/hooks/useIndicatorStatus.ts
index 17b3cf6ba191..d9f4fe8036f7 100644
--- a/src/hooks/useIndicatorStatus.ts
+++ b/src/hooks/useIndicatorStatus.ts
@@ -3,13 +3,16 @@ import type {ValueOf} from 'type-fest';
 import {isConnectionInProgress} from '@libs/actions/connections';
 import {shouldShowQBOReimbursableExportDestinationAccountError} from '@libs/actions/connections/QuickbooksOnline';
 import {hasPaymentMethodError} from '@libs/actions/PaymentMethods';
-import {checkIfFeedConnectionIsBroken, hasPendingExpensifyCardAction} from '@libs/CardUtils';
-import {shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
+import {hasPartiallySetupBankAccount} from '@libs/BankAccountUtils';
+import {hasPendingExpensifyCardAction} from '@libs/CardUtils';
+import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, shouldShowEmployeeListError, shouldShowPolicyError, shouldShowSyncError} from '@libs/PolicyUtils';
 import {hasSubscriptionGreenDotInfo, hasSubscriptionRedDotError} from '@libs/SubscriptionUtils';
 import {hasLoginListError, hasLoginListInfo} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import useCardFeedErrors from './useCardFeedErrors';
 import useOnyx from './useOnyx';
+import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
 import useTheme from './useTheme';
 
 type IndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
@@ -37,9 +40,13 @@ function useIndicatorStatus(): IndicatorStatusResult {
     const [billingDisputePending] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_DISPUTE_PENDING, {canBeMissing: true});
     const [retryBillingFailed] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_FAILED, {canBeMissing: true});
     const [billingStatus] = useOnyx(ONYXKEYS.NVP_PRIVATE_BILLING_STATUS, {canBeMissing: true});
-    const hasBrokenFeedConnection = checkIfFeedConnectionIsBroken(allCards, CONST.EXPENSIFY_CARD.BANK);
     const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
 
+    const {
+        companyCards: {shouldShowRBR: hasCompanyCardFeedErrors},
+    } = useCardFeedErrors();
+    const {policiesWithCardFeedErrors, isPolicyAdmin} = usePoliciesWithCardFeedErrors();
+
     // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
     // those should be cleaned out before doing any error checking
     const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
@@ -52,6 +59,8 @@ function useIndicatorStatus(): IndicatorStatusResult {
             shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
         ),
         [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
+        [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
     };
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return
@@ -61,6 +70,11 @@ function useIndicatorStatus(): IndicatorStatusResult {
         [CONST.INDICATOR_STATUS.HAS_USER_WALLET_ERRORS]: Object.keys(userWallet?.errors ?? {}).length > 0,
         [CONST.INDICATOR_STATUS.HAS_PAYMENT_METHOD_ERROR]: hasPaymentMethodError(bankAccountList, fundList, allCards),
         ...(Object.fromEntries(Object.entries(policyErrors).map(([error, policy]) => [error, !!policy])) as Record<keyof typeof policyErrors, boolean>),
+        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
+        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
+        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
+        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
+        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
         [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_ERRORS]: hasSubscriptionRedDotError(
             stripeCustomerId,
             retryBillingSuccessful,
@@ -69,16 +83,12 @@ function useIndicatorStatus(): IndicatorStatusResult {
             fundList,
             billingStatus,
         ),
-        [CONST.INDICATOR_STATUS.HAS_REIMBURSEMENT_ACCOUNT_ERRORS]: Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
-        [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR]: !!loginList && hasLoginListError(loginList),
-        // Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
-        [CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS]: Object.keys(walletTerms?.errors ?? {}).length > 0 && !walletTerms?.chatReportID,
-        [CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR]: hasBrokenFeedConnection,
-        [CONST.INDICATOR_STATUS.HAS_PHONE_NUMBER_ERROR]: !!privatePersonalDetails?.errorFields?.phoneNumber,
+        [CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS]: !isPolicyAdmin ? hasCompanyCardFeedErrors : false,
     };
 
     const infoChecking: Partial<Record<IndicatorStatus, boolean>> = {
         [CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO]: !!loginList && hasLoginListInfo(loginList, session?.email),
+        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
         [CONST.INDICATOR_STATUS.HAS_SUBSCRIPTION_INFO]: hasSubscriptionGreenDotInfo(
             stripeCustomerId,
             retryBillingSuccessful,
@@ -87,7 +97,7 @@ function useIndicatorStatus(): IndicatorStatusResult {
             fundList,
             billingStatus,
         ),
-        [CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO]: hasPendingExpensifyCardAction(allCards, privatePersonalDetails),
+        [CONST.INDICATOR_STATUS.HAS_PARTIALLY_SETUP_BANK_ACCOUNT_INFO]: hasPartiallySetupBankAccount(bankAccountList),
     };
 
     const [error] = Object.entries(errorChecking).find(([, value]) => value) ?? [];
diff --git a/src/hooks/useWorkspacesTabIndicatorStatus.ts b/src/hooks/useWorkspacesTabIndicatorStatus.ts
index 5c5c56269d80..f8bcccb17f05 100644
--- a/src/hooks/useWorkspacesTabIndicatorStatus.ts
+++ b/src/hooks/useWorkspacesTabIndicatorStatus.ts
@@ -6,6 +6,7 @@ import {getUberConnectionErrorDirectlyFromPolicy, shouldShowCustomUnitsError, sh
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import useOnyx from './useOnyx';
+import usePoliciesWithCardFeedErrors from './usePoliciesWithCardFeedErrors';
 import useTheme from './useTheme';
 
 type WorkspacesTabIndicatorStatus = ValueOf<typeof CONST.INDICATOR_STATUS>;
@@ -23,6 +24,9 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
     // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
     // those should be cleaned out before doing any error checking
     const cleanPolicies = useMemo(() => Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id)), [policies]);
+
+    const {policiesWithCardFeedErrors, isPolicyAdmin} = usePoliciesWithCardFeedErrors();
+
     const policyErrors = {
         [CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS]: Object.values(cleanPolicies).find(shouldShowPolicyError),
         [CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: Object.values(cleanPolicies).find(shouldShowCustomUnitsError),
@@ -32,6 +36,7 @@ function useWorkspacesTabIndicatorStatus(): WorkspacesTabIndicatorStatusResult {
         ),
         [CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: Object.values(cleanPolicies).find(shouldShowQBOReimbursableExportDestinationAccountError),
         [CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: Object.values(cleanPolicies).find(getUberConnectionErrorDirectlyFromPolicy),
+        [CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS]: isPolicyAdmin ? policiesWithCardFeedErrors.at(0) : undefined,
     };
 
     // All of the error & info-checking methods are put into an array. This is so that using _.some() will return

From e03bcc67ce584a1da408840b83e8634bcb3a6362 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 15:27:23 +0000
Subject: [PATCH 106/122] refactor: remove workspace card feed status code

---
 src/libs/CardFeedUtils.ts                     | 19 +------------------
 .../OnyxDerived/configs/cardFeedErrors.ts     | 11 +----------
 src/types/onyx/DerivedValues.ts               |  4 ----
 3 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 04c2295ef64b..0fa3a43a20d1 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
-import type {CardFeedsStatus, CardFeedsStatusByDomainID, CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -511,21 +511,6 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection<CardFeeds>
     return cardFeedsForDisplayPerPolicy;
 }
 
-function getCardFeedStatus(feed: CardFeeds | undefined): CardFeedsStatus {
-    return {
-        errors: feed?.errors,
-        isLoading: feed?.isLoading,
-    };
-}
-
-function getWorkspaceCardFeedsStatus(allFeeds: OnyxCollection<CardFeeds> | undefined): CardFeedsStatusByDomainID {
-    return Object.entries(allFeeds ?? {}).reduce<CardFeedsStatusByDomainID>((acc, [onyxKey, feeds]) => {
-        const domainID = Number(onyxKey.split('_').at(-1));
-        acc[domainID] = getCardFeedStatus(feeds);
-        return acc;
-    }, {} as CardFeedsStatusByDomainID);
-}
-
 function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean): CombinedCardFeeds {
     return Object.entries(allFeeds ?? {}).reduce<CombinedCardFeeds>((acc, [onyxKey, feeds]) => {
         const domainID = Number(onyxKey.split('_').at(-1));
@@ -582,6 +567,4 @@ export {
     getCardFeedsForDisplay,
     getCardFeedsForDisplayPerPolicy,
     getCombinedCardFeedsFromAllFeeds,
-    getCardFeedStatus,
-    getWorkspaceCardFeedsStatus,
 };
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index a0cf5c0ff132..54a626bd4343 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -1,4 +1,4 @@
-import {getCombinedCardFeedsFromAllFeeds, getWorkspaceCardFeedsStatus} from '@libs/CardFeedUtils';
+import {getCombinedCardFeedsFromAllFeeds} from '@libs/CardFeedUtils';
 import {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
 import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
 import CONST from '@src/CONST';
@@ -12,7 +12,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
 const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
     shouldShowRBR: false,
     hasFailedCardAssignments: false,
-    hasWorkspaceErrors: false,
     hasFeedErrors: false,
     isFeedConnectionBroken: false,
 };
@@ -21,9 +20,6 @@ function getShouldShowRBR(state: Partial<CardFeedErrorState>): boolean {
     if (state.hasFeedErrors) {
         return true;
     }
-    if (state.hasWorkspaceErrors) {
-        return true;
-    }
     if (state.hasFailedCardAssignments) {
         return true;
     }
@@ -36,8 +32,6 @@ export default createOnyxDerivedValueConfig({
     dependencies: [ONYXKEYS.CARD_LIST, ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS, ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER],
     compute: ([globalCardList, allWorkspaceCards, failedCompanyCardAssignmentsPerFeed, cardFeeds]) => {
         const combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
-        const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
-
         const cardFeedErrors: CardFeedErrorsObject = {};
         const shouldShowRbrForWorkspaceAccountID: Record<number, boolean> = {};
         const shouldShowRbrForFeedNameWithDomainID: Record<string, boolean> = {};
@@ -93,7 +87,6 @@ export default createOnyxDerivedValueConfig({
             const newFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
                 isFeedConnectionBroken: isFeedConnectionBroken || previousFeedErrors.isFeedConnectionBroken,
                 hasFeedErrors: hasFeedErrors || previousFeedErrors.hasFeedErrors,
-                hasWorkspaceErrors: hasWorkspaceErrors || previousFeedErrors.hasWorkspaceErrors,
                 hasFailedCardAssignments: hasFailedCardAssignments || previousFeedErrors.hasFailedCardAssignments,
             };
 
@@ -116,12 +109,10 @@ export default createOnyxDerivedValueConfig({
 
             allFeedsState.isFeedConnectionBroken ||= newFeedState.isFeedConnectionBroken;
             allFeedsState.hasFeedErrors ||= newFeedState.hasFeedErrors;
-            allFeedsState.hasWorkspaceErrors ||= newFeedState.hasWorkspaceErrors;
             allFeedsState.hasFailedCardAssignments ||= newFeedState.hasFailedCardAssignments;
 
             cardTypeState.isFeedConnectionBroken ||= newFeedState.isFeedConnectionBroken;
             cardTypeState.hasFeedErrors ||= newFeedState.hasFeedErrors;
-            cardTypeState.hasWorkspaceErrors ||= newFeedState.hasWorkspaceErrors;
             cardTypeState.hasFailedCardAssignments ||= newFeedState.hasFailedCardAssignments;
 
             shouldShowRbrForWorkspaceAccountID[workspaceAccountID] ||= shouldShowRBR;
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index f75f9148e1a3..d7cbfc38a6cd 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -107,10 +107,6 @@ type CardFeedErrorState = {
      * Whether some failed card assignments.
      */
     hasFailedCardAssignments: boolean;
-    /**
-     * Whether some workspace errors.
-     */
-    hasWorkspaceErrors: boolean;
     /**
      * Whether a specific feed within a workspace/domain has errors.
      */

From e0240e70cae33714ecf009dcc635b16a5b69241b Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 15:27:35 +0000
Subject: [PATCH 107/122] fix: feed always has feed errors

---
 src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 54a626bd4343..ffd43fd3b060 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -60,6 +60,7 @@ export default createOnyxDerivedValueConfig({
                 ...previousFeedErrors.feedErrors,
                 ...feed?.errors,
             } as Errors;
+            const hasFeedErrors = feedNameWithDomainID ? !isEmptyObject(feedErrors) : false;
 
             const hasFailedCardAssignments = !isEmptyObject(
                 failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
@@ -81,8 +82,6 @@ export default createOnyxDerivedValueConfig({
             } as Record<string, CardErrors>;
 
             const isFeedConnectionBroken = isCardConnectionBroken(card);
-            const hasFeedErrors = feedNameWithDomainID ? !!feedErrors : false;
-            const hasWorkspaceErrors = !!workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
 
             const newFeedState: Omit<CardFeedErrorState, 'shouldShowRBR'> = {
                 isFeedConnectionBroken: isFeedConnectionBroken || previousFeedErrors.isFeedConnectionBroken,

From ed31302e8ecca9e14bbe2f406f2c4a7d8fbb2f77 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 15:27:53 +0000
Subject: [PATCH 108/122] fix: tests

---
 .../unit/useAccountTabIndicatorStatusTest.ts  | 73 ++++++++++++-------
 tests/unit/useIndicatorStatusTest.tsx         | 72 +++++++++++++-----
 2 files changed, 101 insertions(+), 44 deletions(-)

diff --git a/tests/unit/useAccountTabIndicatorStatusTest.ts b/tests/unit/useAccountTabIndicatorStatusTest.ts
index d5b118320db9..0b6764b17a42 100644
--- a/tests/unit/useAccountTabIndicatorStatusTest.ts
+++ b/tests/unit/useAccountTabIndicatorStatusTest.ts
@@ -2,13 +2,39 @@ import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import useAccountTabIndicatorStatus from '@hooks/useAccountTabIndicatorStatus';
+import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
+import initOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
 import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
 
-const getMockForStatus = (status: string) =>
+type TestCase = {
+    name: string;
+    indicatorColor: string;
+    status: IndicatorStatus | undefined;
+};
+
+const cardFeed = {
+    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+    workspaceAccountID: 12345,
+};
+
+const accountCardFeedTestCases = {
+    admin: {
+        name: 'has no account card feed error if admin',
+        indicatorColor: defaultTheme.success,
+        status: undefined,
+    },
+    employee: {
+        name: 'has account card feed error if employee (non-admin)',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    },
+} as const satisfies Record<'admin' | 'employee', TestCase>;
+
+const getMockForStatus = ({status, name}: TestCase) =>
     ({
         [ONYXKEYS.BANK_ACCOUNT_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -82,29 +108,25 @@ const getMockForStatus = (status: string) =>
                       }
                     : undefined,
         },
+        [ONYXKEYS.SESSION]: {
+            email: 'johndoe12@expensify.com',
+        },
         [`${ONYXKEYS.CARD_LIST}`]: {
-            // eslint-disable-next-line @typescript-eslint/naming-convention
             card123: {
                 bank: 'OTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
-            },
-            // eslint-disable-next-line @typescript-eslint/naming-convention
-            card456: {
-                bank: 'ANOTHER_BANK',
-                lastScrapeResult: status === CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR ? 403 : 200,
+                lastScrapeResult: name === accountCardFeedTestCases.admin.name || name === accountCardFeedTestCases.employee.name ? 403 : 200,
+                fundID: String(cardFeed.workspaceAccountID),
             },
         },
-        [ONYXKEYS.SESSION]: {
-            email: 'johndoe12@expensify.com',
+        [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
+            id: '1',
+            name: 'Workspace 1',
+            owner: name === accountCardFeedTestCases.admin.name ? 'johndoe12@expensify.com' : 'otheruser@expensify.com',
+            role: name === accountCardFeedTestCases.admin.name ? 'admin' : 'user',
+            workspaceAccountID: cardFeed.workspaceAccountID,
         },
     }) as unknown as OnyxMultiSetInput;
 
-type TestCase = {
-    name: string;
-    indicatorColor: string;
-    status: string;
-};
-
 const TEST_CASES: TestCase[] = [
     {
         name: 'has user wallet errors',
@@ -131,11 +153,6 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.danger,
         status: CONST.INDICATOR_STATUS.HAS_WALLET_TERMS_ERRORS,
     },
-    {
-        name: 'has card connection error',
-        indicatorColor: defaultTheme.danger,
-        status: CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR,
-    },
     {
         name: 'has phone number error',
         indicatorColor: defaultTheme.danger,
@@ -146,6 +163,8 @@ const TEST_CASES: TestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO,
     },
+    accountCardFeedTestCases.employee,
+    accountCardFeedTestCases.admin,
 ];
 
 describe('useAccountTabIndicatorStatus', () => {
@@ -153,12 +172,13 @@ describe('useAccountTabIndicatorStatus', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
+        initOnyxDerivedValues();
     });
 
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
             await act(async () => {
-                await Onyx.multiSet(getMockForStatus(testCase.status));
+                await Onyx.multiSet(getMockForStatus(testCase));
                 await waitForBatchedUpdatesWithAct();
             });
         });
@@ -375,7 +395,8 @@ describe('useAccountTabIndicatorStatus', () => {
                 await Onyx.multiSet({
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
-                            bank: 'Chase',
+                            bank: cardFeed.feedName,
+                            fundID: String(cardFeed.workspaceAccountID),
                             lastScrapeResult: 403,
                         },
                     },
@@ -387,7 +408,7 @@ describe('useAccountTabIndicatorStatus', () => {
             await waitForBatchedUpdatesWithAct();
             const {status, indicatorColor} = result.current;
 
-            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_CARD_CONNECTION_ERROR);
+            expect(status).toBe(CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS);
             expect(indicatorColor).toBe(defaultTheme.danger);
         });
 
@@ -397,6 +418,7 @@ describe('useAccountTabIndicatorStatus', () => {
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
                             bank: CONST.EXPENSIFY_CARD.BANK,
+                            fundID: String(cardFeed.workspaceAccountID),
                             lastScrapeResult: 403,
                         },
                     },
@@ -416,7 +438,8 @@ describe('useAccountTabIndicatorStatus', () => {
                 await Onyx.multiSet({
                     [`${ONYXKEYS.CARD_LIST}`]: {
                         card1: {
-                            bank: 'Chase',
+                            bank: cardFeed.feedName,
+                            fundID: String(cardFeed.workspaceAccountID),
                             lastScrapeResult: 200,
                         },
                     },
diff --git a/tests/unit/useIndicatorStatusTest.tsx b/tests/unit/useIndicatorStatusTest.tsx
index 69f4ac51503a..84beb9ced5f1 100644
--- a/tests/unit/useIndicatorStatusTest.tsx
+++ b/tests/unit/useIndicatorStatusTest.tsx
@@ -1,21 +1,52 @@
 import {act, renderHook} from '@testing-library/react-native';
 import type {OnyxMultiSetInput} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
-import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 import useIndicatorStatus from '@hooks/useIndicatorStatus';
+import type {IndicatorStatus} from '@hooks/useIndicatorStatus';
 // eslint-disable-next-line no-restricted-imports
 import {defaultTheme} from '@styles/theme';
 import CONST from '@src/CONST';
+import initWithOnyxDerivedValues from '@src/libs/actions/OnyxDerived';
 import ONYXKEYS from '@src/ONYXKEYS';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 
-const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
+type TestCase = {
+    name: string;
+    indicatorColor: string;
+    status: IndicatorStatus | undefined;
+    policyIDWithErrors?: string;
+};
+
+const userID = 'johndoe12@expensify.com';
+const otherUserID = 'otheruser@expensify.com';
+
+const brokenCardFeed = {
+    feedName: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
+    workspaceAccountID: 12345,
+};
+
+const cardFeedErrorTestCases = {
+    admin: {
+        name: 'has policy card feed error if admin',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_POLICY_ADMIN_CARD_FEED_ERRORS,
+        policyIDWithErrors: '1',
+    },
+    employee: {
+        name: 'has account card feed error if employee (non-admin)',
+        indicatorColor: defaultTheme.danger,
+        status: CONST.INDICATOR_STATUS.HAS_EMPLOYEE_CARD_FEED_ERRORS,
+    },
+} as const satisfies Record<'admin' | 'employee', TestCase>;
+
+const getMockForStatus = ({status, name}: TestCase, isAdmin: boolean) =>
     ({
         [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: {
             id: '1',
             name: 'Workspace 1',
-            owner: 'johndoe12@expensify.com',
+            owner: isAdmin ? userID : otherUserID,
             role: isAdmin ? 'admin' : 'user',
+            workspaceAccountID: brokenCardFeed.workspaceAccountID,
             customUnits:
                 status === CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR
                     ? {
@@ -28,7 +59,7 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}2` as const]: {
             id: '2',
             name: 'Workspace 2',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'user',
             errors:
                 status === CONST.INDICATOR_STATUS.HAS_POLICY_ERRORS
@@ -40,12 +71,12 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}3` as const]: {
             id: '3',
             name: 'Workspace 3',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'user',
             employeeList: {
                 // eslint-disable-next-line @typescript-eslint/naming-convention
-                'johndoe12@expensify.com': {
-                    email: 'johndoe12@expensify.com',
+                [userID]: {
+                    email: userID,
                     errors:
                         status === CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR
                             ? {
@@ -58,7 +89,7 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         [`${ONYXKEYS.COLLECTION.POLICY}4` as const]: {
             id: '4',
             name: 'Workspace 4',
-            owner: 'johndoe12@expensify.com',
+            owner: userID,
             role: isAdmin ? 'admin' : 'auditor',
             connections:
                 status === CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS
@@ -103,9 +134,9 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         },
         [ONYXKEYS.LOGIN_LIST]: {
             // eslint-disable-next-line @typescript-eslint/naming-convention
-            'johndoe12@expensify.com': {
+            [userID]: {
                 partnerName: 'John Doe',
-                partnerUserID: 'johndoe12@expensify.com',
+                partnerUserID: userID,
                 validatedDate: status !== CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_INFO ? new Date().toISOString() : undefined,
                 errorFields:
                     status === CONST.INDICATOR_STATUS.HAS_LOGIN_LIST_ERROR
@@ -136,8 +167,14 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
                 cardID: 123456,
                 bank: CONST.EXPENSIFY_CARD.BANK,
                 accountID: 123,
+                fundID: String(brokenCardFeed.workspaceAccountID),
                 state: status === CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO ? CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED : CONST.EXPENSIFY_CARD.STATE.OPEN,
             },
+            card1: {
+                bank: 'OTHER_BANK',
+                lastScrapeResult: name === cardFeedErrorTestCases.admin.name || name === cardFeedErrorTestCases.employee.name ? 403 : 200,
+                fundID: String(brokenCardFeed.workspaceAccountID),
+            },
         },
         [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: {
             legalFirstName: 'John',
@@ -152,13 +189,6 @@ const getMockForStatus = (status: IndicatorStatus, isAdmin = true) =>
         },
     }) as OnyxMultiSetInput;
 
-type TestCase = {
-    name: string;
-    indicatorColor: string;
-    status: IndicatorStatus;
-    policyIDWithErrors?: string;
-};
-
 const TEST_CASES: TestCase[] = [
     {
         name: 'has policy errors',
@@ -238,6 +268,7 @@ const TEST_CASES: TestCase[] = [
         status: CONST.INDICATOR_STATUS.HAS_PENDING_CARD_INFO,
         policyIDWithErrors: undefined,
     },
+    cardFeedErrorTestCases.admin,
 ];
 
 const TEST_CASES_NON_ADMIN: TestCase[] = [
@@ -261,6 +292,7 @@ const TEST_CASES_NON_ADMIN: TestCase[] = [
         indicatorColor: defaultTheme.success,
         status: CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS,
     },
+    cardFeedErrorTestCases.employee,
 ];
 
 describe('useIndicatorStatusTest', () => {
@@ -268,10 +300,12 @@ describe('useIndicatorStatusTest', () => {
         Onyx.init({
             keys: ONYXKEYS,
         });
+        initWithOnyxDerivedValues();
     });
+
     describe.each(TEST_CASES)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase.status));
+            await Onyx.multiSet(getMockForStatus(testCase, true));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {
@@ -301,7 +335,7 @@ describe('useIndicatorStatusTest', () => {
     });
     describe.each(TEST_CASES_NON_ADMIN)('$name', (testCase) => {
         beforeAll(async () => {
-            await Onyx.multiSet(getMockForStatus(testCase.status, false));
+            await Onyx.multiSet(getMockForStatus(testCase, false));
             await waitForBatchedUpdates();
         });
         it('returns correct indicatorColor', async () => {

From 142da033ae9a2f55abd62f4eec5e1f07219f75bf Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 16:30:41 +0000
Subject: [PATCH 109/122] refactor: remove changes related to types and utils
 refactoring

---
 src/hooks/useCardsList.tsx                    |  7 +--
 src/hooks/useCompanyCards.ts                  |  2 +-
 .../OpenPolicyCompanyCardsFeedParams.ts       |  4 +-
 src/libs/CardUtils.ts                         | 52 ++++++-------------
 src/libs/ObjectUtils.ts                       |  1 +
 src/libs/PolicyUtils.ts                       | 13 +++--
 src/libs/actions/OnyxDerived/index.ts         | 12 ++---
 src/libs/actions/Policy/Member.ts             |  5 +-
 src/pages/RoomMemberDetailsPage.tsx           |  4 +-
 src/pages/RoomMembersPage.tsx                 |  4 +-
 .../WorkspaceCompanyCardFeedSelectorPage.tsx  |  4 +-
 src/types/onyx/Card.ts                        |  4 +-
 src/types/onyx/CardFeeds.ts                   | 50 ++++++------------
 tests/unit/CardUtilsTest.ts                   |  9 ++--
 14 files changed, 65 insertions(+), 106 deletions(-)

diff --git a/src/hooks/useCardsList.tsx b/src/hooks/useCardsList.tsx
index 023f61623d40..1a65b7a033c5 100644
--- a/src/hooks/useCardsList.tsx
+++ b/src/hooks/useCardsList.tsx
@@ -1,13 +1,14 @@
 import type {ResultMetadata} from 'react-native-onyx';
-import {filterInactiveCards, splitCompanyCardFeedWithDomainID} from '@libs/CardUtils';
+import {filterInactiveCards} from '@libs/CardUtils';
+import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CompanyCardFeedWithDomainID, WorkspaceCardsList} from '@src/types/onyx';
 import useOnyx from './useOnyx';
 
 /* Custom hook that retrieves a list of company cards for the given selected feed. */
 const useCardsList = (selectedFeed: CompanyCardFeedWithDomainID | undefined): [WorkspaceCardsList | undefined, ResultMetadata<WorkspaceCardsList>] => {
-    const {feedName, domainID} = splitCompanyCardFeedWithDomainID(selectedFeed);
-    const [cardsList, cardsListMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainID}_${feedName}`, {
+    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/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 278683d063a5..fc469ed09c4c 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -21,7 +21,7 @@ type UseCompanyCardsProps = {
 
 type UseCompanyCardsResult = Partial<{
     cardFeedType: CardFeedType;
-    bankName: CompanyCardFeedWithNumber;
+    bankName: CompanyCardFeed;
     feedName: CompanyCardFeedWithDomainID;
     cardList: AssignableCardsList;
     assignedCards: CardList;
diff --git a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
index 0aa6c93e5a42..85c881baf55b 100644
--- a/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
+++ b/src/libs/API/parameters/OpenPolicyCompanyCardsFeedParams.ts
@@ -1,9 +1,7 @@
-import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
-
 type OpenPolicyCompanyCardsFeedParams = {
     domainAccountID?: number;
     policyID: string;
-    feed: CompanyCardFeedWithNumber;
+    feed: string;
 };
 
 export default OpenPolicyCompanyCardsFeedParams;
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 41f0e39e8ec6..ed1bbceb9d9d 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -23,7 +23,7 @@ import type {
     WorkspaceCardsList,
 } from '@src/types/onyx';
 import type {FilteredCardList} from '@src/types/onyx/Card';
-import type {CardFeedData, CardFeedWithDomainID, CompanyCardFeedWithDomainID, 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
@@ -344,11 +344,7 @@ function filterCardsByPersonalDetails(card: Card, searchQuery: string, personalD
     );
 }
 
-function getCardFeedIcon(
-    cardFeed: CompanyCardFeedWithNumber | typeof CONST.EXPENSIFY_CARD.BANK,
-    illustrations: IllustrationsType,
-    companyCardIllustrations: CompanyCardFeedIcons,
-): IconAsset {
+function getCardFeedIcon(cardFeed: CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK, illustrations: IllustrationsType, companyCardIllustrations: CompanyCardFeedIcons): IconAsset {
     const feedIcons = {
         [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: companyCardIllustrations.VisaCompanyCardDetailLarge,
         [CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX]: companyCardIllustrations.AmexCardCompanyCardDetailLarge,
@@ -372,9 +368,8 @@ function getCardFeedIcon(
         return Illustrations.ExpensifyCardImage;
     }
 
-    const feedIcon = feedIcons[cardFeed as CompanyCardFeed];
-    if (feedIcon) {
-        return feedIcon;
+    if (feedIcons[cardFeed]) {
+        return feedIcons[cardFeed];
     }
 
     // In existing OldDot setups other variations of feeds could exist, ex: vcf2, vcf3, cdfbmo
@@ -394,7 +389,7 @@ function getCardFeedIcon(
 /**
  * Verify if the feed is a custom feed. Those are also referred to as commercial feeds.
  */
-function isCustomFeed(feed: CompanyCardFeedWithNumber | CompanyCardFeedWithDomainID | undefined): boolean {
+function isCustomFeed(feed: CompanyCardFeedWithNumber | undefined): boolean {
     if (!feed) {
         return false;
     }
@@ -405,7 +400,7 @@ function isCustomFeed(feed: CompanyCardFeedWithNumber | CompanyCardFeedWithDomai
 function getOriginalCompanyFeeds(cardFeeds: OnyxEntry<CardFeeds>): CompanyFeeds {
     return Object.fromEntries(
         Object.entries(cardFeeds?.settings?.companyCards ?? {}).filter(([key, value]) => {
-            if (value?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || value?.pending) {
+            if (value.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || value.pending) {
                 return false;
             }
             return key !== CONST.EXPENSIFY_CARD.BANK;
@@ -427,7 +422,7 @@ function getCompanyFeeds(cardFeeds: OnyxEntry<CombinedCardFeeds>, shouldFilterOu
     );
 }
 
-function getBankName(feedType: CompanyCardFeedWithNumber): string {
+function getBankName(feedType: CompanyCardFeed): string {
     const feedNamesMapping = {
         [CONST.COMPANY_CARD.FEED_BANK_NAME.VISA]: 'Visa',
         [CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD]: 'Mastercard',
@@ -475,7 +470,7 @@ const getBankCardDetailsImage = (bank: ValueOf<typeof CONST.COMPANY_CARDS.BANKS>
     return iconMap[bank];
 };
 
-function getCustomOrFormattedFeedName(feed?: CompanyCardFeedWithNumber, customFeedName?: string, shouldAddCardsSuffix = true): string | undefined {
+function getCustomOrFormattedFeedName(feed?: CompanyCardFeed, customFeedName?: string, shouldAddCardsSuffix = true): string | undefined {
     if (!feed) {
         return;
     }
@@ -557,24 +552,10 @@ function getSelectedFeed(lastSelectedFeed: OnyxEntry<CompanyCardFeedWithDomainID
     return isValidLastFeed ? lastSelectedFeed : defaultFeed;
 }
 
-function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeedWithNumber, domainID: number | string): CompanyCardFeedWithDomainID {
+function getCompanyCardFeedWithDomainID(feedName: CompanyCardFeed, domainID: number | string): CompanyCardFeedWithDomainID {
     return `${feedName}${CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${domainID}`;
 }
 
-function splitCompanyCardFeedWithDomainID(feedName: CardFeedWithDomainID | undefined): {feedName: CompanyCardFeed | undefined; domainID: number | undefined} {
-    if (!feedName) {
-        return {feedName: undefined, domainID: undefined};
-    }
-
-    const feedNameParts = feedName.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR);
-
-    if (feedNameParts.length !== 2) {
-        return {feedName: feedName as CompanyCardFeed, domainID: undefined};
-    }
-
-    return {feedName: feedNameParts.at(0) as CompanyCardFeed, domainID: Number(feedNameParts.at(1))};
-}
-
 function isSelectedFeedExpired(cardFeed: CombinedCardFeed | undefined): boolean {
     return cardFeed?.expiration ? isBefore(fromUnixTime(cardFeed.expiration), new Date()) : false;
 }
@@ -725,14 +706,15 @@ function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
  * @param workspaceAccountID the workspace account id we want to get cards for
  * @param domainIDs the domain ids we want to get cards for
  */
-function flattenCompanyCards(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number): CardList | undefined {
+function flatAllCardsList(allCardsList: OnyxCollection<WorkspaceCardsList>, workspaceAccountID: number, domainIDs?: number[]): Record<string, Card> | undefined {
     if (!allCardsList) {
         return;
     }
 
     return Object.entries(allCardsList).reduce((acc, [key, cards]) => {
-        const isWorkspaceAccountCard = key.includes(workspaceAccountID.toString());
-        if (!isWorkspaceAccountCard || key.includes(CONST.EXPENSIFY_CARD.BANK)) {
+        const isWorkspaceAccountCards = key.includes(workspaceAccountID.toString());
+        const isDomainCards = domainIDs?.some((domainID) => key.includes(domainID.toString()));
+        if ((!isWorkspaceAccountCards && !isDomainCards) || key.includes(CONST.EXPENSIFY_CARD.BANK)) {
             return acc;
         }
         const {cardList, ...feedCards} = cards ?? {};
@@ -834,8 +816,8 @@ function getCompanyCardFeed(feedWithDomainID: string | undefined): CompanyCardFe
     if (!feedWithDomainID) {
         return '' as CompanyCardFeed;
     }
-
-    return splitCompanyCardFeedWithDomainID(feedWithDomainID as CompanyCardFeedWithDomainID).feedName ?? ('' as CompanyCardFeed);
+    const [feed] = feedWithDomainID.split(CONST.COMPANY_CARD.FEED_KEY_SEPARATOR);
+    return feed as CompanyCardFeed;
 }
 
 /**
@@ -976,12 +958,11 @@ export {
     isCardHiddenFromSearch,
     getFeedType,
     flattenCompanyCards,
-    isCardConnectionBroken,
+    checkIfFeedConnectionIsBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
     hasIssuedExpensifyCard,
     isExpensifyCardFullySetUp,
-    filterAllInactiveCards,
     filterInactiveCards,
     isCardPendingIssue,
     isCardPendingActivate,
@@ -999,7 +980,6 @@ export {
     getOriginalCompanyFeeds,
     getCompanyCardFeed,
     getCompanyCardFeedWithDomainID,
-    splitCompanyCardFeedWithDomainID,
     getEligibleBankAccountsForUkEuCard,
     filterPersonalCards,
     isPersonalCard,
diff --git a/src/libs/ObjectUtils.ts b/src/libs/ObjectUtils.ts
index 1f3fe18fb153..0a23693f2ba7 100644
--- a/src/libs/ObjectUtils.ts
+++ b/src/libs/ObjectUtils.ts
@@ -28,4 +28,5 @@ function filterObject<TObject extends Record<string, unknown>>(obj: TObject, pre
         }, {} as TObject);
 }
 
+// eslint-disable-next-line import/prefer-default-export
 export {shallowCompare, filterObject};
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index e14926105e39..3cfb00a33fbb 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -110,8 +110,7 @@ function getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(policies:
 /**
  * Checks if the current user is an admin of the policy.
  */
-const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string, shouldCheckGlobalPolicyRole = true): boolean =>
-    getPolicyRole(policy, login, shouldCheckGlobalPolicyRole) === CONST.POLICY.ROLE.ADMIN;
+const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;
 
 /**
  * Checks if we have any errors stored within the policy?.employeeList. Determines whether we should show a red brick road error or not.
@@ -350,8 +349,8 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>, isConnecti
     return undefined;
 }
 
-function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string, shouldCheckGlobalPolicyRole = true): string | undefined {
-    if (shouldCheckGlobalPolicyRole && policy?.role) {
+function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin: string | undefined): string | undefined {
+    if (policy?.role) {
         return policy.role;
     }
 
@@ -429,6 +428,11 @@ function isExpensifyTeam(email: string | undefined): boolean {
     return emailDomain === CONST.EXPENSIFY_PARTNER_NAME || emailDomain === CONST.EMAIL.GUIDES_DOMAIN;
 }
 
+/**
+ * Checks if the user with login is an admin of the policy.
+ */
+const isUserPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, login?: string) => !!(policy && policy.employeeList && login && policy.employeeList[login]?.role === CONST.POLICY.ROLE.ADMIN);
+
 /**
  * Checks if the current user is of the role "user" on the policy.
  */
@@ -1726,6 +1730,7 @@ export {
     getCorrectedAutoReportingFrequency,
     isPaidGroupPolicy,
     isPendingDeletePolicy,
+    isUserPolicyAdmin,
     isPolicyAdmin,
     isPolicyUser,
     isPolicyAuditor,
diff --git a/src/libs/actions/OnyxDerived/index.ts b/src/libs/actions/OnyxDerived/index.ts
index 84b0795fd8c5..6b1aca2fb4da 100644
--- a/src/libs/actions/OnyxDerived/index.ts
+++ b/src/libs/actions/OnyxDerived/index.ts
@@ -88,10 +88,6 @@ function init() {
                 setDerivedValue(key, derivedValue);
             };
 
-            function logDependencyChange(dependencyOnyxKey: string) {
-                Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
-            }
-
             for (let i = 0; i < dependencies.length; i++) {
                 const dependencyIndex = i;
                 const dependencyOnyxKey = dependencies[dependencyIndex];
@@ -100,8 +96,8 @@ function init() {
                     Onyx.connectWithoutView({
                         key: dependencyOnyxKey,
                         waitForCollectionCallback: true,
-                        callback: (value, _collectionKey, sourceValue) => {
-                            logDependencyChange(dependencyOnyxKey);
+                        callback: (value, collectionKey, sourceValue) => {
+                            Log.info(`[OnyxDerived] dependency ${collectionKey} for derived key ${key} changed, recomputing`);
                             setDependencyValue(dependencyIndex, value as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             recomputeDerivedValue(dependencyOnyxKey, sourceValue, dependencyIndex);
                         },
@@ -122,7 +118,7 @@ function init() {
                                 Log.info(`[OnyxDerived] No locale found for derived key ${key}, skipping recompute`);
                                 return;
                             }
-                            logDependencyChange(dependencyOnyxKey);
+                            Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
                             setDependencyValue(dependencyIndex, localeValue as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             recomputeDerivedValue(dependencyOnyxKey, localeValue, dependencyIndex);
                         },
@@ -131,7 +127,7 @@ function init() {
                     Onyx.connectWithoutView({
                         key: dependencyOnyxKey,
                         callback: (value) => {
-                            logDependencyChange(dependencyOnyxKey);
+                            Log.info(`[OnyxDerived] dependency ${dependencyOnyxKey} for derived key ${key} changed, recomputing`);
                             setDependencyValue(dependencyIndex, value as Parameters<typeof compute>[0][typeof dependencyIndex]);
                             // if the dependency is not a collection, pass the entire value as the source value
                             recomputeDerivedValue(dependencyOnyxKey, value, dependencyIndex);
diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts
index f5ce0736f562..fb87773e1fee 100644
--- a/src/libs/actions/Policy/Member.ts
+++ b/src/libs/actions/Policy/Member.ts
@@ -22,7 +22,7 @@ import Parser from '@libs/Parser';
 import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
 import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
 import * as PhoneNumber from '@libs/PhoneNumber';
-import {getDefaultApprover, isPolicyAdmin} from '@libs/PolicyUtils';
+import {getDefaultApprover, isUserPolicyAdmin} from '@libs/PolicyUtils';
 import * as ReportActionsUtils from '@libs/ReportActionsUtils';
 import * as ReportUtils from '@libs/ReportUtils';
 import * as FormActions from '@userActions/FormActions';
@@ -270,8 +270,7 @@ function resetAccountingPreferredExporter(policy: OnyxEntry<Policy>, loginList:
     }
     const owner = policy?.owner ?? ReportUtils.getPersonalDetailsForAccountID(policy?.ownerAccountID).login ?? '';
     const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policy.id}` as const;
-
-    const adminLoginList = loginList.filter((login) => isPolicyAdmin(policy, login, false));
+    const adminLoginList = loginList.filter((login) => isUserPolicyAdmin(policy, login));
     const connections = [CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.POLICY.CONNECTIONS.NAME.QBD];
 
     if (!adminLoginList.length) {
diff --git a/src/pages/RoomMemberDetailsPage.tsx b/src/pages/RoomMemberDetailsPage.tsx
index 64f6076945e1..0e6abaa99318 100644
--- a/src/pages/RoomMemberDetailsPage.tsx
+++ b/src/pages/RoomMemberDetailsPage.tsx
@@ -18,7 +18,7 @@ import {removeFromRoom} from '@libs/actions/Report';
 import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
 import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types';
 import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
-import {isPolicyAdmin} from '@libs/PolicyUtils';
+import {isUserPolicyAdmin} from '@libs/PolicyUtils';
 import {isPolicyExpenseChat} from '@libs/ReportUtils';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
@@ -52,7 +52,7 @@ function RoomMemberDetailsPage({report, route}: RoomMemberDetailsPagePageProps)
     const displayName = formatPhoneNumber(getDisplayNameOrDefault(details));
     const isSelectedMemberCurrentUser = accountID === currentUserPersonalDetails?.accountID;
     const isSelectedMemberOwner = accountID === report.ownerAccountID;
-    const shouldDisableRemoveUser = (isPolicyExpenseChat(report) && isPolicyAdmin(policy, details.login)) || isSelectedMemberCurrentUser || isSelectedMemberOwner;
+    const shouldDisableRemoveUser = (isPolicyExpenseChat(report) && isUserPolicyAdmin(policy, details.login)) || isSelectedMemberCurrentUser || isSelectedMemberOwner;
     const removeUser = () => {
         setIsRemoveMemberConfirmModalVisible(false);
         removeFromRoom(report?.reportID, [accountID]);
diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx
index ede169f9730c..ab569650ae08 100644
--- a/src/pages/RoomMembersPage.tsx
+++ b/src/pages/RoomMembersPage.tsx
@@ -36,7 +36,7 @@ import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types';
 import {isPersonalDetailsReady, isSearchStringMatchUserDetails} from '@libs/OptionsListUtils';
 import Parser from '@libs/Parser';
 import {getDisplayNameOrDefault, getPersonalDetailsByIDs} from '@libs/PersonalDetailsUtils';
-import {isPolicyAdmin, isPolicyEmployee as isPolicyEmployeeUtils} from '@libs/PolicyUtils';
+import {isPolicyEmployee as isPolicyEmployeeUtils, isUserPolicyAdmin} from '@libs/PolicyUtils';
 import {getReportAction} from '@libs/ReportActionsUtils';
 import {
     getReportForHeader,
@@ -269,7 +269,7 @@ function RoomMembersPage({report, policy}: RoomMembersPageProps) {
                 continue;
             }
             const pendingChatMember = reportMetadata?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString());
-            const isAdmin = isPolicyAdmin(policy, details.login);
+            const isAdmin = isUserPolicyAdmin(policy, details.login);
             const isDisabled = pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || details.isOptimisticPersonalDetail;
             const isDisabledCheckbox =
                 (isPolicyExpenseChat && isAdmin) ||
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 5536c7266c30..1769a5f45f3b 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -31,14 +31,14 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
-import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CompanyCardFeed} from '@src/types/onyx';
 
 type CardFeedListItem = ListItem & {
     /** Combined feed key */
     value: CompanyCardFeedWithDomainID;
 
     /** Card feed value */
-    feed: CompanyCardFeedWithNumber;
+    feed: CompanyCardFeed;
 };
 
 type WorkspaceCompanyCardFeedSelectorPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED>;
diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index 85b3ec1fbd54..05e10fe6518f 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -1,6 +1,6 @@
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
-import type {CardFeedWithDomainID} from './CardFeeds';
+import type {CompanyCardFeedWithDomainID} from './CardFeeds';
 import type * as OnyxCommon from './OnyxCommon';
 import type PersonalDetails from './PersonalDetails';
 
@@ -298,7 +298,7 @@ type FailedCompanyCardAssignment = {
     domainOrWorkspaceAccountID: number;
 
     /** The name of the feed */
-    feed: CardFeedWithDomainID;
+    feed: CompanyCardFeedWithDomainID;
 
     /** Cardholder personal details */
     cardholder?: PersonalDetails;
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 1d517c1defdb..2d6aa6b060d6 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -4,33 +4,14 @@ import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
 import type * as OnyxCommon from './OnyxCommon';
 
-/** Company card feed name */
+/** Card feed */
 type CompanyCardFeed = ValueOf<typeof CONST.COMPANY_CARD.FEED_BANK_NAME>;
 
-/** Company card feed name with a number */
-type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}`;
+/** Company card feed with domain ID */
+type CompanyCardFeedWithDomainID = `${CompanyCardFeed}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
 
-/** Company card feed name with domain ID */
-type CompanyCardFeedWithDomainID = `${CompanyCardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
-
-/**
- * Either a company card feed name or the Expensify card bank name.
- */
-type CardFeed = CompanyCardFeed | typeof CONST.EXPENSIFY_CARD.BANK;
-
-/**
- * Either a company card feed name or the Expensify card bank name with a number.
- */
-type CardFeedWithNumber = CardFeed | `${CardFeed}${number}`;
-
-/**
- * Card feed name with domain ID
- */
-type CardFeedWithDomainID = `${CardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
-
-/**
- * Either a company card feed name with domain ID or the Expensify card bank name with domain ID.
-type CardFeedWithDomainID = `${CardFeedWithNumber}${typeof CONST.COMPANY_CARD.FEED_KEY_SEPARATOR}${string}`;
+/** Custom card feed with a number */
+type CompanyCardFeedWithNumber = CompanyCardFeed | `${CompanyCardFeed}${number}` | CompanyCardFeedWithDomainID;
 
 /** Statement period end */
 type StatementPeriodEnd = Exclude<ValueOf<typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE>, typeof CONST.COMPANY_CARDS.STATEMENT_CLOSE_DATE.CUSTOM_DAY_OF_MONTH>;
@@ -150,10 +131,10 @@ type DirectCardFeedData = OnyxCommon.OnyxValueWithOfflineFeedback<{
 type CardFeedData = CustomCardFeedData | DirectCardFeedData;
 
 /** Both custom and direct company feeds */
-type CompanyFeeds = Partial<Record<CardFeed, CardFeedData>>;
+type CompanyFeeds = Partial<Record<CompanyCardFeed, CardFeedData>>;
 
 /** Custom feed names */
-type CompanyCardNicknames = Partial<Record<CompanyCardFeedWithNumber, string>>;
+type CompanyCardNicknames = Partial<Record<CompanyCardFeed, string>>;
 
 /** Domain settings model */
 type DomainSettings = {
@@ -185,10 +166,10 @@ type CardFeeds = {
         companyCardNicknames?: CompanyCardNicknames;
 
         /** Company cards feeds */
-        companyCards?: Partial<Record<CompanyCardFeedWithNumber, CustomCardFeedData>>;
+        companyCards?: Partial<Record<CompanyCardFeed, CustomCardFeedData>>;
 
         /** Account details */
-        oAuthAccountDetails?: Partial<Record<CompanyCardFeedWithNumber, DirectCardFeedData>>;
+        oAuthAccountDetails?: Partial<Record<CompanyCardFeed, DirectCardFeedData>>;
 
         /** Email address of the technical contact for the domain */
         technicalContactEmail?: string;
@@ -196,8 +177,10 @@ type CardFeeds = {
         /** Whether to use the technical contact's billing card */
         useTechnicalContactBillingCard?: boolean;
     };
-} &
-    DomainSettings;
+
+    /** Whether we are loading the data via the API */
+    isLoading?: boolean;
+} & DomainSettings;
 
 /** Data required to be sent to add a new card */
 type AddNewCardFeedData = {
@@ -280,19 +263,16 @@ export type {
     AddNewCardFeedStep,
     AddNewCompanyCardFeed,
     AddNewCardFeedData,
-    CardFeed,
-    CardFeedWithNumber,
-    CardFeedWithDomainID,
     CompanyCardFeed,
-    CompanyCardFeedWithNumber,
-    CompanyCardFeedWithDomainID,
     CardFeedDetails,
     DirectCardFeedData,
     CardFeedProvider,
     CardFeedData,
     CompanyFeeds,
+    CompanyCardFeedWithDomainID,
     CustomCardFeedData,
     CompanyCardNicknames,
+    CompanyCardFeedWithNumber,
     FundID,
     StatementPeriodEnd,
     StatementPeriodEndDay,
diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index ff1ec7f29da2..43456dc5fda3 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -8,7 +8,7 @@ import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import IntlStore from '@src/languages/IntlStore';
 import {
     filterInactiveCards,
-    flattenCompanyCards,
+    flatAllCardsList,
     formatCardExpiration,
     getAllCardsForWorkspace,
     getAssignedCardSortKey,
@@ -38,7 +38,6 @@ import {
     lastFourNumbersFromCardName,
     maskCardNumber,
     sortCardsByCardholderName,
-    splitCompanyCardFeedWithDomainID,
     splitMaskedCardNumber,
 } from '@src/libs/CardUtils';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx';
@@ -868,10 +867,10 @@ describe('CardUtils', () => {
         });
     });
 
-    describe('flattenCompanyCards', () => {
+    describe('flatAllCardsList', () => {
         it('should return the flattened list of non-Expensify cards related to the provided workspaceAccountID', () => {
             const workspaceAccountID = 11111111;
-            const flattenedCardsList = flattenCompanyCards(allCardsList, workspaceAccountID);
+            const flattenedCardsList = flatAllCardsList(allCardsList, workspaceAccountID);
             const {cardList, ...customCards} = customFeedCardsList;
             expect(flattenedCardsList).toStrictEqual({
                 ...directFeedCardsMultipleList,
@@ -881,7 +880,7 @@ describe('CardUtils', () => {
 
         it('should return undefined if not defined cards list was provided', () => {
             const workspaceAccountID = 11111111;
-            const flattenedCardsList = flattenCompanyCards(undefined, workspaceAccountID);
+            const flattenedCardsList = flatAllCardsList(undefined, workspaceAccountID);
             expect(flattenedCardsList).toBeUndefined();
         });
     });

From 4e9d205c730f373824f0136297d0cad4937178e2 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 16:42:45 +0000
Subject: [PATCH 110/122] refactor: types and utils after extracting code

---
 src/hooks/useCompanyCards.ts                    |  4 ++--
 src/libs/CardFeedUtils.ts                       |  4 ++--
 src/libs/CardUtils.ts                           | 10 +++++-----
 src/pages/settings/Wallet/PaymentMethodList.tsx |  3 +--
 src/types/onyx/CardFeeds.ts                     |  6 +-----
 src/types/onyx/index.ts                         | 14 +-------------
 6 files changed, 12 insertions(+), 29 deletions(-)

diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index fc469ed09c4c..7975b56f90ec 100644
--- a/src/hooks/useCompanyCards.ts
+++ b/src/hooks/useCompanyCards.ts
@@ -5,7 +5,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {CardFeeds, CardList} from '@src/types/onyx';
 import type {AssignableCardsList, WorkspaceCardsList} from '@src/types/onyx/Card';
-import type {CombinedCardFeeds, CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 import useCardFeeds from './useCardFeeds';
 import type {CombinedCardFeed} from './useCardFeeds';
@@ -48,7 +48,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
     const policyIDKey = policyID || CONST.DEFAULT_MISSING_ID;
 
     const [lastSelectedFeed, lastSelectedFeedMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyIDKey}`, {canBeMissing: true});
-    const [allCardFeeds, allCardFeedsMetadata, ] = useCardFeeds(policyID);
+    const [allCardFeeds, allCardFeedsMetadata] = useCardFeeds(policyID);
 
     const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);
     const bankName = feedName ? getCompanyCardFeed(feedName) : undefined;
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 0fa3a43a20d1..55c10761a492 100644
--- a/src/libs/CardFeedUtils.ts
+++ b/src/libs/CardFeedUtils.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import type {CombinedCardFeeds} from '@src/hooks/useCardFeeds';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
-import type {CombinedCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CombinedCardFeed} from '@src/types/onyx/CardFeeds';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
 import {
     getBankName,
@@ -522,7 +522,7 @@ function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection<CardFeeds> |
             return acc;
         }
 
-        for (const feedName of Object.keys(companyCards) as CompanyCardFeedWithNumber[]) {
+        for (const feedName of Object.keys(companyCards) as CompanyCardFeed[]) {
             const feedSettings = companyCards?.[feedName];
             const oAuthAccountDetails = workspaceFeedsSettings?.oAuthAccountDetails?.[feedName];
             const customFeedName = workspaceFeedsSettings?.companyCardNicknames?.[feedName];
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index ed1bbceb9d9d..a7f81cb8f458 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -638,9 +638,9 @@ function getAllCardsForWorkspace(
 ): CardList {
     const cards: CardList = {};
     const companyCardsDomainFeeds = Object.entries(cardFeeds ?? {}).map(([feedName, feedData]) => ({domainID: feedData.domainID, feedName}));
-    const expensifyCardFeedNames = Object.keys(expensifyCardSettings ?? {}) as CompanyCardFeedWithDomainID[];
-
-    const expensifyCardsDomainIDs = expensifyCardFeedNames.map((key) => splitCompanyCardFeedWithDomainID(key).domainID).filter((id) => !!id) as number[];
+    const expensifyCardsDomainIDs = Object.keys(expensifyCardSettings ?? {})
+        .map((key) => key.split('_').at(-1))
+        .filter((id): id is string => !!id);
 
     for (const [key, values] of Object.entries(allCardList ?? {})) {
         const isWorkspaceAccountCards = workspaceAccountID !== CONST.DEFAULT_NUMBER_ID && key.includes(workspaceAccountID.toString());
@@ -957,8 +957,8 @@ export {
     getAllCardsForWorkspace,
     isCardHiddenFromSearch,
     getFeedType,
-    flattenCompanyCards,
-    checkIfFeedConnectionIsBroken,
+    flatAllCardsList,
+    isCardConnectionBroken,
     isSmartLimitEnabled,
     lastFourNumbersFromCardName,
     hasIssuedExpensifyCard,
diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx
index 63426cdc0d72..2ddcf6dd0788 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.tsx
+++ b/src/pages/settings/Wallet/PaymentMethodList.tsx
@@ -39,7 +39,6 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type {BankAccount, BankAccountList, CardList, CompanyCardFeed} from '@src/types/onyx';
-import type {CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type PaymentMethod from '@src/types/onyx/PaymentMethod';
 import {getEmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject';
 import type IconAsset from '@src/types/utils/IconAsset';
@@ -200,7 +199,7 @@ function PaymentMethodList({
 
                 let shouldShowRBR = false;
                 if (card.fundID) {
-                    const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeedWithNumber, card.fundID);
+                    const feedNameWithDomainID = getCompanyCardFeedWithDomainID(card.bank as CompanyCardFeed, card.fundID);
                     shouldShowRBR = shouldShowRbrForFeedNameWithDomainID[feedNameWithDomainID];
                 } else {
                     shouldShowRBR = true;
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index 2d6aa6b060d6..b566c1540010 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -152,12 +152,8 @@ type DomainSettings = {
         /** Email to primary contact from the domain */
         technicalContactEmail?: string;
     };
-
-    /** Whether we are loading the data via the API */
-    isLoading?: boolean;
 };
 
-
 /** Card feeds model, including domain settings */
 type CardFeeds = {
     /** Feed settings */
@@ -256,7 +252,7 @@ type CombinedCardFeed = CustomCardFeedData &
     };
 
 /** Card feeds combined by domain ID into one object */
-type CombinedCardFeeds = Record<CardFeedWithDomainID, CombinedCardFeed>;
+type CombinedCardFeeds = Record<CompanyCardFeedWithDomainID, CombinedCardFeed>;
 
 export default CardFeeds;
 export type {
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index be2c5db80824..698337f0a3f3 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -19,17 +19,7 @@ import type Card from './Card';
 import type {CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card';
 import type CardContinuousReconciliation from './CardContinuousReconciliation';
 import type CardFeeds from './CardFeeds';
-import type {
-    AddNewCompanyCardFeed,
-    CardFeed,
-    CardFeedWithDomainID,
-    CombinedCardFeed,
-    CombinedCardFeeds,
-    CompanyCardFeed,
-    CompanyCardFeedWithDomainID,
-    DomainSettings,
-    FundID,
-} from './CardFeeds';
+import type {AddNewCompanyCardFeed, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID} from './CardFeeds';
 import type CardOnWaitlist from './CardOnWaitlist';
 import type {CapturedLogs, Log} from './Console';
 import type {CorpayFields, CorpayFormField} from './CorpayFields';
@@ -198,8 +188,6 @@ export type {
     CombinedCardFeeds,
     CardContinuousReconciliation,
     CompanyCardFeedWithDomainID,
-    CardFeed,
-    CardFeedWithDomainID,
     LastExportMethod,
     Locale,
     LockAccountDetails,

From 3cd273440bf7232b671a4c5f2d10fab68e62c2a2 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 16:57:04 +0000
Subject: [PATCH 111/122] fix: tests and type errors

---
 src/hooks/useCardFeedErrors.ts                |  2 +-
 .../OnyxDerived/configs/cardFeedErrors.ts     |  6 +-
 src/types/onyx/DerivedValues.ts               | 12 ++-
 tests/unit/CardFeedErrorsDerivedValueTest.ts  | 77 +++++++++----------
 tests/unit/CardUtilsTest.ts                   |  9 ---
 5 files changed, 50 insertions(+), 56 deletions(-)

diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index ca189e241836..13148d61586d 100644
--- a/src/hooks/useCardFeedErrors.ts
+++ b/src/hooks/useCardFeedErrors.ts
@@ -6,7 +6,7 @@ const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
     shouldShowRBR: false,
     isFeedConnectionBroken: false,
     hasFeedErrors: false,
-    hasWorkspaceErrors: false,
+    // hasWorkspaceErrors: false,
     hasFailedCardAssignments: false,
 };
 
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index ffd43fd3b060..98b18acd3307 100644
--- a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
+++ b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
@@ -4,7 +4,7 @@ import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDer
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Card} from '@src/types/onyx';
-import type {CardFeedWithNumber, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
+import type {CompanyCardFeed, CompanyCardFeedWithNumber} from '@src/types/onyx/CardFeeds';
 import type {CardErrors, CardFeedErrorsObject, CardFeedErrorState} from '@src/types/onyx/DerivedValues';
 import type {Errors} from '@src/types/onyx/OnyxCommon';
 import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -43,7 +43,7 @@ export default createOnyxDerivedValueConfig({
         const cardsWithBrokenFeedConnection: Record<string, Card> = {};
 
         function addErrorsForCard(card: Card) {
-            const bankName = card.bank as CardFeedWithNumber;
+            const bankName = card.bank as CompanyCardFeedWithNumber | typeof CONST.EXPENSIFY_CARD.BANK;
             const workspaceAccountID = Number(card.fundID);
 
             const isExpensifyCard = bankName === CONST.EXPENSIFY_CARD.BANK;
@@ -52,7 +52,7 @@ export default createOnyxDerivedValueConfig({
                 return;
             }
 
-            const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeedWithNumber, workspaceAccountID);
+            const feedNameWithDomainID = getCompanyCardFeedWithDomainID(bankName as CompanyCardFeed, workspaceAccountID);
             const previousFeedErrors = cardFeedErrors[feedNameWithDomainID] ?? DEFAULT_CARD_FEED_ERROR_STATE;
 
             const feed = combinedCompanyCardFeeds?.[feedNameWithDomainID];
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index d7cbfc38a6cd..7ad3c6f1e91a 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -1,8 +1,8 @@
 import type {OnyxCollection} from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import type CONST from '@src/CONST';
-import type {Card, CardFeed} from '.';
-import type {CardFeedWithDomainID, CompanyCardFeedWithNumber} from './CardFeeds';
+import type {Card} from '.';
+import type {CompanyCardFeedWithDomainID, CompanyCardFeedWithNumber} from './CardFeeds';
 import type {Errors} from './OnyxCommon';
 import type Report from './Report';
 import type Transaction from './Transaction';
@@ -111,6 +111,10 @@ type CardFeedErrorState = {
      * Whether a specific feed within a workspace/domain has errors.
      */
     hasFeedErrors: boolean;
+    /**
+     * Whether some workspace has errors.
+     */
+    // hasWorkspaceErrors: boolean;
     /**
      * Whether some feed connection is broken.
      */
@@ -134,7 +138,7 @@ type FeedErrors = CardFeedErrorState & {
 /**
  * The ID of a card feed in the errors map/object.
  */
-type CardFeedId = CardFeed | CompanyCardFeedWithNumber;
+type CardFeedId = CompanyCardFeedWithNumber;
 
 /**
  *
@@ -144,7 +148,7 @@ type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
 /**
  * The errors of all card feeds.
  */
-type CardFeedErrorsObject = Record<CardFeedWithDomainID, FeedErrors>;
+type CardFeedErrorsObject = Record<CompanyCardFeedWithDomainID, FeedErrors>;
 
 /**
  * The errors of card feeds.
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index e32036123e08..2d6885d844b4 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -4,8 +4,7 @@ import {getCompanyCardFeedWithDomainID} from '@libs/CardUtils';
 import CONST from '@src/CONST';
 import cardFeedErrorsConfig from '@src/libs/actions/OnyxDerived/configs/cardFeedErrors';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Card, CardFeeds, CardList, FailedCompanyCardAssignments, WorkspaceCardsList} from '@src/types/onyx';
-import type {CardFeed, CardFeedWithDomainID} from '@src/types/onyx/CardFeeds';
+import type {Card, CardFeeds, CardList, CompanyCardFeed, CompanyCardFeedWithDomainID, FailedCompanyCardAssignments, WorkspaceCardsList} from '@src/types/onyx';
 
 const DERIVED_VALUE_CONTEXT: DerivedValueContext<typeof cardFeedErrorsConfig.key, typeof cardFeedErrorsConfig.dependencies> = {
     currentValue: undefined,
@@ -31,11 +30,11 @@ const CARD_FEEDS = {
     },
 } as const satisfies Partial<
     Record<
-        CardFeed,
+        CompanyCardFeed,
         {
             workspaceAccountID: number;
-            feedName: CardFeed;
-            feedNameWithDomainID: CardFeedWithDomainID;
+            feedName: CompanyCardFeed;
+            feedNameWithDomainID: CompanyCardFeedWithDomainID;
         }
     >
 >;
@@ -84,7 +83,7 @@ describe('CardFeedErrors Derived Value', () => {
             expect(result.all.shouldShowRBR).toBe(false);
             expect(result.all.isFeedConnectionBroken).toBe(false);
             expect(result.all.hasFeedErrors).toBe(false);
-            expect(result.all.hasWorkspaceErrors).toBe(false);
+            // expect(result.all.hasWorkspaceErrors).toBe(false);
             expect(result.all.hasFailedCardAssignments).toBe(false);
         });
 
@@ -324,39 +323,39 @@ describe('CardFeedErrors Derived Value', () => {
             });
         });
 
-        describe('workspace errors detection', () => {
-            it('should detect workspace errors', () => {
-                const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
-
-                const card = createCard({
-                    cardID: CARD_IDS.card2,
-                    bank: cardFeed.feedName,
-                    fundID: String(cardFeed.workspaceAccountID),
-                });
-
-                const globalCardList: CardList = {[CARD_IDS.card1]: card};
-
-                const cardFeeds: OnyxCollection<CardFeeds> = {
-                    [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
-                        errors: {
-                            workspaceError: 'Workspace connection issue',
-                        },
-                        settings: {
-                            companyCards: {
-                                [cardFeed.feedName]: {
-                                    pending: false,
-                                },
-                            },
-                        },
-                    },
-                };
-
-                const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
-
-                expect(result.all.hasWorkspaceErrors).toBe(true);
-                expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
-            });
-        });
+        // describe('workspace errors detection', () => {
+        //     it('should detect workspace errors', () => {
+        //         const cardFeed = CARD_FEEDS[CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
+
+        //         const card = createCard({
+        //             cardID: CARD_IDS.card2,
+        //             bank: cardFeed.feedName,
+        //             fundID: String(cardFeed.workspaceAccountID),
+        //         });
+
+        //         const globalCardList: CardList = {[CARD_IDS.card1]: card};
+
+        //         const cardFeeds: OnyxCollection<CardFeeds> = {
+        //             [`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${cardFeed.workspaceAccountID}`]: {
+        //                 errors: {
+        //                     workspaceError: 'Workspace connection issue',
+        //                 },
+        //                 settings: {
+        //                     companyCards: {
+        //                         [cardFeed.feedName]: {
+        //                             pending: false,
+        //                         },
+        //                     },
+        //                 },
+        //             },
+        //         };
+
+        //         const result = cardFeedErrorsConfig.compute([globalCardList, {}, {}, cardFeeds], DERIVED_VALUE_CONTEXT);
+
+        //         expect(result.all.hasWorkspaceErrors).toBe(true);
+        //         expect(result.cardFeedErrors[cardFeed.feedNameWithDomainID]?.hasWorkspaceErrors).toBe(true);
+        //     });
+        // });
 
         describe('card-level errors detection', () => {
             it('should detect card errors', () => {
diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts
index 43456dc5fda3..b6d221afea4a 100644
--- a/tests/unit/CardUtilsTest.ts
+++ b/tests/unit/CardUtilsTest.ts
@@ -1299,15 +1299,6 @@ describe('CardUtils', () => {
         });
     });
 
-    describe('splitCompanyCardFeedWithDomainID', () => {
-        it('should split the feed name and domain ID', () => {
-            const feedName = 'vcf#11111111';
-            const {feedName: splitFeedName, domainID} = splitCompanyCardFeedWithDomainID(feedName);
-            expect(splitFeedName).toBe('vcf');
-            expect(domainID).toBe(11111111);
-        });
-    });
-
     describe('getPlaidInstitutionId', () => {
         it('should return institution ID from plaid feed name without domain ID', () => {
             const feedName = 'plaid.ins_123456';

From 9f1ba2073941fe20334e903bcdfb00719c79d1b3 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 22:31:32 +0000
Subject: [PATCH 112/122] refactor: remove unused util function

---
 src/libs/CardUtils.ts | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 15def94c42d0..174510982c76 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -704,20 +704,6 @@ function getFeedType(feedKey: CompanyCardFeed, cardFeeds: OnyxEntry<CombinedCard
     return feedKey;
 }
 
-/**
- * Filter out the Expensify cards from the list of cards
- *
- * @param cards the list of cards to filter
- * @returns the list of cards without Expensify cards
- */
-function filterCardsByNonExpensify(cards: CardList | undefined): CardList {
-    if (!cards) {
-        return {};
-    }
-
-    return Object.fromEntries(Object.entries(cards).filter(([key]) => !key.includes(CONST.EXPENSIFY_CARD.BANK)));
-}
-
 /**
  * Takes the list of cards divided by workspaces and feeds and returns the flattened non-Expensify cards related to the provided workspace
  *
@@ -932,7 +918,6 @@ export {
     getDomainOrWorkspaceAccountID,
     mergeCardListWithWorkspaceFeeds,
     isCard,
-    filterCardsByNonExpensify,
     getAllCardsForWorkspace,
     isCardHiddenFromSearch,
     getFeedType,

From dc321472b573207fa15eb9cc937af2e8d83308b5 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 22:37:20 +0000
Subject: [PATCH 113/122] fix: add JSDoc

---
 src/types/onyx/DerivedValues.ts | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 7ad3c6f1e91a..7d4bf828ba47 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -49,7 +49,7 @@ type ReportAttributesDerivedValue = {
 };
 
 /**
- *
+ * The transactions and violations of a report.
  */
 type ReportTransactionsAndViolations = {
     /**
@@ -77,15 +77,15 @@ type OutstandingReportsByPolicyIDDerivedValue = Record<string, OnyxCollection<Re
  */
 type CardErrors = {
     /**
-     *
+     * The errors of the card.
      */
     errors?: Card['errors'];
     /**
-     *
+     * The form field errors of the card.
      */
     errorFields?: Card['errorFields'];
     /**
-     *
+     * Whether the card has a pending action.
      */
     pendingAction?: Card['pendingAction'];
 };
@@ -103,18 +103,22 @@ type CardFeedErrorState = {
      * - The feed connection is broken.
      */
     shouldShowRBR: boolean;
+
     /**
      * Whether some failed card assignments.
      */
     hasFailedCardAssignments: boolean;
+
     /**
      * Whether a specific feed within a workspace/domain has errors.
      */
     hasFeedErrors: boolean;
+
     /**
      * Whether some workspace has errors.
      */
     // hasWorkspaceErrors: boolean;
+
     /**
      * Whether some feed connection is broken.
      */
@@ -141,7 +145,7 @@ type FeedErrors = CardFeedErrorState & {
 type CardFeedId = CompanyCardFeedWithNumber;
 
 /**
- *
+ * The errors of all card feeds by workspace account ID and feed name with domain ID.
  */
 type AllCardFeedErrorsMap = Map<number, Map<CardFeedId, FeedErrors>>;
 
@@ -158,14 +162,17 @@ type CardFeedErrors = {
      * The errors of all card feeds by feed name with domain ID.
      */
     cardFeedErrors: CardFeedErrorsObject;
+
     /**
      * The cards with a broken feed connection.
      */
     cardsWithBrokenFeedConnection: Record<string, Card>;
+
     /**
      * Whether to show the RBR for each workspace account ID.
      */
     shouldShowRbrForWorkspaceAccountID: Record<number, boolean>;
+
     /**
      * Whether to show the RBR for each feed name with domain ID.
      */
@@ -175,10 +182,12 @@ type CardFeedErrors = {
      * The errors of all card feeds.
      */
     all: CardFeedErrorState;
+
     /**
      * The errors of company cards.
      */
     companyCards: CardFeedErrorState;
+
     /**
      * The errors of expensify card.
      */

From 2de68932165874c2b7e263d180b1a76592f5f014 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 22:41:24 +0000
Subject: [PATCH 114/122] fix: invalid type in cardFeedError test

---
 src/types/onyx/Card.ts                       |  4 +---
 tests/unit/CardFeedErrorsDerivedValueTest.ts | 12 ++++++++++--
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index a6f9880ea507..abf81784e2fa 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -348,9 +348,7 @@ type FailedCompanyCardAssignment = {
     /** Assigned card */
     assignedCard: Card | undefined;
 
-    /**
-     * The type of action that's pending
-     */
+    /** The type of action that's pending */
     pendingAction?: OnyxCommon.PendingAction;
 };
 
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index 2d6885d844b4..386c8252bdfc 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -258,7 +258,11 @@ describe('CardFeedErrors Derived Value', () => {
                             domainOrWorkspaceAccountID: cardFeed.workspaceAccountID,
                             feed: cardFeed.feedNameWithDomainID,
                             cardName: card.cardName ?? '',
-                            cardNumber: card.lastFourPAN ?? '',
+                            encryptedCardNumber: card.encryptedCardNumber ?? '',
+                            hasFailedCardAssignment: true,
+                            isCardDeleted: false,
+                            isAssigned: false,
+                            assignedCard: undefined,
                         },
                     },
                 };
@@ -585,7 +589,11 @@ describe('CardFeedErrors Derived Value', () => {
                             domainOrWorkspaceAccountID: cardFeed.workspaceAccountID,
                             feed: cardFeed.feedNameWithDomainID,
                             cardName: card.cardName ?? '',
-                            cardNumber: card.lastFourPAN ?? '',
+                            encryptedCardNumber: card.encryptedCardNumber ?? '',
+                            hasFailedCardAssignment: true,
+                            isCardDeleted: false,
+                            isAssigned: false,
+                            assignedCard: undefined,
                         },
                     },
                 };

From 0396a312b06015aa7b2870f8a55e9e165c62acf2 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Tue, 20 Jan 2026 23:13:31 +0000
Subject: [PATCH 115/122] fix: TS errors

---
 .../companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
index 1769a5f45f3b..2e0e95695b5a 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx
@@ -67,9 +67,9 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
 
         return {
             value: feedName,
-            feed: feedSettings.feed,
+            feed: feedSettings.feed as CompanyCardFeed,
             alternateText: domainName ?? policy?.name,
-            text: getCustomOrFormattedFeedName(feedSettings.feed, feedSettings.customFeedName),
+            text: getCustomOrFormattedFeedName(feedSettings.feed as CompanyCardFeed, feedSettings.customFeedName),
             keyForList: feedName,
             isSelected: feedName === selectedFeedName,
             isDisabled: feedSettings.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
@@ -83,7 +83,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS
                 />
             ) : (
                 <Icon
-                    src={getCardFeedIcon(feedSettings.feed, illustrations, companyCardFeedIcons)}
+                    src={getCardFeedIcon(feedSettings.feed as CompanyCardFeed, illustrations, companyCardFeedIcons)}
                     height={variables.cardIconHeight}
                     width={variables.cardIconWidth}
                     additionalStyles={[styles.mr3, styles.cardIcon]}

From d197bb81cabeb1de2aca0c2c8a3ae1af08377646 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Wed, 21 Jan 2026 15:21:47 +0000
Subject: [PATCH 116/122] dummy change to trigger checks

---
 src/libs/actions/CompanyCards.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index e971de736c37..110cc3356da1 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -478,6 +478,7 @@ function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankNa
         ],
     };
 
+
     const parameters = {
         cardID: Number(cardID),
     };

From ebff921dfd4cdaa05917f05e456ee4c68caf1e45 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Wed, 21 Jan 2026 15:22:06 +0000
Subject: [PATCH 117/122] revert dummy change

---
 src/libs/actions/CompanyCards.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 110cc3356da1..e971de736c37 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -478,7 +478,6 @@ function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankNa
         ],
     };
 
-
     const parameters = {
         cardID: Number(cardID),
     };

From 6faf8a29cd6aa931927467c85680af886dfb8b9a Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Wed, 21 Jan 2026 16:21:58 +0000
Subject: [PATCH 118/122] refactor: types around card assignment

---
 .../WorkspaceCompanyCardsTableItem.tsx        | 42 +++----------
 src/types/onyx/Card.ts                        | 59 +++++++++----------
 2 files changed, 37 insertions(+), 64 deletions(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
index e49f4c98f32a..2a3ef0c45fb4 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
@@ -20,45 +20,21 @@ import Navigation from '@libs/Navigation/Navigation';
 import {getDefaultAvatarURL} from '@libs/UserAvatarUtils';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';
-import type {Card, CompanyCardFeed, CompanyCardFeedWithDomainID, PersonalDetails} from '@src/types/onyx';
-import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon';
-
-type WorkspaceCompanyCardTableItemData = {
-    /**
-     * The masked card number displayed to users (e.g., "XXXX1234" or "VISA - 1234").
-     */
-    cardName: string;
-
-    /**
-     * The card identifier sent to backend.
-     * For direct feeds (Plaid/OAuth): equals cardName
-     * For commercial feeds (Visa/Mastercard/Amex): encrypted value
-     */
-    encryptedCardNumber: string;
-
-    /** User-defined name for the card (e.g., "John's card") */
-    customCardName?: string;
-
-    /** Cardholder personal details */
-    cardholder?: PersonalDetails | null;
-
-    /** Assigned card */
-    assignedCard: Card | undefined;
-
-    /** Failed company card assignment */
-    hasFailedCardAssignment: boolean;
-
-    /** Errors */
-    errors?: Errors;
-
-    /** Pending action */
-    pendingAction?: PendingAction;
+import type {Card, CompanyCardFeed} from '@src/types/onyx';
+import type {CardAssignmentData} from '@src/types/onyx/Card';
 
+type WorkspaceCompanyCardTableItemData = CardAssignmentData & {
     /** Whether the card is deleted */
     isCardDeleted: boolean;
 
     /** Whether the card is assigned */
     isAssigned: boolean;
+
+    /** Assigned card */
+    assignedCard?: Card;
+
+    /** On dismiss error callback */
+    onDismissError?: () => void;
 };
 
 type WorkspaceCompanyCardTableItemProps = {
diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index abf81784e2fa..502dc669de06 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -309,47 +309,43 @@ type WorkspaceCardsList = CardList & {
 };
 
 /**
- * Pending action for a company card assignment
+ *
  */
-type FailedCompanyCardAssignment = {
-    /** The domain or workspace account ID */
-    domainOrWorkspaceAccountID: number;
-
-    /** The name of the feed */
-    feed: CompanyCardFeedWithDomainID;
-
-    /** Cardholder personal details */
-    cardholder?: PersonalDetails;
-
-    /** The name of the card */
+type CardAssignmentData = {
+    /**
+     * The masked card number displayed to users (e.g., "XXXX1234" or "VISA - 1234").
+     */
     cardName: string;
 
-    /** Custom card name */
-    customCardName?: string;
-
-    /** Failed company card assignment */
-    hasFailedCardAssignment: boolean;
-
-    /** Encrypted card number */
+    /**
+     * The card identifier sent to backend.
+     * For direct feeds (Plaid/OAuth): equals cardName
+     * For commercial feeds (Visa/Mastercard/Amex): encrypted value
+     */
     encryptedCardNumber: string;
 
-    /** Card related error messages */
-    errors?: OnyxCommon.Errors;
+    /** User-defined name for the card (e.g., "John's card") */
+    customCardName?: string;
 
-    /** Collection of form field errors  */
-    errorFields?: OnyxCommon.ErrorFields;
+    /** Cardholder personal details */
+    cardholder?: PersonalDetails | null;
 
-    /** Whether the card is deleted */
-    isCardDeleted: boolean;
+    /** Errors */
+    errors?: OnyxCommon.Errors;
 
-    /** Whether the card is assigned */
-    isAssigned: boolean;
+    /** Pending action */
+    pendingAction?: OnyxCommon.PendingAction;
+};
 
-    /** Assigned card */
-    assignedCard: Card | undefined;
+/**
+ * Pending action for a company card assignment
+ */
+type FailedCompanyCardAssignment = CardAssignmentData & {
+    /** The domain or workspace account ID */
+    domainOrWorkspaceAccountID: number;
 
-    /** The type of action that's pending */
-    pendingAction?: OnyxCommon.PendingAction;
+    /** The name of the feed */
+    feed: CompanyCardFeedWithDomainID;
 };
 
 /** Pending action for a company card assignment */
@@ -363,6 +359,7 @@ export type {
     IssueNewCardStep,
     IssueNewCardData,
     WorkspaceCardsList,
+    CardAssignmentData,
     FailedCompanyCardAssignment,
     FailedCompanyCardAssignments,
     CardLimitType,

From 2b3805a4df15c15b338c8e30c6d4ef310c28e4f7 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Wed, 21 Jan 2026 16:22:15 +0000
Subject: [PATCH 119/122] feat: implement dismissal of card unassignment error

---
 src/languages/en.ts                           |  1 +
 src/languages/es.ts                           |  1 +
 src/libs/actions/CompanyCards.ts              | 37 +++++++++++++------
 .../WorkspaceCompanyCardsTableItem.tsx        | 21 +----------
 .../WorkspaceCompanyCardsTable/index.tsx      | 20 +++++++---
 5 files changed, 44 insertions(+), 36 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 812ca9d78cf5..c39009de9dab 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4774,6 +4774,7 @@ const translations = {
             ukRegulation:
                 'Expensify Limited is an agent of Plaid Financial Ltd., an authorised payment institution regulated by the Financial Conduct Authority under the Payment Services Regulations 2017 (Firm Reference Number: 804718). Plaid provides you with regulated account information services through Expensify Limited as its agent.',
             assignCardFailedError: 'Card assignment failed.',
+            unassignCardFailedError: 'Card unassignment failed.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
         },
         expensifyCard: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index fcb04c9aaec0..09b4f6a2da2d 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4521,6 +4521,7 @@ ${amount} para ${merchant} - ${date}`,
             ukRegulation:
                 'Expensify Limited es un agente de Plaid Financial Ltd., una institución de pago autorizada y regulada por la Financial Conduct Authority conforme al Reglamento de Servicios de Pago de 2017 (Número de Referencia de la Firma: 804718). Plaid te proporciona servicios regulados de información de cuentas a través de Expensify Limited como su agente.',
             assignCardFailedError: 'Error al asignar la tarjeta.',
+            unassignCardFailedError: 'Error al desasignar la tarjeta.',
             cardAlreadyAssignedError: 'Esta tarjeta ya está asignada a un usuario en otro espacio de trabajo.',
         },
         expensifyCard: {
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index e971de736c37..3e2ff30d049e 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -327,11 +327,7 @@ function assignWorkspaceCompanyCard(
         cardholder,
         cardName,
         customCardName,
-        assignedCard: undefined,
-        isAssigned: true,
-        isCardDeleted: false,
         encryptedCardNumber,
-        hasFailedCardAssignment: true,
         pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
         errors: {
             failed: translate('workspace.companyCards.assignCardFailedError'),
@@ -409,6 +405,16 @@ function assignWorkspaceCompanyCard(
     API.write(WRITE_COMMANDS.ASSIGN_COMPANY_CARD, parameters, onyxData);
 }
 
+function resetFailedWorkspaceCompanyCardAssignment(domainOrWorkspaceAccountID: number, feed: CompanyCardFeedWithDomainID | undefined, encryptedCardNumber: string) {
+    if (!feed) {
+        return;
+    }
+
+    Onyx.merge(`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feed}`, {
+        [encryptedCardNumber]: null,
+    });
+}
+
 function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed, card: Card) {
     const cardID = card.cardID;
 
@@ -461,7 +467,7 @@ function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankNa
                     [cardID]: {
                         ...card,
                         pendingAction: null,
-                        errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+                        errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.unassignCardFailedError'),
                     },
                 },
             },
@@ -471,7 +477,7 @@ function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankNa
                 value: {
                     [cardID]: {
                         pendingAction: null,
-                        errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+                        errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.companyCards.unassignCardFailedError'),
                     },
                 },
             },
@@ -485,13 +491,21 @@ function unassignWorkspaceCompanyCard(domainOrWorkspaceAccountID: number, bankNa
     API.write(WRITE_COMMANDS.UNASSIGN_COMPANY_CARD, parameters, onyxData);
 }
 
-function resetFailedWorkspaceCompanyCardAssignment(domainOrWorkspaceAccountID: number, feed: CompanyCardFeedWithDomainID | undefined, encryptedCardNumber: string) {
-    if (!feed) {
+function resetFailedWorkspaceCompanyCardUnassignment(domainOrWorkspaceAccountID: number, bankName: CompanyCardFeed | undefined, cardID: number | undefined) {
+    if (!bankName || !cardID) {
         return;
     }
 
-    Onyx.merge(`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feed}`, {
-        [encryptedCardNumber]: null,
+    Onyx.merge(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${domainOrWorkspaceAccountID}_${bankName}`, {
+        [cardID]: {
+            errors: null,
+        },
+    });
+
+    Onyx.merge(ONYXKEYS.CARD_LIST, {
+        [cardID]: {
+            errors: null,
+        },
     });
 }
 
@@ -1006,8 +1020,9 @@ export {
     openPolicyCompanyCardsFeed,
     addNewCompanyCardsFeed,
     assignWorkspaceCompanyCard,
-    unassignWorkspaceCompanyCard,
     resetFailedWorkspaceCompanyCardAssignment,
+    unassignWorkspaceCompanyCard,
+    resetFailedWorkspaceCompanyCardUnassignment,
     updateWorkspaceCompanyCard,
     updateCompanyCardName,
     updateCardTransactionStartDate,
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
index 2a3ef0c45fb4..396114ce3fb7 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableItem.tsx
@@ -14,7 +14,6 @@ import useLocalize from '@hooks/useLocalize';
 import useNetwork from '@hooks/useNetwork';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {resetFailedWorkspaceCompanyCardAssignment} from '@libs/actions/CompanyCards';
 import {getCompanyCardFeedWithDomainID, lastFourNumbersFromCardName, splitMaskedCardNumber} from '@libs/CardUtils';
 import Navigation from '@libs/Navigation/Navigation';
 import {getDefaultAvatarURL} from '@libs/UserAvatarUtils';
@@ -44,12 +43,6 @@ type WorkspaceCompanyCardTableItemProps = {
     /** Policy ID */
     policyID: string;
 
-    /** Feed name */
-    feed: CompanyCardFeedWithDomainID | undefined;
-
-    /** Domain or workspace account ID */
-    domainOrWorkspaceAccountID: number;
-
     /** Card feed icon element */
     CardFeedIcon?: React.ReactNode;
 
@@ -76,8 +69,6 @@ type WorkspaceCompanyCardTableItemProps = {
 function WorkspaceCompanyCardTableItem({
     item,
     policyID,
-    feed,
-    domainOrWorkspaceAccountID,
     CardFeedIcon,
     isPlaidCardFeed,
     shouldUseNarrowTableLayout,
@@ -91,7 +82,7 @@ function WorkspaceCompanyCardTableItem({
     const Expensicons = useMemoizedLazyExpensifyIcons(['ArrowRight']);
     const {isOffline} = useNetwork();
 
-    const {cardName, encryptedCardNumber, customCardName, cardholder, assignedCard, isAssigned, isCardDeleted, hasFailedCardAssignment, errors, pendingAction} = item;
+    const {cardName, encryptedCardNumber, customCardName, cardholder, assignedCard, isAssigned, isCardDeleted, errors, pendingAction, onDismissError} = item;
 
     const lastCardNumbers = isPlaidCardFeed ? lastFourNumbersFromCardName(cardName) : splitMaskedCardNumber(cardName)?.lastDigits;
     const cardholderLoginText = !shouldUseNarrowTableLayout && isAssigned ? Str.removeSMSDomain(cardholder?.login ?? '') : undefined;
@@ -100,14 +91,6 @@ function WorkspaceCompanyCardTableItem({
     const leftColumnTitle = isAssigned ? Str.removeSMSDomain(cardholder?.displayName ?? '') : translate('workspace.moreFeatures.companyCards.unassignedCards');
     const leftColumnSubtitle = shouldUseNarrowTableLayout ? narrowWidthCardName : cardholderLoginText;
 
-    const resetFailedCompanyCardAssignment = () => {
-        if (!hasFailedCardAssignment) {
-            return;
-        }
-
-        resetFailedWorkspaceCompanyCardAssignment(domainOrWorkspaceAccountID, feed, encryptedCardNumber);
-    };
-
     const assignCard = () => onAssignCard(cardName, encryptedCardNumber);
     const isDeleting = !isOffline && pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
 
@@ -116,7 +99,7 @@ function WorkspaceCompanyCardTableItem({
             errorRowStyles={[styles.ph5, styles.mb4]}
             errors={errors}
             pendingAction={pendingAction}
-            onClose={resetFailedCompanyCardAssignment}
+            onClose={onDismissError}
             shouldHideOnDelete={false}
         >
             {isDeleting ? (
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index de2d2fe3ecd9..f8c3ebc6d6db 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -12,6 +12,7 @@ import useNetwork from '@hooks/useNetwork';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {resetFailedWorkspaceCompanyCardAssignment, resetFailedWorkspaceCompanyCardUnassignment} from '@libs/actions/CompanyCards';
 import {getDefaultCardName} from '@libs/CardUtils';
 import tokenizedSearch from '@libs/tokenizedSearch';
 import WorkspaceCompanyCardPageEmptyState from '@pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState';
@@ -56,6 +57,7 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
 
     const {
         feedName,
+        bankName,
         cardList,
         assignedCards,
         cardNames,
@@ -113,26 +115,32 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
         ? []
         : (cardNames?.map((cardName) => {
               // For direct feeds cardID equals cardName, for commercial feeds it's looked up from cardList
-              const cardID = isDirectCardFeed ? cardName : (cardList?.[cardName] ?? '');
-              const failedCompanyCardAssignment = failedCompanyCardAssignments?.[cardID];
+              const encryptedCardNumber = isDirectCardFeed ? cardName : (cardList?.[cardName] ?? '');
+              const failedCompanyCardAssignment = failedCompanyCardAssignments?.[encryptedCardNumber];
 
               if (failedCompanyCardAssignment) {
-                  return failedCompanyCardAssignment;
+                  return {
+                      ...failedCompanyCardAssignment,
+                      onDismissError: () => resetFailedWorkspaceCompanyCardAssignment(domainOrWorkspaceAccountID, feedName, failedCompanyCardAssignment.encryptedCardNumber),
+                      isCardDeleted: false,
+                      isAssigned: true,
+                  };
               }
-              const assignedCard = Object.values(assignedCards ?? {}).find((card: Card) => card.encryptedCardNumber === cardID || card.cardName === cardName);
+
+              const assignedCard = Object.values(assignedCards ?? {}).find((card: Card) => card.encryptedCardNumber === encryptedCardNumber || card.cardName === cardName);
               const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
 
               return {
                   cardName,
-                  encryptedCardNumber: cardID,
+                  encryptedCardNumber,
                   customCardName: assignedCard?.cardID && customCardNames?.[assignedCard.cardID] ? customCardNames?.[assignedCard.cardID] : getDefaultCardName(cardholder?.displayName ?? ''),
                   isCardDeleted: assignedCard?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
                   isAssigned: !!assignedCard,
-                  hasFailedCardAssignment: false,
                   assignedCard,
                   cardholder,
                   errors: assignedCard?.errors,
                   pendingAction: assignedCard?.pendingAction,
+                  onDismissError: () => resetFailedWorkspaceCompanyCardUnassignment(domainOrWorkspaceAccountID, bankName, assignedCard?.cardID),
               };
           }) ?? []);
 

From ee50a449b55c0a2a106a70b0cc9c575432023bcf Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Wed, 21 Jan 2026 17:25:36 +0000
Subject: [PATCH 120/122] fix: TS errors

---
 .../companyCards/WorkspaceCompanyCardsTable/index.tsx     | 2 --
 src/types/onyx/Card.ts                                    | 5 +++++
 tests/unit/CardFeedErrorsDerivedValueTest.ts              | 8 --------
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
index 44542cd1f839..5d78023a249f 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx
@@ -249,8 +249,6 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
             key={`${item.cardName}_${index}`}
             item={item}
             policyID={policyID ?? String(CONST.DEFAULT_NUMBER_ID)}
-            feed={feedName}
-            domainOrWorkspaceAccountID={domainOrWorkspaceAccountID}
             CardFeedIcon={cardFeedIcon}
             isPlaidCardFeed={isDirectCardFeed}
             onAssignCard={onAssignCard}
diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index 502dc669de06..9d7c5d6777dc 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -333,6 +333,11 @@ type CardAssignmentData = {
     /** Errors */
     errors?: OnyxCommon.Errors;
 
+    /**
+     *
+     */
+    errorFields?: OnyxCommon.ErrorFields;
+
     /** Pending action */
     pendingAction?: OnyxCommon.PendingAction;
 };
diff --git a/tests/unit/CardFeedErrorsDerivedValueTest.ts b/tests/unit/CardFeedErrorsDerivedValueTest.ts
index 386c8252bdfc..a2dd3da3c4d4 100644
--- a/tests/unit/CardFeedErrorsDerivedValueTest.ts
+++ b/tests/unit/CardFeedErrorsDerivedValueTest.ts
@@ -259,10 +259,6 @@ describe('CardFeedErrors Derived Value', () => {
                             feed: cardFeed.feedNameWithDomainID,
                             cardName: card.cardName ?? '',
                             encryptedCardNumber: card.encryptedCardNumber ?? '',
-                            hasFailedCardAssignment: true,
-                            isCardDeleted: false,
-                            isAssigned: false,
-                            assignedCard: undefined,
                         },
                     },
                 };
@@ -590,10 +586,6 @@ describe('CardFeedErrors Derived Value', () => {
                             feed: cardFeed.feedNameWithDomainID,
                             cardName: card.cardName ?? '',
                             encryptedCardNumber: card.encryptedCardNumber ?? '',
-                            hasFailedCardAssignment: true,
-                            isCardDeleted: false,
-                            isAssigned: false,
-                            assignedCard: undefined,
                         },
                     },
                 };

From 30d2b9350653bcdbdacc3076d13bd2f6be866817 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Fri, 23 Jan 2026 16:50:12 +0000
Subject: [PATCH 121/122] chore: run translations script

---
 src/languages/de.ts      | 1 +
 src/languages/fr.ts      | 1 +
 src/languages/it.ts      | 1 +
 src/languages/ja.ts      | 1 +
 src/languages/nl.ts      | 1 +
 src/languages/pl.ts      | 1 +
 src/languages/pt-BR.ts   | 1 +
 src/languages/zh-hans.ts | 1 +
 8 files changed, 8 insertions(+)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index a4b3fbf2348a..769c160091a1 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -4890,6 +4890,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
             assign: 'Zuweisen',
             assignCardFailedError: 'Kartenzuweisung fehlgeschlagen.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Aufhebung der Kartenzuweisung fehlgeschlagen.',
         },
         expensifyCard: {
             issueAndManageCards: 'Expensify Cards ausstellen und verwalten',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index b89785f3f112..138057a8dce0 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -4896,6 +4896,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
             assign: 'Assigner',
             assignCardFailedError: 'L’attribution de la carte a échoué.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Échec de la désaffectation de la carte.',
         },
         expensifyCard: {
             issueAndManageCards: 'Émettre et gérer vos cartes Expensify',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index ce077164408e..69fb75eb9c2d 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -4876,6 +4876,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
             assign: 'Assegna',
             assignCardFailedError: 'Assegnazione della carta non riuscita.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Rimozione della carta non riuscita.',
         },
         expensifyCard: {
             issueAndManageCards: 'Emetti e gestisci le tue Expensify Card',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 197488a8d6e6..303325dbe65d 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -4849,6 +4849,7 @@ _より詳しい手順については、[ヘルプサイトをご覧ください
             assign: '割り当て',
             assignCardFailedError: 'カードの割り当てに失敗しました。',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'カードの割り当て解除に失敗しました。',
         },
         expensifyCard: {
             issueAndManageCards: 'Expensify カードの発行と管理',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index f778ac977a3a..6575f6fd28e4 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -4872,6 +4872,7 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO
             assign: 'Toewijzen',
             assignCardFailedError: 'Toewijzing van kaart mislukt.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Kaartontkoppeling mislukt.',
         },
         expensifyCard: {
             issueAndManageCards: 'Uw Expensify Cards uitgeven en beheren',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 8e45fc3139d3..582d8a8efeb3 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -4861,6 +4861,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
             assign: 'Przypisz',
             assignCardFailedError: 'Przypisanie karty nie powiodło się.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Nie udało się odłączyć karty.',
         },
         expensifyCard: {
             issueAndManageCards: 'Wydawaj i zarządzaj swoimi kartami Expensify',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index b3ff62c08ee5..a29d60d06e00 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -4861,6 +4861,7 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT
             assign: 'Atribuir',
             assignCardFailedError: 'Falha na atribuição do cartão.',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: 'Falha ao desatribuir o cartão.',
         },
         expensifyCard: {
             issueAndManageCards: 'Emitir e gerenciar seus Cartões Expensify',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 71c8fc3addb9..6483b11dde2c 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -4770,6 +4770,7 @@ _如需更详细的说明，请[访问我们的帮助网站](${CONST.NETSUITE_IM
             assign: '分配',
             assignCardFailedError: '卡片分配失败。',
             cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
+            unassignCardFailedError: '卡片取消分配失败。',
         },
         expensifyCard: {
             issueAndManageCards: '发放和管理您的 Expensify 卡',

From 5a58af703f3590b2e75e541bdeb129cd5eabd670 Mon Sep 17 00:00:00 2001
From: Christoph Pader <chris@margelo.com>
Date: Mon, 26 Jan 2026 10:14:01 +0000
Subject: [PATCH 122/122] fix: add manual memo to
 `usePoliciesWithCardFeedErrors` to fix tests

---
 src/hooks/usePoliciesWithCardFeedErrors.ts | 25 +++++++++++++---------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/src/hooks/usePoliciesWithCardFeedErrors.ts b/src/hooks/usePoliciesWithCardFeedErrors.ts
index abe424e94f21..4f303998c549 100644
--- a/src/hooks/usePoliciesWithCardFeedErrors.ts
+++ b/src/hooks/usePoliciesWithCardFeedErrors.ts
@@ -1,3 +1,4 @@
+import {useMemo} from 'react';
 import {isPolicyAdmin} from '@libs/PolicyUtils';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Policy} from '@src/types/onyx';
@@ -13,19 +14,23 @@ function usePoliciesWithCardFeedErrors() {
     const cleanPolicies = Object.values(policies ?? {}).filter((policy) => policy?.id);
 
     const {shouldShowRbrForWorkspaceAccountID} = useCardFeedErrors();
-    const policiesWithCardFeedErrors: Policy[] = [];
-    for (const [workspaceAccountID, hasErrors] of Object.entries(shouldShowRbrForWorkspaceAccountID)) {
-        if (!hasErrors) {
-            continue;
-        }
+    const policiesWithCardFeedErrors = useMemo(() => {
+        const p: Policy[] = [];
+        for (const [workspaceAccountID, hasErrors] of Object.entries(shouldShowRbrForWorkspaceAccountID)) {
+            if (!hasErrors) {
+                continue;
+            }
+
+            const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
+            if (!policyWithCardFeedErrors) {
+                continue;
+            }
 
-        const policyWithCardFeedErrors = cleanPolicies.find((policy) => policy?.workspaceAccountID === Number(workspaceAccountID));
-        if (!policyWithCardFeedErrors) {
-            continue;
+            p.push(policyWithCardFeedErrors);
         }
 
-        policiesWithCardFeedErrors.push(policyWithCardFeedErrors);
-    }
+        return p;
+    }, [cleanPolicies, shouldShowRbrForWorkspaceAccountID]);
 
     return {
         policiesWithCardFeedErrors,
