Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proper offline state handling for XeroOrganizationConfigurationPage #41983

1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@ const CONST = {
SYNC: 'sync',
ENABLE_NEW_CATEGORIES: 'enableNewCategories',
EXPORT: 'export',
TENANT_ID: 'tenantID',
IMPORT_CUSTOMERS: 'importCustomers',
IMPORT_TAX_RATES: 'importTaxRates',
INVOICE_STATUS: {
Expand Down
6 changes: 5 additions & 1 deletion src/components/ConnectionLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type ConnectionLayoutProps = {
/** Style of the title text */
titleStyle?: StyleProp<TextStyle> | undefined;

/** Whether to include safe area padding bottom or not */
shouldIncludeSafeAreaPaddingBottom?: boolean;

/** Whether to use ScrollView or not */
shouldUseScrollView?: boolean;
};
Expand Down Expand Up @@ -72,6 +75,7 @@ function ConnectionLayout({
featureName,
contentContainerStyle,
titleStyle,
shouldIncludeSafeAreaPaddingBottom,
shouldUseScrollView = true,
}: ConnectionLayoutProps) {
const {translate} = useLocalize();
Expand All @@ -95,7 +99,7 @@ function ConnectionLayout({
featureName={featureName}
>
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
includeSafeAreaPaddingBottom={!!shouldIncludeSafeAreaPaddingBottom}
shouldEnableMaxHeight
testID={displayName}
>
Expand Down
52 changes: 28 additions & 24 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {useMemo, useRef, useState} from 'react';
import {ActivityIndicator, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import AccountingListSkeletonView from '@components/AccountingListSkeletonView';
import CollapsibleSection from '@components/CollapsibleSection';
import ConfirmModal from '@components/ConfirmModal';
import ConnectToQuickbooksOnlineButton from '@components/ConnectToQuickbooksOnlineButton';
Expand All @@ -12,7 +11,10 @@ import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {MenuItemProps} from '@components/MenuItem';
import MenuItem from '@components/MenuItem';
import MenuItemList from '@components/MenuItemList';
import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Section from '@components/Section';
Expand Down Expand Up @@ -40,6 +42,8 @@ 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 PolicyAccountingPageOnyxProps = {
connectionSyncProgress: OnyxEntry<PolicyConnectionSyncProgress>;
};
Expand Down Expand Up @@ -101,7 +105,7 @@ function accountingIntegrationData(
}
}

function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataFetchNeeded}: PolicyAccountingPageProps) {
function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccountingPageProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand Down Expand Up @@ -141,7 +145,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF
[translate, policyID, isOffline],
);

const connectionsMenuItems: MenuItemProps[] = useMemo(() => {
const connectionsMenuItems: MenuItemData[] = useMemo(() => {
if (isEmptyObject(policy?.connections) && !isSyncInProgress) {
return accountingIntegrations.map((integration) => {
const integrationData = accountingIntegrationData(integration, policyID, translate);
Expand Down Expand Up @@ -212,6 +216,8 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF
}
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? ''));
},
pendingAction: policy?.connections?.xero?.config?.pendingFields?.tenantID,
brickRoadIndicator: policy?.connections?.xero?.config?.errorFields?.tenantID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
},
]
: []),
Expand Down Expand Up @@ -336,30 +342,28 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF
titleStyles={styles.accountSettingsSectionTitle}
childrenStyles={styles.pt5}
>
{isConnectionDataFetchNeeded ? (
<View style={styles.mnh20}>
<AccountingListSkeletonView shouldAnimate />
</View>
) : (
<>
{connectionsMenuItems.map((menuItem) => (
<OfflineWithFeedback pendingAction={menuItem.pendingAction}>
<MenuItem
key={menuItem.title}
brickRoadIndicator={menuItem.brickRoadIndicator}
// eslint-disable-next-line react/jsx-props-no-spreading
{...menuItem}
/>
</OfflineWithFeedback>
))}
{otherIntegrationsItems && (
<CollapsibleSection
title={translate('workspace.accounting.other')}
wrapperStyle={[styles.pr3, styles.mt5, styles.pv3]}
titleStyle={[styles.textNormal, styles.colorMuted]}
textStyle={[styles.flex1, styles.userSelectNone, styles.textNormal, styles.colorMuted]}
>
<MenuItemList
menuItems={connectionsMenuItems}
menuItems={otherIntegrationsItems}
shouldUseSingleExecution
/>
{otherIntegrationsItems && (
<CollapsibleSection
title={translate('workspace.accounting.other')}
wrapperStyle={[styles.pr3, styles.mt5, styles.pv3]}
titleStyle={[styles.textNormal, styles.colorMuted]}
textStyle={[styles.flex1, styles.userSelectNone, styles.textNormal, styles.colorMuted]}
>
<MenuItemList
menuItems={otherIntegrationsItems}
shouldUseSingleExecution
/>
</CollapsibleSection>
)}
</>
</CollapsibleSection>
)}
</Section>
</View>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useMemo} from 'react';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import ConnectionLayout from '@components/ConnectionLayout';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {updatePolicyConnectionConfig} from '@libs/actions/connections';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {findCurrentXeroOrganization, getXeroTenants} from '@libs/PolicyUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import withPolicy from '@pages/workspace/withPolicy';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
Expand All @@ -30,7 +30,8 @@ function XeroOrganizationConfigurationPage({
const {translate} = useLocalize();
const styles = useThemeStyles();
const tenants = useMemo(() => getXeroTenants(policy ?? undefined), [policy]);
const currentXeroOrganization = findCurrentXeroOrganization(tenants, policy?.connections?.xero?.config?.tenantID);
const xeroConfig = policy?.connections?.xero?.config;
const currentXeroOrganization = findCurrentXeroOrganization(tenants, xeroConfig?.tenantID);

const policyID = policy?.id ?? '';

Expand All @@ -46,33 +47,34 @@ function XeroOrganizationConfigurationPage({
return;
}

updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, 'tenantID', keyForList);
updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.TENANT_ID, keyForList);
Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID));
};

return (
<AccessOrNotFoundWrapper
<ConnectionLayout
displayName={XeroOrganizationConfigurationPage.displayName}
headerTitle="workspace.xero.organization"
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]}
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
shouldIncludeSafeAreaPaddingBottom
>
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={XeroOrganizationConfigurationPage.displayName}
<OfflineWithFeedback
errors={ErrorUtils.getLatestErrorField(xeroConfig ?? {}, CONST.XERO_CONFIG.TENANT_ID)}
errorRowStyles={[styles.ph5, styles.mt2]}
onClose={() => Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.TENANT_ID)}
>
<HeaderWithBackButton title={translate('workspace.xero.organization')} />
<ScrollView contentContainerStyle={styles.pb2}>
<Text style={[styles.ph5, styles.pb5]}>{translate('workspace.xero.organizationDescription')}</Text>
<SelectionList
ListItem={RadioListItem}
onSelectRow={saveSelection}
sections={[{data: sections}]}
initiallyFocusedOptionKey={currentXeroOrganization?.id}
/>
</ScrollView>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
<Text style={[styles.ph5, styles.pb5]}>{translate('workspace.xero.organizationDescription')}</Text>
<SelectionList
containerStyle={styles.pb0}
ListItem={RadioListItem}
onSelectRow={saveSelection}
sections={[{data: sections}]}
initiallyFocusedOptionKey={currentXeroOrganization?.id}
/>
</OfflineWithFeedback>
</ConnectionLayout>
);
}

Expand Down
Loading