From 1c6d4cf2dd65b885531120724d7c8da11c323444 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 15 May 2026 17:33:29 +0530 Subject: [PATCH] feat: Add Zenefits action module and ConnectToZenefitsFlow component Signed-off-by: krishna2323 --- .../ConnectToZenefitsFlow/index.native.tsx | 63 ++++++++ .../ConnectToZenefitsFlow/index.tsx | 17 ++ src/components/ConnectToZenefitsFlow/types.ts | 5 + src/libs/actions/connections/Zenefits.ts | 147 ++++++++++++++++++ src/libs/actions/connections/index.ts | 3 + 5 files changed, 235 insertions(+) create mode 100644 src/components/ConnectToZenefitsFlow/index.native.tsx create mode 100644 src/components/ConnectToZenefitsFlow/index.tsx create mode 100644 src/components/ConnectToZenefitsFlow/types.ts create mode 100644 src/libs/actions/connections/Zenefits.ts diff --git a/src/components/ConnectToZenefitsFlow/index.native.tsx b/src/components/ConnectToZenefitsFlow/index.native.tsx new file mode 100644 index 000000000000..461f198bf0fa --- /dev/null +++ b/src/components/ConnectToZenefitsFlow/index.native.tsx @@ -0,0 +1,63 @@ +import React, {useRef, useState} from 'react'; +import {StyleSheet, View} from 'react-native'; +import {WebView} from 'react-native-webview'; +import ActivityIndicator from '@components/ActivityIndicator'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Modal from '@components/Modal'; +import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; +import useThemeStyles from '@hooks/useThemeStyles'; +import getZenefitsSetupLink from '@libs/actions/connections/Zenefits'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type ConnectToZenefitsFlowProps from './types'; + +function ConnectToZenefitsFlow({policyID}: ConnectToZenefitsFlowProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const webViewRef = useRef(null); + const [isWebViewOpen, setIsWebViewOpen] = useState(true); + const [session] = useOnyx(ONYXKEYS.SESSION); + + const renderLoading = () => ( + + + + ); + + const authToken = session?.authToken ?? null; + return ( + setIsWebViewOpen(false)} + fullscreen + isVisible={isWebViewOpen} + type={CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE} + > + setIsWebViewOpen(false)} + shouldDisplayHelpButton={false} + /> + + + + + ); +} + +export default ConnectToZenefitsFlow; diff --git a/src/components/ConnectToZenefitsFlow/index.tsx b/src/components/ConnectToZenefitsFlow/index.tsx new file mode 100644 index 000000000000..5748062bd80c --- /dev/null +++ b/src/components/ConnectToZenefitsFlow/index.tsx @@ -0,0 +1,17 @@ +import {useEffect} from 'react'; +import useEnvironment from '@hooks/useEnvironment'; +import getZenefitsSetupLink from '@libs/actions/connections/Zenefits'; +import {openLink} from '@userActions/Link'; +import type ConnectToZenefitsFlowProps from './types'; + +function ConnectToZenefitsFlow({policyID}: ConnectToZenefitsFlowProps) { + const {environmentURL} = useEnvironment(); + + useEffect(() => { + openLink(getZenefitsSetupLink(policyID), environmentURL); + }, [environmentURL, policyID]); + + return null; +} + +export default ConnectToZenefitsFlow; diff --git a/src/components/ConnectToZenefitsFlow/types.ts b/src/components/ConnectToZenefitsFlow/types.ts new file mode 100644 index 000000000000..7ffa72c72206 --- /dev/null +++ b/src/components/ConnectToZenefitsFlow/types.ts @@ -0,0 +1,5 @@ +type ConnectToZenefitsFlowProps = { + policyID: string; +}; + +export default ConnectToZenefitsFlowProps; diff --git a/src/libs/actions/connections/Zenefits.ts b/src/libs/actions/connections/Zenefits.ts new file mode 100644 index 000000000000..d027e4eb30aa --- /dev/null +++ b/src/libs/actions/connections/Zenefits.ts @@ -0,0 +1,147 @@ +import type {OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import {write} from '@libs/API'; +import type {ConnectPolicyToZenefitsParams} from '@libs/API/parameters'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import {getCommandURL} from '@libs/ApiUtils'; +import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function getZenefitsSetupLink(policyID: string) { + const params: ConnectPolicyToZenefitsParams = {policyID}; + const commandURL = getCommandURL({ + command: READ_COMMANDS.CONNECT_POLICY_TO_ZENEFITS, + shouldSkipWebProxy: true, + }); + return commandURL + new URLSearchParams(params).toString(); +} + +function updateZenefitsApprovalMode( + policyID: string | undefined, + approvalMode: ValueOf, + currentApprovalMode?: ValueOf | null, +) { + if (!policyID) { + return; + } + + const previousApprovalMode = currentApprovalMode ?? null; + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + approvalMode, + pendingFields: {approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + errorFields: {approvalMode: null}, + }, + }, + }, + }, + }, + ]; + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + pendingFields: {approvalMode: null}, + errorFields: {approvalMode: null}, + }, + }, + }, + }, + }, + ]; + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + approvalMode: previousApprovalMode, + pendingFields: {approvalMode: null}, + errorFields: {approvalMode: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, + }, + }, + }, + }, + }, + ]; + + write(WRITE_COMMANDS.UPDATE_ZENEFITS_APPROVAL_MODE, {policyID, approvalMode}, {optimisticData, successData, failureData}); +} + +function updateZenefitsFinalApprover(policyID: string | undefined, finalApprover: string | null, currentFinalApprover?: string | null) { + if (!policyID) { + return; + } + + const previousFinalApprover = currentFinalApprover ?? null; + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + finalApprover, + pendingFields: {finalApprover: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + errorFields: {finalApprover: null}, + }, + }, + }, + }, + }, + ]; + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + pendingFields: {finalApprover: null}, + errorFields: {finalApprover: null}, + }, + }, + }, + }, + }, + ]; + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + zenefits: { + config: { + finalApprover: previousFinalApprover, + pendingFields: {finalApprover: null}, + errorFields: {finalApprover: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, + }, + }, + }, + }, + }, + ]; + + write(WRITE_COMMANDS.UPDATE_ZENEFITS_FINAL_APPROVER, {policyID, finalApprover}, {optimisticData, successData, failureData}); +} + +export {updateZenefitsApprovalMode, updateZenefitsFinalApprover}; + +export default getZenefitsSetupLink; diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index e7b9dd0cec6f..650919ddbe39 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -120,6 +120,9 @@ function getSyncConnectionParameters(connectionName: PolicyConnectionName) { case CONST.POLICY.CONNECTIONS.NAME.GUSTO: { return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_GUSTO, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.GUSTO_SYNC_TITLE}; } + case CONST.POLICY.CONNECTIONS.NAME.ZENEFITS: { + return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_ZENEFITS, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.ZENEFITS_SYNC_TITLE}; + } case CONST.POLICY.CONNECTIONS.NAME.CERTINIA: { return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_FINANCIAL_FORCE, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.FINANCIAL_FORCE_SYNC_CONNECTION}; }