Skip to content

Commit a7ce524

Browse files
authored
Merge pull request Expensify#91070 from software-mansion-labs/merge-hr/connect-flow
[NoQA] [HR Import] ConnectToMergeHRFlow component
2 parents db2123c + 63670a1 commit a7ce524

11 files changed

Lines changed: 60 additions & 234 deletions

File tree

src/components/ConnectToGustoFlow/index.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/components/ConnectToGustoFlow/types.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/components/ConnectToGustoFlow/index.native.tsx renamed to src/components/ConnectToHRFlow/index.native.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import Modal from '@components/Modal';
88
import useLocalize from '@hooks/useLocalize';
99
import useOnyx from '@hooks/useOnyx';
1010
import useThemeStyles from '@hooks/useThemeStyles';
11-
import getGustoSetupLink from '@libs/actions/connections/Gusto';
1211
import CONST from '@src/CONST';
1312
import ONYXKEYS from '@src/ONYXKEYS';
14-
import type ConnectToGustoFlowProps from './types';
13+
import type ConnectToHRFlowProps from './types';
1514

16-
function ConnectToGustoFlow({policyID}: ConnectToGustoFlowProps) {
15+
function ConnectToHRFlow({setupLink}: ConnectToHRFlowProps) {
1716
const {translate} = useLocalize();
1817
const styles = useThemeStyles();
1918
const webViewRef = useRef<WebView>(null);
@@ -24,7 +23,7 @@ function ConnectToGustoFlow({policyID}: ConnectToGustoFlowProps) {
2423
<View style={[StyleSheet.absoluteFill, styles.fullScreenLoading]}>
2524
<ActivityIndicator
2625
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
27-
reasonAttributes={{context: 'ConnectToGustoFlow'}}
26+
reasonAttributes={{context: 'ConnectToHRFlow'}}
2827
/>
2928
</View>
3029
);
@@ -46,7 +45,7 @@ function ConnectToGustoFlow({policyID}: ConnectToGustoFlowProps) {
4645
<WebView
4746
ref={webViewRef}
4847
source={{
49-
uri: getGustoSetupLink(policyID),
48+
uri: setupLink,
5049
headers: {
5150
Cookie: `authToken=${authToken}`,
5251
},
@@ -60,4 +59,4 @@ function ConnectToGustoFlow({policyID}: ConnectToGustoFlowProps) {
6059
);
6160
}
6261

63-
export default ConnectToGustoFlow;
62+
export default ConnectToHRFlow;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {useEffect} from 'react';
2+
import useEnvironment from '@hooks/useEnvironment';
3+
import {openLink} from '@userActions/Link';
4+
import type ConnectToHRFlowProps from './types';
5+
6+
function ConnectToHRFlow({setupLink}: ConnectToHRFlowProps) {
7+
const {environmentURL} = useEnvironment();
8+
9+
useEffect(() => {
10+
openLink(setupLink, environmentURL);
11+
}, [environmentURL, setupLink]);
12+
13+
return null;
14+
}
15+
16+
export default ConnectToHRFlow;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type ConnectToHRFlowProps = {
2+
/** The URL to open for the HR provider's connection flow */
3+
setupLink: string;
4+
};
5+
6+
export default ConnectToHRFlowProps;

src/components/ConnectToZenefitsFlow/index.native.tsx

Lines changed: 0 additions & 63 deletions
This file was deleted.

src/components/ConnectToZenefitsFlow/index.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/components/ConnectToZenefitsFlow/types.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/libs/actions/connections/MergeHR.ts

Lines changed: 11 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,21 @@ import type {OnyxUpdate} from 'react-native-onyx';
22
import Onyx from 'react-native-onyx';
33
import type {ValueOf} from 'type-fest';
44
import {read, write} from '@libs/API';
5+
import type {ConnectPolicyToMergeParams} from '@libs/API/parameters';
56
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
7+
import {getCommandURL} from '@libs/ApiUtils';
68
import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
79
import CONST from '@src/CONST';
810
import type {MergeHRProviderSlug} from '@src/CONST/MERGE_HR_PROVIDERS';
911
import ONYXKEYS from '@src/ONYXKEYS';
10-
import type {Connections} from '@src/types/onyx/Policy';
1112

12-
/**
13-
* Fetches a Merge link_token. The token is returned by the server and stored in a RAM-only Onyx key
14-
* so it is never persisted to disk. The UI reads it to initialize the Merge Link SDK.
15-
* The actual connection is only established after the user completes the Merge
16-
* Link flow and the public_token callback is processed by the backend.
17-
*/
18-
function connectPolicyToMergeHR(policyID: string, integration: MergeHRProviderSlug) {
19-
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY | typeof ONYXKEYS.RAM_ONLY_MERGE_HR_LINK_TOKEN>> = [
20-
{
21-
onyxMethod: Onyx.METHOD.MERGE,
22-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
23-
value: {
24-
connections: {
25-
[CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]: {
26-
config: {
27-
pendingFields: {integration: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD},
28-
errorFields: {integration: null},
29-
},
30-
},
31-
},
32-
},
33-
},
34-
{
35-
onyxMethod: Onyx.METHOD.SET,
36-
key: ONYXKEYS.RAM_ONLY_MERGE_HR_LINK_TOKEN,
37-
value: null,
38-
},
39-
];
40-
41-
const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
42-
{
43-
onyxMethod: Onyx.METHOD.MERGE,
44-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
45-
value: {
46-
connections: {
47-
[CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]: {
48-
config: {
49-
pendingFields: {integration: null},
50-
errorFields: {integration: null},
51-
},
52-
},
53-
},
54-
},
55-
},
56-
];
57-
58-
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
59-
{
60-
onyxMethod: Onyx.METHOD.MERGE,
61-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
62-
value: {
63-
connections: {
64-
[CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]: {
65-
config: {
66-
pendingFields: {integration: null},
67-
errorFields: {integration: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')},
68-
},
69-
},
70-
},
71-
},
72-
},
73-
];
74-
75-
read(
76-
READ_COMMANDS.CONNECT_POLICY_TO_MERGE,
77-
{
78-
policyID,
79-
integration,
80-
},
81-
{optimisticData, successData, failureData},
82-
);
13+
function getMergeHRSetupLink(policyID: string, integration: MergeHRProviderSlug) {
14+
const params: ConnectPolicyToMergeParams = {policyID, integration};
15+
const commandURL = getCommandURL({
16+
command: READ_COMMANDS.CONNECT_POLICY_TO_MERGE,
17+
shouldSkipWebProxy: true,
18+
});
19+
return commandURL + new URLSearchParams(params).toString();
8320
}
8421

8522
/**
@@ -247,40 +184,6 @@ function updateMergeHRFinalApprover(policyID: string, finalApprover: string | nu
247184
);
248185
}
249186

250-
/**
251-
* Removes the Merge HR connection from a policy.
252-
*/
253-
function disconnectMergeHR(policyID: string, currentConnection: Connections[typeof CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]) {
254-
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY | typeof ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS>> = [
255-
{
256-
onyxMethod: Onyx.METHOD.MERGE,
257-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
258-
value: {
259-
connections: {
260-
[CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]: null,
261-
},
262-
},
263-
},
264-
{
265-
onyxMethod: Onyx.METHOD.SET,
266-
key: `${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`,
267-
value: null,
268-
},
269-
];
270-
271-
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY | typeof ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS>> = [
272-
{
273-
onyxMethod: Onyx.METHOD.MERGE,
274-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
275-
value: {
276-
connections: {
277-
[CONST.POLICY.CONNECTIONS.NAME.MERGE_HR]: currentConnection,
278-
},
279-
},
280-
},
281-
];
282-
283-
write(WRITE_COMMANDS.REMOVE_POLICY_CONNECTION, {policyID, connectionName: CONST.POLICY.CONNECTIONS.NAME.MERGE_HR}, {optimisticData, failureData});
284-
}
187+
export {syncMergeHR, updateMergeHRApprovalMode, updateMergeHRFinalApprover};
285188

286-
export {connectPolicyToMergeHR, syncMergeHR, updateMergeHRApprovalMode, updateMergeHRFinalApprover, disconnectMergeHR};
189+
export default getMergeHRSetupLink;

src/pages/workspace/hr/WorkspaceHRPage.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native';
22
import React, {useEffect, useState} from 'react';
33
import {View} from 'react-native';
44
import CollapsibleSection from '@components/CollapsibleSection';
5-
import ConnectToGustoFlow from '@components/ConnectToGustoFlow';
5+
import ConnectToHRFlow from '@components/ConnectToHRFlow';
66
import HeaderWithBackButton from '@components/HeaderWithBackButton';
77
import ScreenWrapper from '@components/ScreenWrapper';
88
import ScrollView from '@components/ScrollView';
@@ -48,7 +48,7 @@ function WorkspaceHRPage({
4848
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`);
4949
const icons = useMemoizedLazyExpensifyIcons(['GustoSquare', 'ZenefitsSquare']);
5050
const illustrations = useMemoizedLazyIllustrations(['NewUser']);
51-
const [activeGustoFlowKey, setActiveGustoFlowKey] = useState<number>();
51+
const [activeHRFlow, setActiveHRFlow] = useState<{setupLink: string; key: number} | undefined>();
5252

5353
useWorkspaceDocumentTitle(undefined, 'workspace.common.hr');
5454

@@ -82,12 +82,12 @@ function WorkspaceHRPage({
8282

8383
const shouldBeBlocked = !HR_BETAS.some(isBetaEnabled);
8484

85-
const handleConnect = (connectionName: string) => {
86-
if (connectionName !== CONST.POLICY.CONNECTIONS.NAME.GUSTO) {
85+
const handleConnect = (setupLink: string | undefined) => {
86+
if (!setupLink) {
8787
return;
8888
}
89-
// eslint-disable-next-line react-hooks/purity
90-
setActiveGustoFlowKey(Math.random());
89+
// eslint-disable-next-line react-hooks/purity -- random key forces remount on every press, even for the same provider
90+
setActiveHRFlow({setupLink, key: Math.random()});
9191
};
9292

9393
return (
@@ -104,10 +104,10 @@ function WorkspaceHRPage({
104104
shouldShowOfflineIndicatorInWideScreen
105105
offlineIndicatorStyle={styles.mtAuto}
106106
>
107-
{!!activeGustoFlowKey && (
108-
<ConnectToGustoFlow
109-
key={activeGustoFlowKey}
110-
policyID={policyID}
107+
{!!activeHRFlow && (
108+
<ConnectToHRFlow
109+
key={activeHRFlow.key}
110+
setupLink={activeHRFlow.setupLink}
111111
/>
112112
)}
113113
<HeaderWithBackButton
@@ -130,7 +130,7 @@ function WorkspaceHRPage({
130130
key={card.key}
131131
card={card}
132132
policy={policy}
133-
handleConnect={() => handleConnect(card.connectionName)}
133+
handleConnect={() => handleConnect(card.setupLink)}
134134
/>
135135
))}
136136
{connectedCards.length === 0 &&
@@ -139,7 +139,7 @@ function WorkspaceHRPage({
139139
key={card.key}
140140
card={card}
141141
policy={policy}
142-
handleConnect={() => handleConnect(card.connectionName)}
142+
handleConnect={() => handleConnect(card.setupLink)}
143143
/>
144144
))}
145145
</View>
@@ -156,7 +156,7 @@ function WorkspaceHRPage({
156156
key={card.key}
157157
card={card}
158158
policy={policy}
159-
handleConnect={() => handleConnect(card.connectionName)}
159+
handleConnect={() => handleConnect(card.setupLink)}
160160
/>
161161
))}
162162
</CollapsibleSection>

0 commit comments

Comments
 (0)