Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,15 @@ const ROUTES = {
return `workspaces/${policyID}/hr/gusto/approval-mode` as const;
},
},
WORKSPACE_HR_GUSTO_FINAL_APPROVER: {
route: 'workspaces/:policyID/hr/gusto/final-approver',
getRoute: (policyID: string | undefined) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the WORKSPACE_HR_GUSTO_FINAL_APPROVER route');
}
return `workspaces/${policyID}/hr/gusto/final-approver` as const;
},
},
WORKSPACE_TAGS: {
route: 'workspaces/:policyID/tags',
getRoute: (policyID: string | undefined) => {
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ const SCREENS = {
PROFILE: 'Workspace_Overview',
HR: 'Workspace_HR',
HR_GUSTO_APPROVAL_MODE: 'Workspace_HR_Gusto_Approval_Mode',
HR_GUSTO_FINAL_APPROVER: 'Workspace_HR_Gusto_Final_Approver',
COMPANY_CARDS: 'Workspace_CompanyCards',
COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_BrokenCardFeedConnection',
COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_RefreshCardFeedConnection',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.LOCK.UNLOCK_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/settings/Security/LockAccount/UnlockAccountPage').default,
[SCREENS.SETTINGS.LOCK.FAILED_TO_LOCK_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/settings/Security/LockAccount/FailedToLockAccountPage').default,
[SCREENS.WORKSPACE.HR_GUSTO_APPROVAL_MODE]: () => require<ReactComponentModule>('../../../../pages/workspace/hr/gusto/GustoApprovalModePage').default,
[SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER]: () => require<ReactComponentModule>('../../../../pages/workspace/hr/gusto/GustoFinalApproverPage').default,
[SCREENS.WORKSPACE.REPORTS_DEFAULT_TITLE]: () => require<ReactComponentModule>('../../../../pages/workspace/reports/ReportsDefaultTitle').default,
[SCREENS.WORKSPACE.RULES_AUTO_APPROVE_REPORTS_UNDER]: () => require<ReactComponentModule>('../../../../pages/workspace/rules/RulesAutoApproveReportsUnderPage').default,
[SCREENS.WORKSPACE.RULES_RANDOM_REPORT_AUDIT]: () => require<ReactComponentModule>('../../../../pages/workspace/rules/RulesRandomReportAuditPage').default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ const WORKSPACE_TO_RHP: Partial<Record<keyof WorkspaceSplitNavigatorParamList, s
SCREENS.WORKSPACE.TAX_VALUE,
SCREENS.WORKSPACE.TAX_CODE,
],
[SCREENS.WORKSPACE.HR]: [SCREENS.WORKSPACE.HR_GUSTO_APPROVAL_MODE],
[SCREENS.WORKSPACE.HR]: [SCREENS.WORKSPACE.HR_GUSTO_APPROVAL_MODE, SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER],
[SCREENS.WORKSPACE.TAGS]: [
SCREENS.WORKSPACE.TAGS_SETTINGS,
SCREENS.WORKSPACE.TAGS_EDIT,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,9 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.WORKSPACE.HR_GUSTO_APPROVAL_MODE]: {
path: ROUTES.WORKSPACE_HR_GUSTO_APPROVAL_MODE.route,
},
[SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER]: {
path: ROUTES.WORKSPACE_HR_GUSTO_FINAL_APPROVER.route,
},
[SCREENS.WORKSPACE.RULES_CUSTOM]: {
path: ROUTES.RULES_CUSTOM.route,
},
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,9 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.HR_GUSTO_APPROVAL_MODE]: {
policyID: string;
};
[SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER]: {
policyID: string;
};
[SCREENS.WORKSPACE.RULES_PROHIBITED_DEFAULT]: {
policyID: string;
};
Expand Down
62 changes: 61 additions & 1 deletion src/libs/actions/connections/Gusto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,66 @@ function updateGustoApprovalMode(
write(WRITE_COMMANDS.UPDATE_GUSTO_APPROVAL_MODE, {policyID, approvalMode}, {optimisticData, successData, failureData});
}

export {updateGustoApprovalMode};
function updateGustoFinalApprover(policyID: string | undefined, finalApprover: string | null, currentFinalApprover?: string | null) {
if (!policyID) {
return;
}

const previousFinalApprover = currentFinalApprover ?? null;
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
gusto: {
config: {
finalApprover,
pendingFields: {finalApprover: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
errorFields: {finalApprover: null},
},
},
},
},
},
];
const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
gusto: {
config: {
pendingFields: {finalApprover: null},
errorFields: {finalApprover: null},
},
},
},
},
},
];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY>> = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
gusto: {
config: {
finalApprover: previousFinalApprover,
pendingFields: {finalApprover: null},
errorFields: {finalApprover: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')},
},
},
},
},
},
];

write(WRITE_COMMANDS.UPDATE_GUSTO_FINAL_APPROVER, {policyID, finalApprover}, {optimisticData, successData, failureData});
}

export {updateGustoApprovalMode, updateGustoFinalApprover};

export default getGustoSetupLink;
40 changes: 30 additions & 10 deletions src/pages/workspace/hr/WorkspaceHRPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {openPolicyHRPage} from '@libs/actions/PolicyConnections';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types';
import {getDisplayNameOrDefault, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
import {getIntegrationLastSuccessfulDate, isGustoConnected} from '@libs/PolicyUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -123,6 +124,13 @@ function WorkspaceHRPage({
return translate('workspace.hr.gusto.notSet');
}
};
const getGustoFinalApproverDisplayName = (finalApprover?: string | null) => {
if (!finalApprover) {
return translate('workspace.hr.gusto.notSet');
}

return getDisplayNameOrDefault(getPersonalDetailByEmail(finalApprover), finalApprover, false);
};
let gustoRowRightComponent;
if (!isConnected) {
gustoRowRightComponent = (
Expand Down Expand Up @@ -200,16 +208,28 @@ function WorkspaceHRPage({
rightComponent={gustoRowRightComponent}
/>
{isConnected && (
<OfflineWithFeedback pendingAction={gustoConfig?.pendingFields?.approvalMode}>
<MenuItemWithTopDescription
description={translate('workspace.hr.gusto.approvalMode')}
title={getGustoApprovalModeLabel(gustoConfig?.approvalMode)}
style={[styles.sectionMenuItemTopDescription, styles.mt2]}
shouldShowRightIcon
brickRoadIndicator={gustoConfig?.errorFields?.approvalMode ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_HR_GUSTO_APPROVAL_MODE.getRoute(policyID))}
/>
</OfflineWithFeedback>
<>
<OfflineWithFeedback pendingAction={gustoConfig?.pendingFields?.approvalMode}>
<MenuItemWithTopDescription
description={translate('workspace.hr.gusto.approvalMode')}
title={getGustoApprovalModeLabel(gustoConfig?.approvalMode)}
style={[styles.sectionMenuItemTopDescription, styles.mt2]}
shouldShowRightIcon
brickRoadIndicator={gustoConfig?.errorFields?.approvalMode ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_HR_GUSTO_APPROVAL_MODE.getRoute(policyID))}
/>
</OfflineWithFeedback>
<OfflineWithFeedback pendingAction={gustoConfig?.pendingFields?.finalApprover}>
<MenuItemWithTopDescription
description={translate('workspace.hr.gusto.finalApprover')}
title={getGustoFinalApproverDisplayName(gustoConfig?.finalApprover)}
style={styles.sectionMenuItemTopDescription}
shouldShowRightIcon
brickRoadIndicator={gustoConfig?.errorFields?.finalApprover ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_HR_GUSTO_FINAL_APPROVER.getRoute(policyID))}
/>
</OfflineWithFeedback>
</>
)}
</Section>
</View>
Expand Down
62 changes: 62 additions & 0 deletions src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import WorkspaceMembersSelectionList from '@components/WorkspaceMembersSelectionList';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import useThemeStyles from '@hooks/useThemeStyles';
import {updateGustoFinalApprover} from '@libs/actions/connections/Gusto';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {isGustoConnected} from '@libs/PolicyUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

type GustoFinalApproverPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER>;

function GustoFinalApproverPage({
route: {
params: {policyID},
},
}: GustoFinalApproverPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isBetaEnabled} = usePermissions();
const policy = usePolicy(policyID);
const finalApprover = policy?.connections?.gusto?.config?.finalApprover ?? null;

return (
<AccessOrNotFoundWrapper
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED}
shouldBeBlocked={!isBetaEnabled(CONST.BETAS.GUSTO) || (!!policy && !isGustoConnected(policy))}
>
<ScreenWrapper
enableEdgeToEdgeBottomSafeAreaPadding
style={[styles.defaultModalContainer]}
testID="GustoFinalApproverPage"
shouldEnableMaxHeight
>
<HeaderWithBackButton
title={translate('workspace.hr.gusto.finalApprover')}
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID))}
/>
<WorkspaceMembersSelectionList
policyID={policyID}
selectedApprover={finalApprover ?? ''}
setApprover={(email) => {
updateGustoFinalApprover(policyID, email, finalApprover);
Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID)));
}}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
);
}

export default GustoFinalApproverPage;
Loading