diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index ad06f9fcb78cc..c10da1800694c 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -153,9 +153,15 @@ type MenuItemBaseProps = { /** Error to display at the bottom of the component */ errorText?: MaybePhraseKey; + /** Any additional styles to pass to error text. */ + errorTextStyle?: StyleProp; + /** Hint to display at the bottom of the component */ hintText?: MaybePhraseKey; + /** Should the error text red dot indicator be shown */ + shouldShowRedDotIndicator?: boolean; + /** A boolean flag that gives the icon a green fill if true */ success?: boolean; @@ -308,6 +314,8 @@ function MenuItem( helperText, helperTextStyle, errorText, + errorTextStyle, + shouldShowRedDotIndicator, hintText, success = false, focused = false, @@ -683,9 +691,9 @@ function MenuItem( {!!errorText && ( )} {!!hintText && ( diff --git a/src/languages/en.ts b/src/languages/en.ts index 580cfd258c9b8..f0ce58f823604 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2341,6 +2341,17 @@ export default { } } }, + syncError: (integration?: ConnectionName): string => { + switch (integration) { + case CONST.POLICY.CONNECTIONS.NAME.QBO: + return "Couldn't connect to QuickBooks Online."; + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return "Couldn't connect to Xero."; + default: { + return "Couldn't connect to integration."; + } + } + }, accounts: 'Chart of accounts', taxes: 'Taxes', imported: 'Imported', diff --git a/src/languages/es.ts b/src/languages/es.ts index 23d7756c5dbeb..945bef68b0344 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2345,6 +2345,17 @@ export default { } } }, + syncError: (integration?: ConnectionName): string => { + switch (integration) { + case CONST.POLICY.CONNECTIONS.NAME.QBO: + return 'No se puede conectar a QuickBooks Online.'; + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return 'No se puede conectar a Xero'; + default: { + return 'No se ha podido conectar a la integración.'; + } + } + }, accounts: 'Plan de cuentas', taxes: 'Impuestos', imported: 'Importado', diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 424016af38af1..9b463cf1780c4 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -1,4 +1,4 @@ -import type {OnyxUpdate} from 'react-native-onyx'; +import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { @@ -13,6 +13,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ConnectionName, Connections, PolicyConnectionName} from '@src/types/onyx/Policy'; +import type Policy from '@src/types/onyx/Policy'; function removePolicyConnection(policyID: string, connectionName: PolicyConnectionName) { const optimisticData: OnyxUpdate[] = [ @@ -231,4 +232,8 @@ function updateManyPolicyConnectionConfigs, connectionName: PolicyConnectionName): boolean { + return policy?.connections?.[connectionName]?.lastSync?.isSuccessful === false; +} + +export {removePolicyConnection, updatePolicyConnectionConfig, updateManyPolicyConnectionConfigs, hasSynchronizationError, syncConnection}; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 9296b30a38d73..d33611ec09f25 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -26,7 +26,7 @@ import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {removePolicyConnection, syncConnection} from '@libs/actions/connections'; +import {hasSynchronizationError, removePolicyConnection, syncConnection} from '@libs/actions/connections'; import {findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroTenants} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -41,7 +41,7 @@ import type {PolicyConnectionName} from '@src/types/onyx/Policy'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; -type MenuItemData = MenuItemProps & {pendingAction?: OfflineWithFeedbackProps['pendingAction']}; +type MenuItemData = MenuItemProps & {pendingAction?: OfflineWithFeedbackProps['pendingAction']; errors?: OfflineWithFeedbackProps['errors']}; type PolicyAccountingPageOnyxProps = { connectionSyncProgress: OnyxEntry; @@ -163,16 +163,19 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting if (!connectedIntegration) { return []; } + const shouldShowSynchronizationError = hasSynchronizationError(policy, connectedIntegration); const integrationData = accountingIntegrationData(connectedIntegration, policyID, translate); const iconProps = integrationData?.icon ? {icon: integrationData.icon, iconType: CONST.ICON_TYPE_AVATAR} : {}; return [ { ...iconProps, interactive: false, - wrapperStyle: [styles.sectionMenuItemTopDescription], + wrapperStyle: [styles.sectionMenuItemTopDescription, shouldShowSynchronizationError && styles.pb0], shouldShowRightComponent: true, title: integrationData?.title, - + errorText: shouldShowSynchronizationError ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, + errorTextStyle: [styles.mt5], + shouldShowRedDotIndicator: true, description: isSyncInProgress ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) : translate('workspace.accounting.lastSync'), @@ -199,7 +202,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting ), }, - ...(policyConnectedToXero + ...(policyConnectedToXero && !shouldShowSynchronizationError ? [ { description: translate('workspace.xero.organization'), @@ -220,7 +223,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting }, ] : []), - ...(isEmptyObject(policy?.connections) + ...(isEmptyObject(policy?.connections) || shouldShowSynchronizationError ? [] : [ { @@ -250,21 +253,25 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting ]), ]; }, [ - connectedIntegration, - connectionSyncProgress?.stageInProgress, - currentXeroOrganization, - currentXeroOrganizationName, - tenants, + policy, isSyncInProgress, - overflowMenu, - policy?.connections, - policyConnectedToXero, + connectedIntegration, policyID, - styles, + translate, + styles.sectionMenuItemTopDescription, + styles.pb0, + styles.mt5, + styles.popoverMenuIcon, + styles.fontWeightNormal, + connectionSyncProgress?.stageInProgress, theme.spinner, + overflowMenu, threeDotsMenuPosition, - translate, + policyConnectedToXero, + currentXeroOrganizationName, + tenants.length, accountingIntegrations, + currentXeroOrganization?.id, ]); const otherIntegrationsItems = useMemo(() => {