From ffc39909241c47473a6d7dbd8c3d637c7a1852d6 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Tue, 10 Mar 2026 12:02:28 +0100 Subject: [PATCH 1/3] Add reasonAttributes to accounting and company cards skeleton usages --- .../workspace/accounting/PolicyAccountingPage.tsx | 10 +++++++++- .../accounting/qbd/QuickBooksDesktopSetupPage.tsx | 8 +++++++- .../companyCards/BankConnection/index.native.tsx | 14 +++++++++++++- .../companyCards/BankConnection/index.tsx | 8 ++++++++ .../WorkspaceCompanyCardDetailsPage.tsx | 13 ++++++++++++- .../companyCards/addNew/PlaidConnectionStep.tsx | 10 +++++++++- .../assignCard/TransactionStartDateStep.tsx | 6 ++++++ 7 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index b25342b0110c..9746737eb075 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -47,6 +47,7 @@ import { settingsPendingAction, shouldShowSyncError, } from '@libs/PolicyUtils'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -417,6 +418,10 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { }, ]; + const syncActivityReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'PolicyAccountingPage.connectionsMenuItems', + isSyncInProgress, + }; return [ { ...iconProps, @@ -429,7 +434,10 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { shouldShowRedDotIndicator: true, description: connectionMessage, rightComponent: isSyncInProgress ? ( - + ) : ( {shouldShowError && ( @@ -100,7 +106,7 @@ function RequireQuickBooksDesktopModal({route}: RequireQuickBooksDesktopModalPro {translate('workspace.qbd.setupPage.body')} {!hasResultOfFetchingSetupLink ? ( - + ) : ( ; + const fullscreenReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'BankConnection', + }; + const activityReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'BankConnection', + isAllFeedsResultLoading, + isBlockedToAddNewFeedsWithoutFeed: isBlockedToAddNewFeeds && !feed, + isConnectionCompleted, + isPlaid, + }; + const renderLoading = () => ; useEffect(() => { if (!policyID || !isBlockedToAddNewFeeds || feed) { @@ -189,6 +200,7 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti )} {isNewFeedHasError && ( diff --git a/src/pages/workspace/companyCards/BankConnection/index.tsx b/src/pages/workspace/companyCards/BankConnection/index.tsx index 90d9415fd6a4..79e91521b3ca 100644 --- a/src/pages/workspace/companyCards/BankConnection/index.tsx +++ b/src/pages/workspace/companyCards/BankConnection/index.tsx @@ -19,6 +19,7 @@ import useUpdateFeedBrokenConnection from '@hooks/useUpdateFeedBrokenConnection' import {setAssignCardStepAndData} from '@libs/actions/CompanyCards'; import {checkIfNewFeedConnected, getBankName, getCompanyCardFeed, isSelectedFeedExpired} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import WorkspaceCompanyCardsErrorConfirmation from '@pages/workspace/companyCards/WorkspaceCompanyCardsErrorConfirmation'; @@ -214,10 +215,17 @@ function BankConnection({policyID: policyIDFromProps, feed, route}: BankConnecti /> ); } + const activityReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'BankConnection', + isPlaid, + isAllFeedsResultLoading, + isBlockedToAddNewFeedsWithoutFeed: isBlockedToAddNewFeeds && !feed, + }; return ( ); }; diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 84e6eba2bb76..b61547bbf84b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -30,6 +30,7 @@ import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {getConnectedIntegration} from '@libs/PolicyUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Navigation from '@navigation/Navigation'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -120,6 +121,11 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag return format(getLocalDateFromDatetime(card?.lastScrape), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); }, [getLocalDateFromDatetime, card?.lastScrape, translate]); + const lastUpdatedActivityReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'WorkspaceCompanyCardDetailsPage', + isLoadingLastUpdated: card?.isLoadingLastUpdated, + }; + // Don't show NotFoundPage if card is being unassigned or data is still loading if ((!card && !isUnassigningRef.current && !isLoadingOnyxValue(allBankCardsMetadata) && !isLoadingOnyxValue(cardListMetadata)) || (isCardBeingUnassigned && !isUnassigningRef.current)) { return ; @@ -221,7 +227,12 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag ) : null} } + rightComponent={ + + } description={translate('workspace.moreFeatures.companyCards.lastUpdated')} title={card?.isLoadingLastUpdated ? translate('workspace.moreFeatures.companyCards.updating') : lastScrape} interactive={false} diff --git a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx index 348da6f18191..c6b9762af087 100644 --- a/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx +++ b/src/pages/workspace/companyCards/addNew/PlaidConnectionStep.tsx @@ -16,6 +16,7 @@ import {setAddNewCompanyCardStepAndData, setAssignCardStepAndData} from '@libs/a import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Navigation from '@navigation/Navigation'; import {handleRestrictedEvent} from '@userActions/App'; import {setPlaidEvent} from '@userActions/BankAccounts'; @@ -194,9 +195,16 @@ function PlaidConnectionStep({feed, policyID, onExit}: {feed?: CompanyCardFeedWi } if (plaidData?.isLoading) { + const reasonAttributes: SkeletonSpanReasonAttributes = { + context: 'PlaidConnectionStep.renderPlaidLink', + isPlaidDataLoading: plaidData?.isLoading, + }; return ( - + ); } diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx index ac2a94b1a425..eb3f010cbec4 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateStep.tsx @@ -11,6 +11,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import Navigation from '@navigation/Navigation'; import {setAssignCardStepAndData} from '@userActions/CompanyCards'; @@ -85,6 +86,10 @@ function TransactionStartDateStep() { ]; const isLoading = isLoadingOnyxValue(assignCardMeta); + const activityReasonAttributes: SkeletonSpanReasonAttributes = { + context: 'TransactionStartDateStep', + isLoading, + }; return ( ) : ( <> From 258a00eb9ecb08ff95c948b49050b200006ffc84 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Tue, 10 Mar 2026 13:06:59 +0100 Subject: [PATCH 2/3] Fix inverted hasResultOfFetchingSetupLink in reasonAttributes --- .../qbd/QuickBooksDesktopSetupPage.tsx | 317 ++++++++++-------- 1 file changed, 183 insertions(+), 134 deletions(-) diff --git a/src/pages/workspace/accounting/qbd/QuickBooksDesktopSetupPage.tsx b/src/pages/workspace/accounting/qbd/QuickBooksDesktopSetupPage.tsx index 133ecbbe7b2d..ff6c8df24c2d 100644 --- a/src/pages/workspace/accounting/qbd/QuickBooksDesktopSetupPage.tsx +++ b/src/pages/workspace/accounting/qbd/QuickBooksDesktopSetupPage.tsx @@ -1,150 +1,199 @@ -import React, {useCallback, useEffect, useState} from 'react'; -import {View} from 'react-native'; -import ActivityIndicator from '@components/ActivityIndicator'; -import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; -import Button from '@components/Button'; -import CopyTextToClipboard from '@components/CopyTextToClipboard'; -import FixedFooter from '@components/FixedFooter'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Icon from '@components/Icon'; -import ImageSVG from '@components/ImageSVG'; -import RenderHTML from '@components/RenderHTML'; -import ScreenWrapper from '@components/ScreenWrapper'; -import Text from '@components/Text'; -import useEnvironment from '@hooks/useEnvironment'; -import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; -import {setConnectionError} from '@userActions/connections'; -import {getQuickbooksDesktopCodatSetupLink} from '@userActions/connections/QuickbooksDesktop'; -import {enablePolicyTaxes} from '@userActions/Policy/Policy'; -import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; +import React, { useCallback, useEffect, useState } from "react"; +import { View } from "react-native"; +import ActivityIndicator from "@components/ActivityIndicator"; +import FullPageOfflineBlockingView from "@components/BlockingViews/FullPageOfflineBlockingView"; +import Button from "@components/Button"; +import CopyTextToClipboard from "@components/CopyTextToClipboard"; +import FixedFooter from "@components/FixedFooter"; +import HeaderWithBackButton from "@components/HeaderWithBackButton"; +import Icon from "@components/Icon"; +import ImageSVG from "@components/ImageSVG"; +import RenderHTML from "@components/RenderHTML"; +import ScreenWrapper from "@components/ScreenWrapper"; +import Text from "@components/Text"; +import useEnvironment from "@hooks/useEnvironment"; +import { useMemoizedLazyIllustrations } from "@hooks/useLazyAsset"; +import useLocalize from "@hooks/useLocalize"; +import useNetwork from "@hooks/useNetwork"; +import useThemeStyles from "@hooks/useThemeStyles"; +import Navigation from "@libs/Navigation/Navigation"; +import type { PlatformStackScreenProps } from "@libs/Navigation/PlatformStackNavigation/types"; +import type { SettingsNavigatorParamList } from "@libs/Navigation/types"; +import type { SkeletonSpanReasonAttributes } from "@libs/telemetry/useSkeletonSpan"; +import { setConnectionError } from "@userActions/connections"; +import { getQuickbooksDesktopCodatSetupLink } from "@userActions/connections/QuickbooksDesktop"; +import { enablePolicyTaxes } from "@userActions/Policy/Policy"; +import CONST from "@src/CONST"; +import ROUTES from "@src/ROUTES"; +import type SCREENS from "@src/SCREENS"; -type RequireQuickBooksDesktopModalProps = PlatformStackScreenProps; +type RequireQuickBooksDesktopModalProps = PlatformStackScreenProps< + SettingsNavigatorParamList, + typeof SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_SETUP_REQUIRED_DEVICE_MODAL +>; -function RequireQuickBooksDesktopModal({route}: RequireQuickBooksDesktopModalProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - const {environmentURL} = useEnvironment(); - const illustrations = useMemoizedLazyIllustrations(['BrokenMagnifyingGlass', 'LaptopWithSecondScreenSync']); - const policyID: string = route.params.policyID; - const [hasError, setHasError] = useState(false); - const [codatSetupLink, setCodatSetupLink] = useState(''); - const hasResultOfFetchingSetupLink = !!codatSetupLink || hasError; +function RequireQuickBooksDesktopModal({ + route, +}: RequireQuickBooksDesktopModalProps) { + const { translate } = useLocalize(); + const styles = useThemeStyles(); + const { environmentURL } = useEnvironment(); + const illustrations = useMemoizedLazyIllustrations([ + "BrokenMagnifyingGlass", + "LaptopWithSecondScreenSync", + ]); + const policyID: string = route.params.policyID; + const [hasError, setHasError] = useState(false); + const [codatSetupLink, setCodatSetupLink] = useState(""); + const hasResultOfFetchingSetupLink = !!codatSetupLink || hasError; - const fetchSetupLink = useCallback(() => { - setHasError(false); - // eslint-disable-next-line rulesdir/no-thenable-actions-in-views - getQuickbooksDesktopCodatSetupLink(policyID).then((response) => { - if (!response?.jsonCode) { - return; - } + const fetchSetupLink = useCallback(() => { + setHasError(false); + // eslint-disable-next-line rulesdir/no-thenable-actions-in-views + getQuickbooksDesktopCodatSetupLink(policyID).then((response) => { + if (!response?.jsonCode) { + return; + } - if (response.jsonCode === CONST.JSON_CODE.SUCCESS) { - setCodatSetupLink(String(response?.setupUrl ?? '')); - } else { - setConnectionError(policyID, CONST.POLICY.CONNECTIONS.NAME.QBD, translate('workspace.qbd.setupPage.setupErrorTitle')); - setHasError(true); - } - }); - }, [policyID, translate]); + if (response.jsonCode === CONST.JSON_CODE.SUCCESS) { + setCodatSetupLink(String(response?.setupUrl ?? "")); + } else { + setConnectionError( + policyID, + CONST.POLICY.CONNECTIONS.NAME.QBD, + translate("workspace.qbd.setupPage.setupErrorTitle"), + ); + setHasError(true); + } + }); + }, [policyID, translate]); - useEffect(() => { - // Since QBD doesn't support Taxes, we should disable them from the LHN when connecting to QBD - enablePolicyTaxes(policyID, false); + useEffect(() => { + // Since QBD doesn't support Taxes, we should disable them from the LHN when connecting to QBD + enablePolicyTaxes(policyID, false); - fetchSetupLink(); - // disabling this rule, as we want this to run only on the first render - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + fetchSetupLink(); + // disabling this rule, as we want this to run only on the first render + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - useNetwork({ - onReconnect: () => { - if (hasResultOfFetchingSetupLink) { - return; - } - fetchSetupLink(); - }, - }); + useNetwork({ + onReconnect: () => { + if (hasResultOfFetchingSetupLink) { + return; + } + fetchSetupLink(); + }, + }); - const shouldShowError = hasError; + const shouldShowError = hasError; - const activityReasonAttributes: SkeletonSpanReasonAttributes = { - context: 'RequireQuickBooksDesktopModal', - hasResultOfFetchingSetupLink: !hasResultOfFetchingSetupLink, - }; + const activityReasonAttributes: SkeletonSpanReasonAttributes = { + context: "RequireQuickBooksDesktopModal", + hasResultOfFetchingSetupLink, + }; - const children = ( - <> - {shouldShowError && ( - - - {translate('workspace.qbd.setupPage.setupErrorTitle')} - - - - - )} - {!shouldShowError && ( - - - - + const children = ( + <> + {shouldShowError && ( + + + + {translate("workspace.qbd.setupPage.setupErrorTitle")} + + + + + + )} + {!shouldShowError && ( + + + + - {translate('workspace.qbd.setupPage.title')} - {translate('workspace.qbd.setupPage.body')} - - {!hasResultOfFetchingSetupLink ? ( - - ) : ( - - )} - - -