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

[QBO Export] feat: handling auto-sync errors #45251

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
233a37a
feat: display RBR for auto-sync errors
kosmydel Jul 11, 2024
43e5fe1
feat: reconnect integrations
kosmydel Jul 11, 2024
0fecab4
refactor: add AccountingContext
kosmydel Jul 12, 2024
4c5e3fd
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 15, 2024
06b87f7
fix
kosmydel Jul 15, 2024
300be83
refactor: move context
kosmydel Jul 15, 2024
1c7f281
refactor: rename buttons to flows
kosmydel Jul 15, 2024
176954e
cleanup
kosmydel Jul 15, 2024
5ebb813
address internal review
kosmydel Jul 15, 2024
fa5b87e
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 16, 2024
1a936f1
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 16, 2024
55cb88b
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 17, 2024
1344312
add refs to NetSuite
kosmydel Jul 17, 2024
0119167
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 18, 2024
60ec0dd
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Jul 22, 2024
0467385
fix infinite API calls
war-in Jul 23, 2024
269e573
add `shouldStartIntegrationFlow` flag to all flows
war-in Jul 23, 2024
4d25ba2
fix double confirmation modal on connect to sage intacct
war-in Jul 23, 2024
f051b0b
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Jul 26, 2024
a43e8da
fix post-merge errors
war-in Jul 26, 2024
5f47f7c
fix double confirmation issue
war-in Jul 26, 2024
b96632c
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Jul 26, 2024
8925771
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Jul 29, 2024
b46a61c
refactor
kosmydel Jul 30, 2024
44b9703
Merge branch 'main' into @kosmydel/qbo-export/auto-sync-errors
kosmydel Jul 30, 2024
fae49c1
cleanup
kosmydel Jul 30, 2024
b2c159b
refactor disconnect modals
kosmydel Jul 30, 2024
de1c056
add requiresControlPolicy flag
kosmydel Jul 30, 2024
57b0add
use correct navigation for upgrade workspace
war-in Jul 30, 2024
ad8ba6a
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Jul 31, 2024
a5d10e0
move navigation alias to policy accounting page
war-in Jul 31, 2024
4d97f6e
set active integration to undefined when disconnecting from another i…
war-in Jul 31, 2024
312f925
Revert "set active integration to undefined when disconnecting from a…
war-in Jul 31, 2024
d2cdfaf
measure ref position on every render -> we can properly show popover …
war-in Jul 31, 2024
32d47f6
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 1, 2024
f1e2688
after-merge fixes
war-in Aug 1, 2024
d23f888
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 6, 2024
daa6d16
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 6, 2024
9a1d35b
Revert "feat: display RBR for auto-sync errors"
war-in Aug 6, 2024
ee23986
post merge fixes
war-in Aug 6, 2024
41bc1d2
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 8, 2024
6c1c3cf
do not show enter credentials menu for SageIntacct and Netsuite integ…
war-in Aug 8, 2024
970f83e
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 9, 2024
21cc77c
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 12, 2024
1b1a5a4
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 13, 2024
bbb08ab
close modal before reconnecting
war-in Aug 13, 2024
45ebd52
fix lint
war-in Aug 13, 2024
34368ae
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 14, 2024
5058706
post-merge fixes
war-in Aug 14, 2024
55881cf
review comment
war-in Aug 19, 2024
3143a3a
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/auto-sync-er…
war-in Aug 19, 2024
451bb3d
rename integrationRefs
war-in Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 0 additions & 135 deletions src/components/ConnectToNetSuiteButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +0,0 @@
import React, {useRef, useState} from 'react';
import type {View} from 'react-native';
import AccountingConnectionConfirmationModal from '@components/AccountingConnectionConfirmationModal';
import Button from '@components/Button';
import * as Expensicons from '@components/Icon/Expensicons';
import PopoverMenu from '@components/PopoverMenu';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePolicy from '@hooks/usePolicy';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {removePolicyConnection} from '@libs/actions/connections';
import {getAdminPoliciesConnectedToNetSuite} from '@libs/actions/Policy/Policy';
import Navigation from '@libs/Navigation/Navigation';
import {isControlPolicy} from '@libs/PolicyUtils';
import type {AnchorPosition} from '@styles/index';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {ConnectToNetSuiteButtonProps} from './types';

function ConnectToNetSuiteButton({policyID, shouldDisconnectIntegrationBeforeConnecting, integrationToDisconnect}: ConnectToNetSuiteButtonProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const policy = usePolicy(policyID);

const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false);

const hasPoliciesConnectedToNetSuite = !!getAdminPoliciesConnectedToNetSuite()?.length;
const {isSmallScreenWidth} = useWindowDimensions();
const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false);
const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState<AnchorPosition>({horizontal: 0, vertical: 0});
const threeDotsMenuContainerRef = useRef<View>(null);
const connectionOptions = [
{
icon: Expensicons.LinkCopy,
text: translate('workspace.common.createNewConnection'),
onSelected: () => {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
setIsReuseConnectionsPopoverOpen(false);
},
},
{
icon: Expensicons.Copy,
text: translate('workspace.common.reuseExistingConnection'),
onSelected: () => {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_EXISTING_CONNECTIONS.getRoute(policyID));
setIsReuseConnectionsPopoverOpen(false);
},
},
];

return (
<>
<Button
onPress={() => {
if (!isControlPolicy(policy)) {
Navigation.navigate(
ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.netsuite.alias, ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID)),
);
return;
}

if (shouldDisconnectIntegrationBeforeConnecting && integrationToDisconnect) {
setIsDisconnectModalOpen(true);
return;
}

if (!hasPoliciesConnectedToNetSuite) {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
return;
}

if (!isSmallScreenWidth) {
threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => {
setReuseConnectionPopoverPosition({
horizontal: x + width,
vertical: y + height,
});
});
}
setIsReuseConnectionsPopoverOpen(true);
}}
text={translate('workspace.accounting.setup')}
style={styles.justifyContentCenter}
small
isDisabled={isOffline}
ref={threeDotsMenuContainerRef}
/>
<PopoverMenu
isVisible={isReuseConnectionsPopoverOpen}
onClose={() => {
setIsReuseConnectionsPopoverOpen(false);
}}
withoutOverlay
menuItems={connectionOptions}
onItemSelected={(item) => {
if (!item?.onSelected) {
return;
}
item.onSelected();
}}
anchorPosition={reuseConnectionPopoverPosition}
anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP}}
anchorRef={threeDotsMenuContainerRef}
/>
{shouldDisconnectIntegrationBeforeConnecting && isDisconnectModalOpen && integrationToDisconnect && (
<AccountingConnectionConfirmationModal
onConfirm={() => {
removePolicyConnection(policyID, integrationToDisconnect);
setIsDisconnectModalOpen(false);

if (!hasPoliciesConnectedToNetSuite) {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
return;
}
if (!isSmallScreenWidth) {
threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => {
setReuseConnectionPopoverPosition({
horizontal: x + width,
vertical: y + height,
});
});
}
setIsReuseConnectionsPopoverOpen(true);
}}
integrationToConnect={CONST.POLICY.CONNECTIONS.NAME.NETSUITE}
onCancel={() => setIsDisconnectModalOpen(false)}
/>
)}
</>
);
}

export default ConnectToNetSuiteButton;
86 changes: 86 additions & 0 deletions src/components/ConnectToNetSuiteFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, {useEffect, useState} from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import PopoverMenu from '@components/PopoverMenu';
import useLocalize from '@hooks/useLocalize';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {getAdminPoliciesConnectedToNetSuite} from '@libs/actions/Policy/Policy';
import Navigation from '@libs/Navigation/Navigation';
import {useAccountingContext} from '@pages/workspace/accounting/AccountingContext';
import type {AnchorPosition} from '@styles/index';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {ConnectToNetSuiteFlowProps} from './types';

function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) {
const {translate} = useLocalize();

const hasPoliciesConnectedToNetSuite = !!getAdminPoliciesConnectedToNetSuite()?.length;
const {isSmallScreenWidth} = useWindowDimensions();
const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false);
const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState<AnchorPosition>({horizontal: 0, vertical: 0});
const {popoverAnchorRefs} = useAccountingContext();

const threeDotsMenuContainerRef = popoverAnchorRefs?.current?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE];

const connectionOptions = [
{
icon: Expensicons.LinkCopy,
text: translate('workspace.common.createNewConnection'),
onSelected: () => {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
setIsReuseConnectionsPopoverOpen(false);
},
},
{
icon: Expensicons.Copy,
text: translate('workspace.common.reuseExistingConnection'),
onSelected: () => {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_EXISTING_CONNECTIONS.getRoute(policyID));
setIsReuseConnectionsPopoverOpen(false);
},
},
];

useEffect(() => {
if (!hasPoliciesConnectedToNetSuite) {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
return;
}
setIsReuseConnectionsPopoverOpen(true);
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (threeDotsMenuContainerRef) {
if (!isSmallScreenWidth) {
threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => {
setReuseConnectionPopoverPosition({
horizontal: x + width,
vertical: y + height,
});
});
}

return (
<PopoverMenu
isVisible={isReuseConnectionsPopoverOpen}
onClose={() => {
setIsReuseConnectionsPopoverOpen(false);
}}
withoutOverlay
menuItems={connectionOptions}
onItemSelected={(item) => {
if (!item?.onSelected) {
return;
}
item.onSelected();
}}
anchorPosition={reuseConnectionPopoverPosition}
anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP}}
anchorRef={threeDotsMenuContainerRef}
/>
);
}
}

export default ConnectToNetSuiteFlow;
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {PolicyConnectionName} from '@src/types/onyx/Policy';

type ConnectToNetSuiteButtonProps = {
type ConnectToNetSuiteFlowProps = {
policyID: string;
shouldDisconnectIntegrationBeforeConnecting?: boolean;
integrationToDisconnect?: PolicyConnectionName;
};

// eslint-disable-next-line import/prefer-default-export
export type {ConnectToNetSuiteButtonProps};
export type {ConnectToNetSuiteFlowProps};
112 changes: 0 additions & 112 deletions src/components/ConnectToQuickbooksOnlineButton/index.native.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +0,0 @@
import React, {useRef, useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {WebView} from 'react-native-webview';
import AccountingConnectionConfirmationModal from '@components/AccountingConnectionConfirmationModal';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import Button from '@components/Button';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import {removePolicyConnection} from '@libs/actions/connections';
import {getQuickbooksOnlineSetupLink} from '@libs/actions/connections/QuickbooksOnline';
import * as PolicyAction from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Session} from '@src/types/onyx';
import type {ConnectToQuickbooksOnlineButtonProps} from './types';

type ConnectToQuickbooksOnlineButtonOnyxProps = {
/** Session info for the currently logged in user. */
session: OnyxEntry<Session>;
};

const renderLoading = () => <FullScreenLoadingIndicator />;

function ConnectToQuickbooksOnlineButton({
policyID,
session,
shouldDisconnectIntegrationBeforeConnecting,
integrationToDisconnect,
}: ConnectToQuickbooksOnlineButtonProps & ConnectToQuickbooksOnlineButtonOnyxProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const webViewRef = useRef<WebView>(null);
const [isWebViewOpen, setWebViewOpen] = useState(false);
const {isOffline} = useNetwork();

const authToken = session?.authToken ?? null;

const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false);

return (
<>
<Button
onPress={() => {
if (shouldDisconnectIntegrationBeforeConnecting && integrationToDisconnect) {
setIsDisconnectModalOpen(true);
return;
}
// Since QBO doesn't support Taxes, we should disable them from the LHN when connecting to QBO
PolicyAction.enablePolicyTaxes(policyID, false);
setWebViewOpen(true);
}}
text={translate('workspace.accounting.setup')}
style={styles.justifyContentCenter}
small
isDisabled={isOffline}
/>
{shouldDisconnectIntegrationBeforeConnecting && integrationToDisconnect && isDisconnectModalOpen && (
<AccountingConnectionConfirmationModal
onConfirm={() => {
// Since QBO doesn't support Taxes, we should disable them from the LHN when connecting to QBO
PolicyAction.enablePolicyTaxes(policyID, false);
removePolicyConnection(policyID, integrationToDisconnect);
setIsDisconnectModalOpen(false);
setWebViewOpen(true);
}}
integrationToConnect={CONST.POLICY.CONNECTIONS.NAME.QBO}
onCancel={() => setIsDisconnectModalOpen(false)}
/>
)}
{isWebViewOpen && (
<Modal
onClose={() => setWebViewOpen(false)}
fullscreen
isVisible
type={CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE}
>
<HeaderWithBackButton
title={translate('workspace.accounting.title')}
onBackButtonPress={() => setWebViewOpen(false)}
/>
<FullPageOfflineBlockingView>
<WebView
ref={webViewRef}
source={{
uri: getQuickbooksOnlineSetupLink(policyID),
headers: {
Cookie: `authToken=${authToken}`,
},
}}
incognito // 'incognito' prop required for Android, issue here https://github.com/react-native-webview/react-native-webview/issues/1352
startInLoadingState
renderLoading={renderLoading}
/>
</FullPageOfflineBlockingView>
</Modal>
)}
</>
);
}

ConnectToQuickbooksOnlineButton.displayName = 'ConnectToQuickbooksOnlineButton';

export default withOnyx<ConnectToQuickbooksOnlineButtonProps & ConnectToQuickbooksOnlineButtonOnyxProps, ConnectToQuickbooksOnlineButtonOnyxProps>({
session: {
key: ONYXKEYS.SESSION,
},
})(ConnectToQuickbooksOnlineButton);
Loading
Loading