diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 05b4259c0b2f..faa7f7df5c87 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -981,7 +981,6 @@ const CONST = { EUR_BILLING: 'eurBilling', PAY_INVOICE_VIA_EXPENSIFY: 'payInvoiceViaExpensify', SUGGESTED_FOLLOWUPS: 'suggestedFollowups', - GUSTO: 'gustoNewDot', ZENEFITS: 'zenefitsNewDot', BULK_EDIT: 'bulkEdit', BULK_EDIT_WORKSPACES: 'bulkEditWorkspaces', diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index abcde72a8753..96d2283d5530 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -168,10 +168,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac [CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]: policy?.tax?.trackingEnabled, [CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]: policy?.areCompanyCardsEnabled, [CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: !!policy?.areConnectionsEnabled || hasAccountingFeatureConnection(policy), - [CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED]: - (isBetaEnabled(CONST.BETAS.GUSTO) || isBetaEnabled(CONST.BETAS.ZENEFITS) || isBetaEnabled(CONST.BETAS.MERGE_HR)) && - (policy?.isHREnabled === true || isAnyHRConnected(policy)) && - canPolicyAccessFeature(policy, CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED), + [CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED]: (policy?.isHREnabled === true || isAnyHRConnected(policy)) && canPolicyAccessFeature(policy, CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED), [CONST.POLICY.MORE_FEATURES.ARE_EXPENSIFY_CARDS_ENABLED]: policy?.areExpensifyCardsEnabled, [CONST.POLICY.MORE_FEATURES.ARE_REPORT_FIELDS_ENABLED]: policy?.areReportFieldsEnabled, [CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED]: policy?.areRulesEnabled, diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage/index.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage/index.tsx index 0f67511e74d8..1cc4256daaa2 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage/index.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage/index.tsx @@ -14,7 +14,6 @@ import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; -import usePermissions from '@hooks/usePermissions'; import usePolicyData from '@hooks/usePolicyData'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -77,7 +76,6 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {translate} = useLocalize(); - const {isBetaEnabled} = usePermissions(); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const {showConfirmModal} = useConfirmModal(); const illustrations = useMemoizedLazyIllustrations([ @@ -326,35 +324,33 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro Navigation.navigate(ROUTES.WORKSPACE_RECEIPT_PARTNERS.getRoute(policyID)); }} /> - {(isBetaEnabled(CONST.BETAS.GUSTO) || isBetaEnabled(CONST.BETAS.ZENEFITS) || isBetaEnabled(CONST.BETAS.MERGE_HR)) && ( - { - if (!policyID) { - return; - } - if (isEnabled && !isControlPolicy(policy)) { - Navigation.navigate( - ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.hr.alias, ROUTES.WORKSPACE_MORE_FEATURES.getRoute(policyID)), - ); - return; - } - enablePolicyHR(policyID, isEnabled); - }} - onPress={() => { - if (!policyID) { - return; - } - Navigation.navigate(ROUTES.WORKSPACE_HR.getRoute(policyID)); - }} - /> - )} + { + if (!policyID) { + return; + } + if (isEnabled && !isControlPolicy(policy)) { + Navigation.navigate( + ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, CONST.UPGRADE_FEATURE_INTRO_MAPPING.hr.alias, ROUTES.WORKSPACE_MORE_FEATURES.getRoute(policyID)), + ); + return; + } + enablePolicyHR(policyID, isEnabled); + }} + onPress={() => { + if (!policyID) { + return; + } + Navigation.navigate(ROUTES.WORKSPACE_HR.getRoute(policyID)); + }} + /> diff --git a/src/pages/workspace/hr/HRApprovalModePageBase.tsx b/src/pages/workspace/hr/HRApprovalModePageBase.tsx index 482ddf0bb9f9..6ea9d421af6d 100644 --- a/src/pages/workspace/hr/HRApprovalModePageBase.tsx +++ b/src/pages/workspace/hr/HRApprovalModePageBase.tsx @@ -30,7 +30,7 @@ type ApprovalModeValue = ValueOf | ValueOf = { testID: string; - beta: Beta; + beta?: Beta; isConnected: (policy: OnyxEntry) => boolean; approvalModes: {BASIC: T; MANAGER: T; CUSTOM: T}; getCurrentApprovalMode: (policy: OnyxEntry) => T | null; @@ -119,7 +119,7 @@ function HRApprovalModePageBase({policyID, config}: accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]} policyID={policyID} featureName={CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED} - shouldBeBlocked={!isBetaEnabled(config.beta) || (!!policy && !config.isConnected(policy))} + shouldBeBlocked={(!!config.beta && !isBetaEnabled(config.beta)) || (!!policy && !config.isConnected(policy))} > ) => boolean; getCurrentFinalApprover: (policy: OnyxEntry) => string | null; getHeaderTitle: (providerName: string) => string; @@ -44,7 +44,7 @@ function HRFinalApproverPageBase({policyID, config}: HRFinalApproverPageBaseProp accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]} policyID={policyID} featureName={CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED} - shouldBeBlocked={!isBetaEnabled(config.beta) || (!!policy && !config.isConnected(policy))} + shouldBeBlocked={(!!config.beta && !isBetaEnabled(config.beta)) || (!!policy && !config.isConnected(policy))} > ; function WorkspaceHRPage({ @@ -86,8 +84,6 @@ function WorkspaceHRPage({ connectedCards.sort(byName); disconnectedCards.sort(byName); - const shouldBeBlocked = !HR_BETAS.some(isBetaEnabled); - const handleConnect = (setupLink: string | undefined) => { if (!setupLink) { return; @@ -113,7 +109,6 @@ function WorkspaceHRPage({ accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]} policyID={policyID} featureName={CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED} - shouldBeBlocked={shouldBeBlocked} > > = { testID: 'GustoApprovalModePage', - beta: CONST.BETAS.GUSTO, isConnected: isGustoConnected, approvalModes: CONST.GUSTO.APPROVAL_MODE, getCurrentApprovalMode: (policy) => policy?.connections?.gusto?.config?.approvalMode ?? null, diff --git a/src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx b/src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx index eeb4f3df150d..ad2ff894055c 100644 --- a/src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx +++ b/src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx @@ -6,7 +6,6 @@ import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {isGustoConnected} from '@libs/PolicyUtils'; import HRFinalApproverPageBase from '@pages/workspace/hr/HRFinalApproverPageBase'; import type {HRFinalApproverProviderConfig} from '@pages/workspace/hr/HRFinalApproverPageBase'; -import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; type GustoFinalApproverPageProps = PlatformStackScreenProps; @@ -20,7 +19,6 @@ function GustoFinalApproverPage({ const config: HRFinalApproverProviderConfig = { testID: 'GustoFinalApproverPage', - beta: CONST.BETAS.GUSTO, isConnected: isGustoConnected, getCurrentFinalApprover: (policy) => policy?.connections?.gusto?.config?.finalApprover ?? null, getProviderName: () => translate('workspace.hr.gusto.title'), diff --git a/src/pages/workspace/hr/utils.ts b/src/pages/workspace/hr/utils.ts index dc60f2587714..25c38792e876 100644 --- a/src/pages/workspace/hr/utils.ts +++ b/src/pages/workspace/hr/utils.ts @@ -187,7 +187,7 @@ function getCardConfig(policy: OnyxEntry, connectionName: HRConnectionNa const STATIC_HR_PROVIDERS = [ { key: 'gusto', - beta: CONST.BETAS.GUSTO, + beta: undefined, connectionName: CONST.POLICY.CONNECTIONS.NAME.GUSTO, titleKey: 'workspace.hr.gusto.title', iconParam: 'gustoIcon', @@ -238,11 +238,11 @@ function getHRCards({policy, connectionSyncProgress, isBetaEnabled, getLocalDate const cards: HRCardDescriptor[] = []; for (const provider of STATIC_HR_PROVIDERS) { - if (!isBetaEnabled(provider.beta)) { - continue; - } const {connectionName} = provider; const state = getHRCardState({policy, connectionName, connectionSyncProgress, getLocalDateFromDatetime}); + if (provider.beta && !isBetaEnabled(provider.beta) && !state.isConnected) { + continue; + } const config = getCardConfig(policy, connectionName); cards.push({ key: provider.key, diff --git a/tests/unit/HrUtilsTest.ts b/tests/unit/HrUtilsTest.ts index 185a2ec1e8da..b244e79b0954 100644 --- a/tests/unit/HrUtilsTest.ts +++ b/tests/unit/HrUtilsTest.ts @@ -304,13 +304,15 @@ describe('getApprovalModeLabel', () => { }); describe('getHRCards', () => { - it('returns no cards when no betas are enabled', () => { + it('returns only the Gusto card when no betas are enabled', () => { const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled: noBetasEnabled})); - expect(cards).toHaveLength(0); + expect(cards).toHaveLength(1); + expect(cards?.at(0)?.key).toBe('gusto'); + expect(cards?.at(0)?.connectionName).toBe(GUSTO); }); - it('returns Gusto and Zenefits cards when their betas are enabled', () => { - const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.GUSTO || beta === CONST.BETAS.ZENEFITS; + it('returns Gusto and Zenefits cards when the Zenefits beta is enabled', () => { + const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.ZENEFITS; const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); expect(cards).toHaveLength(2); @@ -325,15 +327,13 @@ describe('getHRCards', () => { const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); const mergeKeys = Object.keys(MERGE_HR_PROVIDERS); - expect(cards).toHaveLength(mergeKeys.length); for (const slug of mergeKeys) { expect(cards.find((c) => c.key === `merge_${slug}`)).toBeDefined(); } }); it('sets correct routes for Gusto cards', () => { - const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.GUSTO; - const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); + const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled: noBetasEnabled})); expect(cards?.at(0)?.approvalModeRoute).toBe(ROUTES.WORKSPACE_HR_GUSTO_APPROVAL_MODE.getRoute(POLICY_ID)); expect(cards?.at(0)?.finalApproverRoute).toBe(ROUTES.WORKSPACE_HR_GUSTO_FINAL_APPROVER.getRoute(POLICY_ID)); @@ -342,21 +342,41 @@ describe('getHRCards', () => { it('sets correct routes for Zenefits cards', () => { const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.ZENEFITS; const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); + const zenefits = cards.find((c) => c.key === 'zenefits'); - expect(cards?.at(0)?.approvalModeRoute).toBe(ROUTES.WORKSPACE_HR_ZENEFITS_APPROVAL_MODE.getRoute(POLICY_ID)); - expect(cards?.at(0)?.finalApproverRoute).toBe(ROUTES.WORKSPACE_HR_ZENEFITS_FINAL_APPROVER.getRoute(POLICY_ID)); + expect(zenefits?.approvalModeRoute).toBe(ROUTES.WORKSPACE_HR_ZENEFITS_APPROVAL_MODE.getRoute(POLICY_ID)); + expect(zenefits?.finalApproverRoute).toBe(ROUTES.WORKSPACE_HR_ZENEFITS_FINAL_APPROVER.getRoute(POLICY_ID)); }); it('sets correct routes for Merge HR cards', () => { const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.MERGE_HR; const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); + const mergeCards = cards.filter((c) => c.key.startsWith('merge_')); - for (const card of cards) { + expect(mergeCards.length).toBeGreaterThan(0); + for (const card of mergeCards) { expect(card.approvalModeRoute).toBe(ROUTES.WORKSPACE_HR_MERGE_APPROVAL_MODE.getRoute(POLICY_ID)); expect(card.finalApproverRoute).toBe(ROUTES.WORKSPACE_HR_MERGE_FINAL_APPROVER.getRoute(POLICY_ID)); } }); + it('returns the connected Zenefits card even when the Zenefits beta is disabled', () => { + const policy = makePolicy({ + connections: { + zenefits: { + config: {approvalMode: CONST.ZENEFITS.APPROVAL_MODE.BASIC, finalApprover: 'admin@test.com'}, + data: {}, + lastSync: {isSuccessful: true}, + }, + } as unknown as Policy['connections'], + }); + const cards = getHRCards(makeGetHRCardsParams({policy, isBetaEnabled: noBetasEnabled})); + const zenefits = cards.find((c) => c.key === 'zenefits'); + + expect(zenefits?.isConnected).toBe(true); + expect(zenefits?.config).toBeDefined(); + }); + it('marks the connected Gusto card as connected with config', () => { const policy = makePolicy({ connections: { @@ -367,8 +387,7 @@ describe('getHRCards', () => { }, } as unknown as Policy['connections'], }); - const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.GUSTO; - const cards = getHRCards(makeGetHRCardsParams({policy, isBetaEnabled})); + const cards = getHRCards(makeGetHRCardsParams({policy, isBetaEnabled: noBetasEnabled})); expect(cards?.at(0)?.isConnected).toBe(true); expect(cards?.at(0)?.config).toBeDefined(); @@ -466,7 +485,7 @@ describe('getHRCards', () => { it('uses provider icons from params for static providers', () => { const gustoIcon = {testId: 'gusto'} as unknown as IconAsset; const trinetIcon = {testId: 'zenefits'} as unknown as IconAsset; - const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.GUSTO || beta === CONST.BETAS.ZENEFITS; + const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.ZENEFITS; const cards = getHRCards(makeGetHRCardsParams({gustoIcon, trinetIcon, isBetaEnabled})); expect(cards?.at(0)?.icon).toBe(gustoIcon); @@ -476,8 +495,10 @@ describe('getHRCards', () => { it('uses provider iconUrl for Merge cards', () => { const isBetaEnabled: GetHRCardsParams['isBetaEnabled'] = (beta) => beta === CONST.BETAS.MERGE_HR; const cards = getHRCards(makeGetHRCardsParams({isBetaEnabled})); + const mergeCards = cards.filter((c) => c.key.startsWith('merge_')); - for (const card of cards) { + expect(mergeCards.length).toBeGreaterThan(0); + for (const card of mergeCards) { const slug = card.key.replace('merge_', ''); const expected = MERGE_HR_PROVIDERS[slug as keyof typeof MERGE_HR_PROVIDERS]?.iconUrl; expect(card.icon).toBe(expected);