Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7154,6 +7154,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
connectionDescription: (providerName: string) => `Verbinden Sie ${providerName}, um Mitarbeitergenehmigungen mit Ihrem Workspace zu synchronisieren.`,
approvalMode: 'Genehmigungsmodus',
finalApprover: 'Endgültige:r Genehmiger:in',
providerFinalApprover: (providerName: string) => `${providerName} Endgenehmigende*r`,
notSet: 'Nicht festgelegt',
approvalModeDescription: (providerName: string) => `Mitglieder und Manager sind für die Synchronisation mit ${providerName} eingerichtet.`,
approvalModeWarningTitle: 'Genehmigungsmodus ändern?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6456,6 +6456,7 @@ const translations = {
connectionDescription: (providerName: string) => `Connect ${providerName} to keep employee approvals in sync with your workspace.`,
approvalMode: 'Approval mode',
finalApprover: 'Final approver',
providerFinalApprover: (providerName: string) => `${providerName} final approver`,
notSet: 'Not set',
approvalModeDescription: (providerName: string) => `Members and managers are set up to sync with ${providerName}.`,
approvalModeWarningTitle: 'Change approval mode?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6269,6 +6269,7 @@ ${amount} para ${merchant} - ${date}`,
connectionDescription: (providerName: string) => `Conecta ${providerName} para mantener sincronizadas las aprobaciones de empleados con tu espacio de trabajo.`,
approvalMode: 'Modo de aprobación',
finalApprover: 'Aprobador final',
providerFinalApprover: (providerName: string) => `Aprobador final de ${providerName}`,
notSet: 'No configurado',
approvalModeDescription: (providerName: string) => `Los miembros y gerentes están configurados para sincronizarse con ${providerName}.`,
approvalModeWarningTitle: '¿Cambiar modo de aprobación?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7177,6 +7177,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
connectionDescription: (providerName: string) => `Connectez ${providerName} pour synchroniser les approbations des employés avec votre espace de travail.`,
approvalMode: "Mode d'approbation",
finalApprover: 'Approbateur final',
providerFinalApprover: (providerName: string) => `Approbateur final ${providerName}`,
notSet: 'Non défini',
approvalModeDescription: (providerName: string) => `Les membres et les responsables sont configurés pour se synchroniser avec ${providerName}.`,
approvalModeWarningTitle: 'Changer le mode d’approbation ?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7141,6 +7141,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
connectionDescription: (providerName: string) => `Collega ${providerName} per mantenere sincronizzate le approvazioni dei dipendenti con il tuo spazio di lavoro.`,
approvalMode: 'Modalità di approvazione',
finalApprover: 'Approvatore finale',
providerFinalApprover: (providerName: string) => `Approvatore finale ${providerName}`,
notSet: 'Non impostato',
approvalModeDescription: (providerName: string) => `I membri e i responsabili sono configurati per la sincronizzazione con ${providerName}.`,
approvalModeWarningTitle: 'Cambiare modalità di approvazione?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7060,6 +7060,7 @@ ${reportName}
connectionDescription: (providerName: string) => `${providerName}を接続して、従業員の承認をワークスペースと同期させましょう。`,
approvalMode: '承認モード',
finalApprover: '最終承認者',
providerFinalApprover: (providerName: string) => `${providerName} 最終承認者`,
notSet: '未設定',
approvalModeDescription: (providerName: string) => `メンバーとマネージャーは ${providerName} と同期するように設定されています。`,
approvalModeWarningTitle: '承認モードを変更しますか?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7116,6 +7116,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
connectionDescription: (providerName: string) => `Verbind ${providerName} om goedkeuringen van werknemers gesynchroniseerd te houden met je werkruimte.`,
approvalMode: 'Goedkeuringsmodus',
finalApprover: 'Eindgoedkeurder',
providerFinalApprover: (providerName: string) => `Laatste ${providerName}-fiatteur`,
notSet: 'Niet ingesteld',
approvalModeDescription: (providerName: string) => `Leden en managers zijn ingesteld om te synchroniseren met ${providerName}.`,
approvalModeWarningTitle: 'Goedkeuringsmodus wijzigen?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7111,6 +7111,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
connectionDescription: (providerName: string) => `Połącz ${providerName}, aby synchronizować akceptacje pracowników z Twoim miejscem pracy.`,
approvalMode: 'Tryb zatwierdzania',
finalApprover: 'Ostateczny zatwierdzający',
providerFinalApprover: (providerName: string) => `Ostateczny zatwierdzający ${providerName}`,
notSet: 'Nie ustawiono',
approvalModeDescription: (providerName: string) => `Członkowie i menedżerowie są skonfigurowani do synchronizacji z ${providerName}.`,
approvalModeWarningTitle: 'Zmienić tryb zatwierdzania?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7116,6 +7116,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
connectionDescription: (providerName: string) => `Conecte ${providerName} para manter as aprovações de funcionários sincronizadas com seu workspace.`,
approvalMode: 'Modo de aprovação',
finalApprover: 'Aprovador final',
providerFinalApprover: (providerName: string) => `Aprovador final de ${providerName}`,
notSet: 'Não definido',
approvalModeDescription: (providerName: string) => `Membros e gerentes estão configurados para sincronizar com ${providerName}.`,
approvalModeWarningTitle: 'Alterar modo de aprovação?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6933,6 +6933,7 @@ ${reportName}
connectionDescription: (providerName: string) => `连接 ${providerName},以在您的工作区中同步员工审批。`,
approvalMode: '审批模式',
finalApprover: '最终审批人',
providerFinalApprover: (providerName: string) => `${providerName} 最终审批人`,
notSet: '未设置',
approvalModeDescription: (providerName: string) => `成员和管理员已设置为与 ${providerName} 同步。`,
approvalModeWarningTitle: '更改审批模式?',
Expand Down
73 changes: 73 additions & 0 deletions src/pages/workspace/hr/HRFinalApproverPageBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import WorkspaceMembersSelectionList from '@components/WorkspaceMembersSelectionList';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type Beta from '@src/types/onyx/Beta';
import type {PolicyConnectionSyncProgress} from '@src/types/onyx/Policy';
import type Policy from '@src/types/onyx/Policy';

type HRFinalApproverProviderConfig = {
testID: string;
beta: Beta;
isConnected: (policy: OnyxEntry<Policy>) => boolean;
getCurrentFinalApprover: (policy: OnyxEntry<Policy>) => string | null;
getHeaderTitle: (providerName: string) => string;
getProviderName: (policy: OnyxEntry<Policy>) => string;
handleSave: (params: {policyID: string; email: string; currentFinalApprover: string | null; connectionSyncProgress?: OnyxEntry<PolicyConnectionSyncProgress>}) => void;
};

type HRFinalApproverPageBaseProps = {
policyID: string;
config: HRFinalApproverProviderConfig;
};

function HRFinalApproverPageBase({policyID, config}: HRFinalApproverPageBaseProps) {
const styles = useThemeStyles();
const {isBetaEnabled} = usePermissions();
const policy = usePolicy(policyID);
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`);
const finalApprover = config.getCurrentFinalApprover(policy);
const providerName = config.getProviderName(policy);

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(config.beta) || (!!policy && !config.isConnected(policy))}
>
<ScreenWrapper
enableEdgeToEdgeBottomSafeAreaPadding
style={[styles.defaultModalContainer]}
testID={config.testID}
shouldEnableMaxHeight
>
<HeaderWithBackButton
title={config.getHeaderTitle(providerName)}
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID))}
/>
<WorkspaceMembersSelectionList
policyID={policyID}
selectedApprover={finalApprover ?? ''}
setApprover={(email) => {
config.handleSave({policyID, email, currentFinalApprover: finalApprover, connectionSyncProgress});
Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID)));
}}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
);
}

export type {HRFinalApproverProviderConfig};
export default HRFinalApproverPageBase;
56 changes: 15 additions & 41 deletions src/pages/workspace/hr/gusto/GustoFinalApproverPage.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
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 useOnyx from '@hooks/useOnyx';
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 HRFinalApproverPageBase from '@pages/workspace/hr/HRFinalApproverPageBase';
import type {HRFinalApproverProviderConfig} from '@pages/workspace/hr/HRFinalApproverPageBase';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

type GustoFinalApproverPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.HR_GUSTO_FINAL_APPROVER>;
Expand All @@ -25,40 +16,23 @@ function GustoFinalApproverPage({
params: {policyID},
},
}: GustoFinalApproverPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isBetaEnabled} = usePermissions();
const policy = usePolicy(policyID);
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`);
const finalApprover = policy?.connections?.gusto?.config?.finalApprover ?? null;

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'),
getHeaderTitle: () => translate('workspace.hr.finalApprover'),
handleSave: ({policyID: id, email, currentFinalApprover, connectionSyncProgress}) => updateGustoFinalApprover(id, email, currentFinalApprover, connectionSyncProgress),
};

return (
<AccessOrNotFoundWrapper
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
<HRFinalApproverPageBase
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.finalApprover')}
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID))}
/>
<WorkspaceMembersSelectionList
policyID={policyID}
selectedApprover={finalApprover ?? ''}
setApprover={(email) => {
updateGustoFinalApprover(policyID, email, finalApprover, connectionSyncProgress);
Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.WORKSPACE_HR.getRoute(policyID)));
}}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
config={config}
/>
);
}

Expand Down
35 changes: 17 additions & 18 deletions src/pages/workspace/hr/merge/MergeHRFinalApproverPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React from 'react';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import Navigation from '@libs/Navigation/Navigation';
import {updateMergeHRFinalApprover} from '@libs/actions/connections/MergeHR';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import {getConnectedHRProvider, isMergeHRConnected} 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';

Expand All @@ -18,22 +17,22 @@ function MergeHRFinalApproverPage({
},
}: MergeHRFinalApproverPageProps) {
const {translate} = useLocalize();
const {isBetaEnabled} = usePermissions();

const config: HRFinalApproverProviderConfig = {
testID: 'MergeHRFinalApproverPage',
beta: CONST.BETAS.MERGE_HR,
isConnected: isMergeHRConnected,
getCurrentFinalApprover: (policy) => policy?.connections?.merge_hris?.config?.finalApprover ?? null,
getProviderName: (policy) => getConnectedHRProvider(policy)?.displayName ?? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.merge_hris,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve provider name from Merge connection config

Using getConnectedHRProvider(policy) here can return Gusto or Zenefits because that helper picks the first connected HR integration, so when a workspace has multiple HR connections the Merge final-approver page can render the wrong header label (for example, showing a Gusto title on the Merge route). This page should derive its provider name from the Merge connection itself (e.g., policy.connections.merge_hris.config.integration) so the title always matches the active Merge provider.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a user should only have one HR integration connected

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering about the case when a policy deletes one connection and adds a second, do we have both objects

getHeaderTitle: (providerName) => translate('workspace.hr.providerFinalApprover', providerName),
handleSave: ({policyID: id, email, currentFinalApprover}) => updateMergeHRFinalApprover(id, email, currentFinalApprover),
};

return (
<AccessOrNotFoundWrapper
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
<HRFinalApproverPageBase
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.IS_HR_ENABLED}
shouldBeBlocked={!isBetaEnabled(CONST.BETAS.MERGE_HR)}
>
<ScreenWrapper testID="MergeHRFinalApproverPage">
<HeaderWithBackButton
title={translate('workspace.hr.finalApprover')}
onBackButtonPress={() => Navigation.goBack()}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
config={config}
/>
);
}

Expand Down
Loading