diff --git a/assets/images/product-illustrations/broken-magnifying-glass.svg b/assets/images/product-illustrations/broken-magnifying-glass.svg
index 351b1cf86d38..436bdce844c9 100644
--- a/assets/images/product-illustrations/broken-magnifying-glass.svg
+++ b/assets/images/product-illustrations/broken-magnifying-glass.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 22e530eaaa26..f0cc4905b01a 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -3582,6 +3582,8 @@ const CONST = {
COMPANY_CARDS: {
BROKEN_CONNECTION_IGNORED_STATUSES: brokenConnectionScrapeStatuses,
CONNECTION_ERROR: 'connectionError',
+ WORKSPACE_FEEDS_LOAD_ERROR: 'workspaceFeedsLoadError',
+ FEED_LOAD_ERROR: 'feedLoadError',
STEP: {
SELECT_BANK: 'SelectBank',
SELECT_FEED_TYPE: 'SelectFeedType',
diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx
index c822a4605c79..88f4207da81e 100644
--- a/src/components/BlockingViews/BlockingView.tsx
+++ b/src/components/BlockingViews/BlockingView.tsx
@@ -152,7 +152,7 @@ function BlockingView({
/>
)}
- {title}
+ {title}
{CustomSubtitle}
{!CustomSubtitle && (
diff --git a/src/components/BlockingViews/FullPageErrorView.tsx b/src/components/BlockingViews/FullPageErrorView.tsx
index 5cd9d28fb25a..a40e510c57cc 100644
--- a/src/components/BlockingViews/FullPageErrorView.tsx
+++ b/src/components/BlockingViews/FullPageErrorView.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import {View} from 'react-native';
-import type {StyleProp, TextStyle} from 'react-native';
+import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
@@ -28,10 +28,12 @@ type FullPageErrorViewProps = {
/** The style of the subtitle message */
subtitleStyle?: StyleProp;
+
+ containerStyle?: StyleProp;
};
// eslint-disable-next-line rulesdir/no-negated-variables
-function FullPageErrorView({testID, children = null, shouldShow = false, title = '', subtitle = '', shouldForceFullScreen = false, subtitleStyle}: FullPageErrorViewProps) {
+function FullPageErrorView({testID, children = null, shouldShow = false, title = '', subtitle = '', shouldForceFullScreen = false, subtitleStyle, containerStyle}: FullPageErrorViewProps) {
const styles = useThemeStyles();
const illustrations = useMemoizedLazyIllustrations(['BrokenMagnifyingGlass']);
@@ -39,7 +41,7 @@ function FullPageErrorView({testID, children = null, shouldShow = false, title =
return (
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index 6034026dc50f..3ad663631cb5 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -1141,6 +1141,7 @@ function Search({
{
- openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID);
+ openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID, translate);
};
const {isOffline} = useNetwork({onReconnect: fetchCompanyCards});
diff --git a/src/hooks/useCardFeedErrors.ts b/src/hooks/useCardFeedErrors.ts
index 13148d61586d..ca189e241836 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/hooks/useCardFeeds.tsx b/src/hooks/useCardFeeds.tsx
index 473b938fcc9f..a214aa390545 100644
--- a/src/hooks/useCardFeeds.tsx
+++ b/src/hooks/useCardFeeds.tsx
@@ -1,7 +1,7 @@
import type {OnyxCollection, ResultMetadata} from 'react-native-onyx';
-import {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 {CardFeeds, CardFeedsStatusByDomainID, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeedWithDomainID} from '@src/types/onyx';
import useOnyx from './useOnyx';
import useWorkspaceAccountID from './useWorkspaceAccountID';
@@ -19,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>, CardFeeds | undefined] => {
+const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefined, ResultMetadata>, CardFeeds | undefined, CardFeedsStatusByDomainID] => {
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 +31,9 @@ const useCardFeeds = (policyID: string | undefined): [CombinedCardFeeds | undefi
workspaceFeeds = getCombinedCardFeedsFromAllFeeds(allFeeds, shouldIncludeFeedPredicate);
}
- return [workspaceFeeds, allFeedsResult, defaultFeed];
+ const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(allFeeds);
+
+ return [workspaceFeeds, allFeedsResult, defaultFeed, workspaceCardFeedsStatus];
};
export default useCardFeeds;
diff --git a/src/hooks/useCompanyCards.ts b/src/hooks/useCompanyCards.ts
index 7975b56f90ec..32e0f5119434 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, CompanyCardFeed, CompanyCardFeedWithDomainID, CompanyFeeds} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatusByDomainID, 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';
@@ -25,6 +25,7 @@ type UseCompanyCardsResult = Partial<{
feedName: CompanyCardFeedWithDomainID;
cardList: AssignableCardsList;
assignedCards: CardList;
+ workspaceCardFeedsStatus: CardFeedsStatusByDomainID;
cardNames: string[];
allCardFeeds: CombinedCardFeeds;
companyCardFeeds: CompanyFeeds;
@@ -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, , workspaceCardFeedsStatus] = useCardFeeds(policyID);
const feedName = feedNameProp ?? getSelectedFeed(lastSelectedFeed, allCardFeeds);
const bankName = feedName ? getCompanyCardFeed(feedName) : undefined;
@@ -90,6 +91,7 @@ function useCompanyCards({policyID, feedName: feedNameProp}: UseCompanyCardsProp
companyCardFeeds,
cardList,
assignedCards,
+ workspaceCardFeedsStatus,
cardNames,
selectedFeed,
bankName,
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 4348d6af9605..4aa957b70382 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -4949,6 +4949,14 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
assignCardFailedError: 'Kartenzuweisung fehlgeschlagen.',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
unassignCardFailedError: 'Aufhebung der Kartenzuweisung fehlgeschlagen.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Kartenfeeds konnten nicht geladen werden',
+ workspaceFeedsCouldNotBeLoadedMessage:
+ 'Beim Laden der Workspace-Kartenfeeds ist ein Fehler aufgetreten. Bitte versuche es erneut oder wende dich an deine Administratorin bzw. deinen Administrator.',
+ feedCouldNotBeLoadedTitle: 'Dieser Feed konnte nicht geladen werden',
+ feedCouldNotBeLoadedMessage: 'Beim Laden dieses Feeds ist ein Fehler aufgetreten. Bitte versuche es erneut oder kontaktiere deine Administratorin/deinen Administrator.',
+ tryAgain: 'Erneut versuchen',
+ },
},
expensifyCard: {
issueAndManageCards: 'Expensify Cards ausstellen und verwalten',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 74ab2fd16c0f..28b7bee75c48 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4765,6 +4765,13 @@ const translations = {
companyCards: {
addCards: 'Add cards',
selectCards: 'Select cards',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: "Couldn't load card feeds",
+ workspaceFeedsCouldNotBeLoadedMessage: 'An error occurred while loading workspace card feeds. Please try again or contact your administrator.',
+ feedCouldNotBeLoadedTitle: "Couldn't load this feed",
+ feedCouldNotBeLoadedMessage: 'An error occurred while loading this feed. Please try again or contact your administrator.',
+ tryAgain: 'Try again',
+ },
addNewCard: {
other: 'Other',
cardProviders: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 3df43c2b0497..bcaf5d150e6c 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4512,6 +4512,14 @@ ${amount} para ${merchant} - ${date}`,
companyCards: {
addCards: 'Añadir tarjetas',
selectCards: 'Seleccionar tarjetas',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Error al cargar las fuentes de tarjetas del espacio de trabajo',
+ workspaceFeedsCouldNotBeLoadedMessage:
+ 'Ocurrió un error al cargar las fuentes de tarjetas del espacio de trabajo. Por favor, inténtelo de nuevo o contacte a su administrador.',
+ feedCouldNotBeLoadedTitle: 'Error al cargar esta fuente de tarjetas',
+ feedCouldNotBeLoadedMessage: 'Ocurrió un error al cargar esta fuente de tarjetas. Por favor, inténtelo de nuevo o contacte a su administrador.',
+ tryAgain: 'Inténtalo de nuevo',
+ },
addNewCard: {
other: 'Otros',
cardProviders: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 4b1b826482c4..f6228c8747d6 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -4956,6 +4956,14 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
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.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Impossible de charger les flux de carte',
+ workspaceFeedsCouldNotBeLoadedMessage:
+ 'Une erreur s’est produite lors du chargement des flux de cartes de l’espace de travail. Veuillez réessayer ou contacter votre administrateur.',
+ feedCouldNotBeLoadedTitle: 'Impossible de charger ce flux',
+ feedCouldNotBeLoadedMessage: 'Une erreur s’est produite lors du chargement de ce flux. Veuillez réessayer ou contacter votre administrateur.',
+ tryAgain: 'Réessayer',
+ },
},
expensifyCard: {
issueAndManageCards: 'Émettre et gérer vos cartes Expensify',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 8ef5103b5b7d..00956daf561f 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -4935,6 +4935,14 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
assignCardFailedError: 'Assegnazione della carta non riuscita.',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
unassignCardFailedError: 'Rimozione della carta non riuscita.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Impossibile caricare i feed delle carte',
+ workspaceFeedsCouldNotBeLoadedMessage:
+ 'Si è verificato un errore durante il caricamento dei feed della scheda dell’area di lavoro. Riprova o contatta il tuo amministratore.',
+ feedCouldNotBeLoadedTitle: 'Impossibile caricare questo feed',
+ feedCouldNotBeLoadedMessage: 'Si è verificato un errore durante il caricamento di questo feed. Riprova o contatta il tuo amministratore.',
+ tryAgain: 'Riprova',
+ },
},
expensifyCard: {
issueAndManageCards: 'Emetti e gestisci le tue Expensify Card',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 29398886299c..4f26953f19d4 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -4907,6 +4907,13 @@ _より詳しい手順については、[ヘルプサイトをご覧ください
assignCardFailedError: 'カードの割り当てに失敗しました。',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
unassignCardFailedError: 'カードの割り当て解除に失敗しました。',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'カードフィードを読み込めませんでした',
+ workspaceFeedsCouldNotBeLoadedMessage: 'ワークスペースのカードフィードを読み込む際にエラーが発生しました。もう一度お試しいただくか、管理者に連絡してください。',
+ feedCouldNotBeLoadedTitle: 'このフィードを読み込めませんでした',
+ feedCouldNotBeLoadedMessage: 'このフィードの読み込み中にエラーが発生しました。もう一度お試しいただくか、管理者に連絡してください。',
+ tryAgain: '再試行',
+ },
},
expensifyCard: {
issueAndManageCards: 'Expensify カードの発行と管理',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index a29f8bacdf33..74d6de88289b 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -4680,7 +4680,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze helppagina](${CONST.NETSUITE
_Voor meer gedetailleerde instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPORT.HELP_LINKS.CUSTOM_SEGMENTS})_.`,
customSegmentScriptIDTitle: 'Wat is de script-ID?',
- customSegmentScriptIDFooter: `Je kunt aangepaste segmentscript-ID’s in NetSuite vinden onder:
+ customSegmentScriptIDFooter: `Je kunt aangepaste segmentscript-ID’s in NetSuite vinden onder:
1. *Customization > Lists, Records, & Fields > Custom Segments*.
2. Klik op een aangepast segment.
@@ -4931,6 +4931,14 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO
assignCardFailedError: 'Toewijzing van kaart mislukt.',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
unassignCardFailedError: 'Kaartontkoppeling mislukt.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Kan kaartfeeds niet laden',
+ workspaceFeedsCouldNotBeLoadedMessage:
+ 'Er is een fout opgetreden bij het laden van de kaartfeeds van de werkruimte. Probeer het opnieuw of neem contact op met uw beheerder.',
+ feedCouldNotBeLoadedTitle: 'Kon deze feed niet laden',
+ feedCouldNotBeLoadedMessage: 'Er is een fout opgetreden bij het laden van deze feed. Probeer het opnieuw of neem contact op met uw beheerder.',
+ tryAgain: 'Opnieuw proberen',
+ },
},
expensifyCard: {
issueAndManageCards: 'Uw Expensify Cards uitgeven en beheren',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 67b0050f7a51..9b14ccfc490a 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -798,7 +798,7 @@ const translations: TranslationDeepObject = {
expiredCodeDescription: 'Wróć do oryginalnego urządzenia i poproś o nowy kod',
successfulNewCodeRequest: 'Kod został wysłany. Sprawdź swoje urządzenie.',
tfaRequiredTitle: dedent(`
- Dwuskładnikowe uwierzytelnianie
+ Dwuskładnikowe uwierzytelnianie
wymagane
`),
tfaRequiredDescription: dedent(`
@@ -4921,6 +4921,13 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
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.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Nie można było wczytać kanałów kart',
+ workspaceFeedsCouldNotBeLoadedMessage: 'Wystąpił błąd podczas ładowania kanałów kart w przestrzeni roboczej. Spróbuj ponownie lub skontaktuj się z administratorem.',
+ feedCouldNotBeLoadedTitle: 'Nie można było wczytać tego kanału',
+ feedCouldNotBeLoadedMessage: 'Wystąpił błąd podczas ładowania tego kanału. Spróbuj ponownie lub skontaktuj się ze swoim administratorem.',
+ tryAgain: 'Spróbuj ponownie',
+ },
},
expensifyCard: {
issueAndManageCards: 'Wydawaj i zarządzaj swoimi kartami Expensify',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 6251c0b90fe7..f7ad064fbb1f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -4920,6 +4920,13 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT
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.',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: 'Não foi possível carregar os feeds do cartão',
+ workspaceFeedsCouldNotBeLoadedMessage: 'Ocorreu um erro ao carregar os feeds de cartões do workspace. Tente novamente ou entre em contato com o administrador.',
+ feedCouldNotBeLoadedTitle: 'Não foi possível carregar este feed',
+ feedCouldNotBeLoadedMessage: 'Ocorreu um erro ao carregar este feed. Tente novamente ou entre em contato com o seu administrador.',
+ tryAgain: 'Tentar novamente',
+ },
},
expensifyCard: {
issueAndManageCards: 'Emitir e gerenciar seus Cartões Expensify',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 4bfcf7b5a0d4..bbff72a1bd1e 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -781,7 +781,7 @@ const translations: TranslationDeepObject = {
请输入在最初请求该代码的设备上显示的代码
`),
doNotShare: dedent(`
- 不要与任何人分享你的验证码。
+ 不要与任何人分享你的验证码。
Expensify 永远不会向你索要它!
`),
or: ',或',
@@ -790,7 +790,7 @@ const translations: TranslationDeepObject = {
expiredCodeDescription: '返回原始设备并请求新验证码',
successfulNewCodeRequest: '已请求验证码。请检查您的设备。',
tfaRequiredTitle: dedent(`
- 双重身份验证
+ 双重身份验证
必填
`),
tfaRequiredDescription: dedent(`
@@ -4828,6 +4828,13 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
assignCardFailedError: '卡片分配失败。',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
unassignCardFailedError: '卡片取消分配失败。',
+ error: {
+ workspaceFeedsCouldNotBeLoadedTitle: '无法加载卡片信息流',
+ workspaceFeedsCouldNotBeLoadedMessage: '加载工作区卡片动态时出错。请重试或联系您的管理员。',
+ feedCouldNotBeLoadedTitle: '无法加载此订阅内容',
+ feedCouldNotBeLoadedMessage: '加载此信息流时出错。请重试或联系您的管理员。',
+ tryAgain: '重试',
+ },
},
expensifyCard: {
issueAndManageCards: '发放和管理您的 Expensify 卡',
diff --git a/src/libs/CardFeedUtils.ts b/src/libs/CardFeedUtils.ts
index 9186fee79a9f..47645f2f07df 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} from '@src/types/onyx/CardFeeds';
+import type {CardFeedsStatus, CardFeedsStatusByDomainID, CombinedCardFeed} from '@src/types/onyx/CardFeeds';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {
getBankName,
@@ -515,6 +515,21 @@ function getCardFeedsForDisplayPerPolicy(allCardFeeds: OnyxCollection
return cardFeedsForDisplayPerPolicy;
}
+function getCardFeedStatus(feed: CardFeeds | undefined): CardFeedsStatus {
+ return {
+ errors: feed?.errors,
+ isLoading: feed?.isLoading,
+ };
+}
+
+function getWorkspaceCardFeedsStatus(allFeeds: OnyxCollection | undefined): CardFeedsStatusByDomainID {
+ return Object.entries(allFeeds ?? {}).reduce((acc, [onyxKey, feeds]) => {
+ const domainID = Number(onyxKey.split('_').at(-1));
+ acc[domainID] = getCardFeedStatus(feeds);
+ return acc;
+ }, {} as CardFeedsStatusByDomainID);
+}
+
function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection | undefined, includeFeedPredicate?: (feed: CombinedCardFeed) => boolean): CombinedCardFeeds {
return Object.entries(allFeeds ?? {}).reduce((acc, [onyxKey, feeds]) => {
const domainID = Number(onyxKey.split('_').at(-1));
@@ -530,17 +545,19 @@ function getCombinedCardFeedsFromAllFeeds(allFeeds: OnyxCollection |
const feedSettings = companyCards?.[feedName];
const oAuthAccountDetails = workspaceFeedsSettings?.oAuthAccountDetails?.[feedName];
const customFeedName = workspaceFeedsSettings?.companyCardNicknames?.[feedName];
+ const status = workspaceFeedsSettings?.cardFeedsStatus?.[feedName];
if (!domainID) {
continue;
}
- const combinedCardFeed = {
+ const combinedCardFeed: CombinedCardFeed = {
...feedSettings,
...oAuthAccountDetails,
customFeedName,
domainID,
feed: feedName,
+ status,
};
if (includeFeedPredicate && !includeFeedPredicate(combinedCardFeed)) {
@@ -571,4 +588,6 @@ export {
getCardFeedsForDisplay,
getCardFeedsForDisplayPerPolicy,
getCombinedCardFeedsFromAllFeeds,
+ getCardFeedStatus,
+ getWorkspaceCardFeedsStatus,
};
diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts
index 46c5a1f66142..0bdbc9715cd5 100644
--- a/src/libs/actions/CompanyCards.ts
+++ b/src/libs/actions/CompanyCards.ts
@@ -839,23 +839,37 @@ function clearCompanyCardErrorField(domainOrWorkspaceAccountID: number, cardID:
});
}
-function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID: number) {
+function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID: number, translate: LocaleContextProps['translate']) {
const optimisticData: Array> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
value: {
isLoading: true,
+ errors: null,
},
},
];
- const finallyData: Array> = [
+ const successData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
+ value: {
+ isLoading: false,
+ },
+ },
+ ];
+
+ const failureData: Array> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainOrWorkspaceAccountID}`,
value: {
isLoading: false,
+ errors: {
+ [CONST.COMPANY_CARDS.WORKSPACE_FEEDS_LOAD_ERROR]: translate('workspace.companyCards.error.workspaceFeedsCouldNotBeLoadedMessage'),
+ },
},
},
];
@@ -864,17 +878,69 @@ function openPolicyCompanyCardsPage(policyID: string, domainOrWorkspaceAccountID
policyID,
};
- API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, finallyData});
+ API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_PAGE, params, {optimisticData, successData, failureData});
}
-function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, feed: CompanyCardFeedWithNumber) {
+function openPolicyCompanyCardsFeed(domainAccountID: number, policyID: string, feed: CompanyCardFeedWithNumber, translate: LocaleContextProps['translate']) {
const parameters: OpenPolicyCompanyCardsFeedParams = {
domainAccountID,
policyID,
feed,
};
- API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters);
+ const optimisticData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
+ value: {
+ settings: {
+ cardFeedsStatus: {
+ [feed]: {
+ isLoading: true,
+ errors: null,
+ },
+ },
+ },
+ },
+ },
+ ];
+
+ const successData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
+ value: {
+ settings: {
+ cardFeedsStatus: {
+ [feed]: {
+ isLoading: false,
+ },
+ },
+ },
+ },
+ },
+ ];
+
+ const failureData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
+ value: {
+ settings: {
+ cardFeedsStatus: {
+ [feed]: {
+ isLoading: false,
+ errors: {
+ [CONST.COMPANY_CARDS.FEED_LOAD_ERROR]: translate('workspace.companyCards.error.feedCouldNotBeLoadedMessage'),
+ },
+ },
+ },
+ },
+ },
+ },
+ ];
+
+ API.read(READ_COMMANDS.OPEN_POLICY_COMPANY_CARDS_FEED, parameters, {optimisticData, successData, failureData});
}
function openAssignFeedCardPage(policyID: string, feed: CompanyCardFeed, domainOrWorkspaceAccountID: number) {
diff --git a/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts b/src/libs/actions/OnyxDerived/configs/cardFeedErrors.ts
index 98b18acd3307..fadc0a491679 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 {filterInactiveCards, getCompanyCardFeedWithDomainID, isCardConnectionBroken} from '@libs/CardUtils';
import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig';
import CONST from '@src/CONST';
@@ -13,6 +13,7 @@ const DEFAULT_CARD_FEED_ERROR_STATE: CardFeedErrorState = {
shouldShowRBR: false,
hasFailedCardAssignments: false,
hasFeedErrors: false,
+ hasWorkspaceErrors: false,
isFeedConnectionBroken: false,
};
@@ -20,6 +21,9 @@ function getShouldShowRBR(state: Partial): boolean {
if (state.hasFeedErrors) {
return true;
}
+ if (state.hasWorkspaceErrors) {
+ return true;
+ }
if (state.hasFailedCardAssignments) {
return true;
}
@@ -32,6 +36,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 combinedCompanyCardFeeds = getCombinedCardFeedsFromAllFeeds(cardFeeds);
+ const workspaceCardFeedsStatus = getWorkspaceCardFeedsStatus(cardFeeds);
+
const cardFeedErrors: CardFeedErrorsObject = {};
const shouldShowRbrForWorkspaceAccountID: Record = {};
const shouldShowRbrForFeedNameWithDomainID: Record = {};
@@ -62,6 +68,9 @@ export default createOnyxDerivedValueConfig({
} as Errors;
const hasFeedErrors = feedNameWithDomainID ? !isEmptyObject(feedErrors) : false;
+ const workspaceErrors = workspaceCardFeedsStatus?.[workspaceAccountID]?.errors;
+ const hasWorkspaceErrors = !!workspaceErrors;
+
const hasFailedCardAssignments = !isEmptyObject(
failedCompanyCardAssignmentsPerFeed?.[`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${workspaceAccountID}_${feedNameWithDomainID}`],
);
@@ -86,6 +95,7 @@ export default createOnyxDerivedValueConfig({
const newFeedState: Omit = {
isFeedConnectionBroken: isFeedConnectionBroken || previousFeedErrors.isFeedConnectionBroken,
hasFeedErrors: hasFeedErrors || previousFeedErrors.hasFeedErrors,
+ hasWorkspaceErrors: hasWorkspaceErrors || previousFeedErrors.hasWorkspaceErrors,
hasFailedCardAssignments: hasFailedCardAssignments || previousFeedErrors.hasFailedCardAssignments,
};
@@ -96,6 +106,7 @@ export default createOnyxDerivedValueConfig({
shouldShowRBR,
cardErrors,
feedErrors,
+ workspaceErrors,
};
// Track cards with broken feed connection
@@ -108,10 +119,12 @@ 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/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
index bcffcee6ddf3..077d40c10dce 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
@@ -1,4 +1,4 @@
-import React, {useEffect, useState} from 'react';
+import React, {useCallback, useEffect, useState} from 'react';
import DecisionModal from '@components/DecisionModal';
import useAssignCard from '@hooks/useAssignCard';
import useCompanyCards from '@hooks/useCompanyCards';
@@ -43,24 +43,38 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
} = companyCards;
const domainOrWorkspaceAccountID = getDomainOrWorkspaceAccountID(workspaceAccountID, selectedFeed);
+
+ const loadPolicyCompanyCardsPage = useCallback(() => {
+ openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID, translate);
+ }, [domainOrWorkspaceAccountID, policyID, translate]);
+
const {isOffline} = useNetwork({
- onReconnect: () => openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID),
+ onReconnect: loadPolicyCompanyCardsPage,
});
const isLoading = !isOffline && (!allCardFeeds || (isFeedAdded && isLoadingOnyxValue(cardListMetadata)));
- useEffect(() => {
- openPolicyCompanyCardsPage(policyID, domainOrWorkspaceAccountID);
- }, [policyID, domainOrWorkspaceAccountID]);
useEffect(() => {
+ if (isOffline) {
+ return;
+ }
+
+ loadPolicyCompanyCardsPage();
+ }, [policyID, domainOrWorkspaceAccountID, loadPolicyCompanyCardsPage, isOffline]);
+
+ const loadPolicyCompanyCardsFeed = useCallback(() => {
if (isLoading || !bankName || isFeedPending) {
return;
}
const clientMemberEmails = Object.keys(getMemberAccountIDsForWorkspace(policy?.employeeList));
openWorkspaceMembersPage(policyID, clientMemberEmails);
- openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, bankName);
- }, [bankName, isLoading, policyID, isFeedPending, domainOrWorkspaceAccountID, policy?.employeeList]);
+ openPolicyCompanyCardsFeed(domainOrWorkspaceAccountID, policyID, bankName, translate);
+ }, [bankName, domainOrWorkspaceAccountID, isFeedPending, isLoading, policy?.employeeList, policyID, translate]);
+
+ useEffect(() => {
+ loadPolicyCompanyCardsFeed();
+ }, [loadPolicyCompanyCardsFeed]);
const [shouldShowOfflineModal, setShouldShowOfflineModal] = useState(false);
const {assignCard, isAssigningCardDisabled} = useAssignCard({feedName, policyID, setShouldShowOfflineModal});
@@ -85,6 +99,8 @@ function WorkspaceCompanyCardsPage({route}: WorkspaceCompanyCardsPageProps) {
companyCards={companyCards}
onAssignCard={assignCard}
isAssigningCardDisabled={isAssigningCardDisabled}
+ onReloadPage={loadPolicyCompanyCardsPage}
+ onReloadFeed={loadPolicyCompanyCardsFeed}
/>
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
index a3efa4417519..dbec2a801ff2 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
@@ -127,7 +127,7 @@ function WorkspaceCompanyCardsTableHeaderButtons({policyID, feedName, isLoading,
) : (
- {(isFeedConnectionBroken || hasFeedErrors) && (
+ {!isLoading && (isFeedConnectionBroken || hasFeedErrors) && (
void;
- /** Whether to disable assign card button */
- isAssigningCardDisabled: boolean;
+ /** On reload page callback */
+ onReloadPage: () => void;
+
+ /** On reload feed callback */
+ onReloadFeed: () => void;
};
-function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspaceAccountID, companyCards, onAssignCard, isAssigningCardDisabled}: WorkspaceCompanyCardsTableProps) {
+function WorkspaceCompanyCardsTable({
+ policyID,
+ isPolicyLoaded,
+ domainOrWorkspaceAccountID,
+ companyCards,
+ onAssignCard,
+ isAssigningCardDisabled,
+ onReloadPage,
+ onReloadFeed,
+}: WorkspaceCompanyCardsTableProps) {
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const {translate, localeCompare} = useLocalize();
@@ -61,6 +79,7 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
bankName,
cardList,
assignedCards,
+ workspaceCardFeedsStatus,
cardNames,
cardFeedType,
selectedFeed,
@@ -77,14 +96,34 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
const [failedCompanyCardAssignments] = useOnyx(`${ONYXKEYS.COLLECTION.FAILED_COMPANY_CARDS_ASSIGNMENTS}${domainOrWorkspaceAccountID}_${feedName ?? ''}`, {canBeMissing: true});
const hasNoAssignedCard = Object.keys(assignedCards ?? {}).length === 0;
- const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || !isPolicyLoaded || isLoadingOnyxValue(lastSelectedFeedMetadata);
+ const areWorkspaceCardFeedsLoading = !!workspaceCardFeedsStatus?.[domainOrWorkspaceAccountID]?.isLoading;
+ const workspaceCardFeedsErrors = workspaceCardFeedsStatus?.[domainOrWorkspaceAccountID]?.errors;
+
+ const selectedFeedStatus = selectedFeed?.status;
+ const selectedFeedErrors = selectedFeedStatus?.errors;
+
+ const [feedErrorKey, feedErrorMessage] = Object.entries(workspaceCardFeedsErrors ?? selectedFeedErrors ?? {}).at(0) ?? [];
+ const hasFeedErrors = !!feedErrorKey;
+
+ let feedErrorTitle: string | undefined;
+ let feedErrorReloadAction: (() => void) | undefined;
+ if (feedErrorKey === CONST.COMPANY_CARDS.WORKSPACE_FEEDS_LOAD_ERROR) {
+ feedErrorTitle = translate('workspace.companyCards.error.workspaceFeedsCouldNotBeLoadedTitle');
+ feedErrorReloadAction = onReloadPage;
+ } else if (feedErrorKey === CONST.COMPANY_CARDS.FEED_LOAD_ERROR) {
+ feedErrorTitle = translate('workspace.companyCards.error.feedCouldNotBeLoadedTitle');
+ feedErrorReloadAction = onReloadFeed;
+ }
+
+ const isLoadingFeed = (!feedName && isInitiallyLoadingFeeds) || !isPolicyLoaded || isLoadingOnyxValue(lastSelectedFeedMetadata) || !!selectedFeedStatus?.isLoading;
const isLoadingCards = cardFeedType === 'directFeed' ? selectedFeed?.accountList === undefined : isLoadingOnyxValue(cardListMetadata) || cardList === undefined;
+ const isLoadingPage = !isOffline && (isLoadingFeed || isLoadingOnyxValue(personalDetailsMetadata) || areWorkspaceCardFeedsLoading);
+ const isLoading = isLoadingPage || isLoadingFeed;
- const isLoadingPage = !isOffline && (isLoadingFeed || isLoadingOnyxValue(personalDetailsMetadata));
- const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed;
- const showTableControls = showCards && !!selectedFeed && !isLoadingCards;
- const showTableHeaderButtons = (showTableControls || isLoadingPage || isFeedPending) && !!feedName;
+ const showCards = !isInitiallyLoadingFeeds && !isFeedPending && !isNoFeed && !isLoadingFeed && !hasFeedErrors;
+ const showTableControls = showCards && !!selectedFeed && !isLoadingCards && !hasFeedErrors;
+ const showTableHeaderButtons = (showTableControls || isLoadingPage || isFeedPending || feedErrorKey === CONST.COMPANY_CARDS.FEED_LOAD_ERROR) && !!feedName;
const isGB = countryByIp === CONST.COUNTRY.GB;
const shouldShowGBDisclaimer = isGB && (isNoFeed || hasNoAssignedCard);
@@ -272,6 +311,7 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
isNarrowLayoutRef.current = true;
const activeSorting = tableRef.current?.getActiveSorting();
+ // eslint-disable-next-line react-hooks/set-state-in-effect
setActiveSortingInWideLayout(activeSorting);
tableRef.current?.updateSorting({columnKey: 'member', order: 'asc'});
return;
@@ -285,10 +325,15 @@ function WorkspaceCompanyCardsTable({policyID, isPolicyLoaded, domainOrWorkspace
tableRef.current?.updateSorting(activeSortingInWideLayout);
}, [activeSortingInWideLayout, shouldUseNarrowTableLayout]);
+ const illustrations = useMemoizedLazyIllustrations(['BrokenMagnifyingGlass']);
+ const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle({
+ addBottomSafeAreaPadding: true,
+ });
+
const headerButtonsComponent = showTableHeaderButtons ? (
{shouldRenderHeaderAsChild && headerButtonsComponent}
- {(isLoadingPage || isFeedPending || isNoFeed) && (
+ {(isLoading || isFeedPending || isNoFeed) && !feedErrorKey && (
- {isLoadingPage && }
+ {isLoading && }
- {isFeedPending && (
+ {!isLoading && isFeedPending && (
)}
- {isNoFeed && (
+ {!isLoading && isNoFeed && (
)}
+ {!!feedErrorKey && !isLoading && (
+
+
+
+
+
+
+ )}
+
{showCards && (
<>
{!shouldUseNarrowTableLayout && !isLoadingFeed && }
diff --git a/src/styles/index.ts b/src/styles/index.ts
index d27ec88e1299..504d787d97eb 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -2578,12 +2578,18 @@ const staticStyles = (theme: ThemeColors) =>
alignSelf: 'center',
},
- blockingErrorViewContainer: {
+ searchBlockingErrorViewContainer: {
paddingBottom: variables.contentHeaderHeight,
maxWidth: 475,
alignSelf: 'center',
},
+ companyCardsBlockingErrorViewContainer: {
+ maxWidth: 475,
+ alignSelf: 'center',
+ flex: undefined,
+ },
+
forcedBlockingViewContainer: {
...positioning.pFixed,
top: 0,
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index 4c509891322d..276a7ff7981c 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -324,6 +324,9 @@ export default {
errorPageIconWidth: 116,
errorPageIconHeight: 168,
+ companyCardsPageNotFoundIconWidth: 97,
+ companyCardsPageNotFoundIconHeight: 140,
+
h20: 20,
h28: 28,
h36: 36,
diff --git a/src/types/onyx/CardFeeds.ts b/src/types/onyx/CardFeeds.ts
index b566c1540010..0b5c580a90d7 100644
--- a/src/types/onyx/CardFeeds.ts
+++ b/src/types/onyx/CardFeeds.ts
@@ -154,6 +154,25 @@ type DomainSettings = {
};
};
+/** 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;
+
+/**
+ * Collection of card feeds status by domain ID
+ */
+type WorkspaceCardFeedsStatus = Record;
+
/** Card feeds model, including domain settings */
type CardFeeds = {
/** Feed settings */
@@ -167,16 +186,17 @@ type CardFeeds = {
/** Account details */
oAuthAccountDetails?: Partial>;
+ /** Collection of card feeds status by domain ID */
+ cardFeedsStatus?: WorkspaceCardFeedsStatus;
+
/** Email address of the technical contact for the domain */
technicalContactEmail?: string;
/** 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 = {
@@ -249,6 +269,9 @@ type CombinedCardFeed = CustomCardFeedData &
/** Feed name */
feed: CompanyCardFeedWithNumber;
+
+ /** Card feed status */
+ status?: CardFeedsStatus;
};
/** Card feeds combined by domain ID into one object */
@@ -264,6 +287,9 @@ export type {
DirectCardFeedData,
CardFeedProvider,
CardFeedData,
+ CardFeedsStatus,
+ CardFeedsStatusByDomainID,
+ WorkspaceCardFeedsStatus,
CompanyFeeds,
CompanyCardFeedWithDomainID,
CustomCardFeedData,
diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts
index 44d1df867c42..0dc59c2f4dc5 100644
--- a/src/types/onyx/DerivedValues.ts
+++ b/src/types/onyx/DerivedValues.ts
@@ -118,7 +118,7 @@ type CardFeedErrorState = {
/**
* Whether some workspace has errors.
*/
- // hasWorkspaceErrors: boolean;
+ hasWorkspaceErrors: boolean;
/**
* Whether some feed connection is broken.
@@ -138,6 +138,10 @@ type FeedErrors = CardFeedErrorState & {
* The errors of all cards for a specific feed within a workspace/domain.
*/
cardErrors: Record;
+ /**
+ * The errors of the workspace/domain.
+ */
+ workspaceErrors?: Errors;
};
/**
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 5f71f9a32e64..7f206fed568c 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -19,7 +19,18 @@ 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, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID} from './CardFeeds';
+import type {
+ AddNewCompanyCardFeed,
+ CardFeedsStatus,
+ CardFeedsStatusByDomainID,
+ CombinedCardFeed,
+ CombinedCardFeeds,
+ CompanyCardFeed,
+ CompanyCardFeedWithDomainID,
+ DomainSettings,
+ FundID,
+ WorkspaceCardFeedsStatus,
+} from './CardFeeds';
import type CardOnWaitlist from './CardOnWaitlist';
import type {CapturedLogs, Log} from './Console';
import type {CorpayFields, CorpayFormField} from './CorpayFields';
@@ -292,6 +303,9 @@ export type {
CancellationDetails,
ApprovalWorkflowOnyx,
CardFeeds,
+ CardFeedsStatus,
+ CardFeedsStatusByDomainID,
+ WorkspaceCardFeedsStatus,
DomainSettings,
SaveSearch,
RecentSearchItem,