diff --git a/src/languages/de.ts b/src/languages/de.ts index d06de5339f6..e4debff60ad 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -7153,6 +7153,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc syncError: (providerName: string) => `Verbindung zu ${providerName} nicht möglich`, connectionDescription: (providerName: string) => `Verbinden Sie ${providerName}, um Mitarbeitergenehmigungen mit Ihrem Workspace zu synchronisieren.`, approvalMode: 'Genehmigungsmodus', + providerApprovalMode: (providerName: string) => `${providerName}-Genehmigungsmodus`, finalApprover: 'Endgültige:r Genehmiger:in', providerFinalApprover: (providerName: string) => `${providerName} Endgenehmigende*r`, notSet: 'Nicht festgelegt', diff --git a/src/languages/en.ts b/src/languages/en.ts index 1c78a7d1cc2..e9fbab6c603 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -6455,6 +6455,7 @@ const translations = { syncError: (providerName: string) => `Can't connect to ${providerName}`, connectionDescription: (providerName: string) => `Connect ${providerName} to keep employee approvals in sync with your workspace.`, approvalMode: 'Approval mode', + providerApprovalMode: (providerName: string) => `${providerName} approval mode`, finalApprover: 'Final approver', providerFinalApprover: (providerName: string) => `${providerName} final approver`, notSet: 'Not set', diff --git a/src/languages/es.ts b/src/languages/es.ts index 82a6b681bc9..92bf3cfb866 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -6268,6 +6268,7 @@ ${amount} para ${merchant} - ${date}`, syncError: (providerName: string) => `No se puede conectar con ${providerName}`, connectionDescription: (providerName: string) => `Conecta ${providerName} para mantener sincronizadas las aprobaciones de empleados con tu espacio de trabajo.`, approvalMode: 'Modo de aprobación', + providerApprovalMode: (providerName: string) => `Modo de aprobación de ${providerName}`, finalApprover: 'Aprobador final', providerFinalApprover: (providerName: string) => `Aprobador final de ${providerName}`, notSet: 'No configurado', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 8314c1611c3..05f0c8abdab 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -7176,6 +7176,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e syncError: (providerName: string) => `Impossible de se connecter à ${providerName}`, connectionDescription: (providerName: string) => `Connectez ${providerName} pour synchroniser les approbations des employés avec votre espace de travail.`, approvalMode: "Mode d'approbation", + providerApprovalMode: (providerName: string) => `Mode d'approbation ${providerName}`, finalApprover: 'Approbateur final', providerFinalApprover: (providerName: string) => `Approbateur final ${providerName}`, notSet: 'Non défini', diff --git a/src/languages/it.ts b/src/languages/it.ts index 2cc651d73d6..5171ad9538b 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -7140,6 +7140,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`, syncError: (providerName: string) => `Impossibile connettersi a ${providerName}`, connectionDescription: (providerName: string) => `Collega ${providerName} per mantenere sincronizzate le approvazioni dei dipendenti con il tuo spazio di lavoro.`, approvalMode: 'Modalità di approvazione', + providerApprovalMode: (providerName: string) => `Modalità di approvazione ${providerName}`, finalApprover: 'Approvatore finale', providerFinalApprover: (providerName: string) => `Approvatore finale ${providerName}`, notSet: 'Non impostato', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 9ed7a093c3f..069e8a9bf70 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -7059,6 +7059,7 @@ ${reportName} syncError: (providerName: string) => `${providerName}に接続できません`, connectionDescription: (providerName: string) => `${providerName}を接続して、従業員の承認をワークスペースと同期させましょう。`, approvalMode: '承認モード', + providerApprovalMode: (providerName: string) => `${providerName} 承認モード`, finalApprover: '最終承認者', providerFinalApprover: (providerName: string) => `${providerName} 最終承認者`, notSet: '未設定', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 58090079469..fa77838ffc0 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -7115,6 +7115,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`, syncError: (providerName: string) => `Kan geen verbinding maken met ${providerName}`, connectionDescription: (providerName: string) => `Verbind ${providerName} om goedkeuringen van werknemers gesynchroniseerd te houden met je werkruimte.`, approvalMode: 'Goedkeuringsmodus', + providerApprovalMode: (providerName: string) => `${providerName}-goedkeuringsmodus`, finalApprover: 'Eindgoedkeurder', providerFinalApprover: (providerName: string) => `Laatste ${providerName}-fiatteur`, notSet: 'Niet ingesteld', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 94347b63a19..ad21525bfad 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -7110,6 +7110,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`, syncError: (providerName: string) => `Nie można połączyć z ${providerName}`, connectionDescription: (providerName: string) => `Połącz ${providerName}, aby synchronizować akceptacje pracowników z Twoim miejscem pracy.`, approvalMode: 'Tryb zatwierdzania', + providerApprovalMode: (providerName: string) => `Tryb zatwierdzania ${providerName}`, finalApprover: 'Ostateczny zatwierdzający', providerFinalApprover: (providerName: string) => `Ostateczny zatwierdzający ${providerName}`, notSet: 'Nie ustawiono', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 5634690d9c2..c7e81b1905a 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -7115,6 +7115,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`, syncError: (providerName: string) => `Não é possível conectar ao ${providerName}`, connectionDescription: (providerName: string) => `Conecte ${providerName} para manter as aprovações de funcionários sincronizadas com seu workspace.`, approvalMode: 'Modo de aprovação', + providerApprovalMode: (providerName: string) => `Modo de aprovação do ${providerName}`, finalApprover: 'Aprovador final', providerFinalApprover: (providerName: string) => `Aprovador final de ${providerName}`, notSet: 'Não definido', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 725b24a7af1..e06bd81a997 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -6932,6 +6932,7 @@ ${reportName} syncError: (providerName: string) => `无法连接到 ${providerName}`, connectionDescription: (providerName: string) => `连接 ${providerName},以在您的工作区中同步员工审批。`, approvalMode: '审批模式', + providerApprovalMode: (providerName: string) => `${providerName} 审批模式`, finalApprover: '最终审批人', providerFinalApprover: (providerName: string) => `${providerName} 最终审批人`, notSet: '未设置', diff --git a/src/pages/workspace/hr/HRApprovalModePageBase.tsx b/src/pages/workspace/hr/HRApprovalModePageBase.tsx new file mode 100644 index 00000000000..9dd2dc0c322 --- /dev/null +++ b/src/pages/workspace/hr/HRApprovalModePageBase.tsx @@ -0,0 +1,160 @@ +import React, {useState} from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import Button from '@components/Button'; +import FixedFooter from '@components/FixedFooter'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; +import RenderHTML from '@components/RenderHTML'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import Text from '@components/Text'; +import useConfirmModal from '@hooks/useConfirmModal'; +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 Navigation from '@libs/Navigation/Navigation'; +import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type Beta from '@src/types/onyx/Beta'; +import type Policy from '@src/types/onyx/Policy'; +import type {PolicyConnectionSyncProgress} from '@src/types/onyx/Policy'; + +type ApprovalModeValue = ValueOf | ValueOf; + +type HRApprovalModeProviderConfig = { + testID: string; + beta: Beta; + isConnected: (policy: OnyxEntry) => boolean; + approvalModes: {BASIC: T; MANAGER: T; CUSTOM: T}; + getCurrentApprovalMode: (policy: OnyxEntry) => T | null; + getProviderName: (policy: OnyxEntry) => string; + getHeaderTitle: (providerName: string) => string; + handleSave: (params: {policyID: string; draftApprovalMode: T; currentApprovalMode: T | null; connectionSyncProgress?: OnyxEntry}) => void; +}; + +type ApprovalModeListItem = ListItem & { + value: T; +}; + +type HRApprovalModePageBaseProps = { + policyID: string; + config: HRApprovalModeProviderConfig; +}; + +function HRApprovalModePageBase({policyID, config}: HRApprovalModePageBaseProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const {showConfirmModal} = useConfirmModal(); + const {isBetaEnabled} = usePermissions(); + const policy = usePolicy(policyID); + const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`); + + const providerName = config.getProviderName(policy); + const currentApprovalMode = config.getCurrentApprovalMode(policy); + const [draftApprovalMode, setDraftApprovalMode] = useState(); + const selectedApprovalMode = draftApprovalMode ?? currentApprovalMode; + const isSaveDisabled = !draftApprovalMode || draftApprovalMode === currentApprovalMode; + + const approvalModeOptions: Array> = [ + { + text: translate('workspace.hr.approvalModes.basic.label'), + alternateText: translate('workspace.hr.approvalModes.basic.description'), + keyForList: config.approvalModes.BASIC, + value: config.approvalModes.BASIC, + isSelected: selectedApprovalMode === config.approvalModes.BASIC, + }, + { + text: translate('workspace.hr.approvalModes.manager.label'), + alternateText: translate('workspace.hr.approvalModes.manager.description', providerName), + keyForList: config.approvalModes.MANAGER, + value: config.approvalModes.MANAGER, + isSelected: selectedApprovalMode === config.approvalModes.MANAGER, + }, + { + text: translate('workspace.hr.approvalModes.custom.label'), + alternateText: translate('workspace.hr.approvalModes.custom.description'), + keyForList: config.approvalModes.CUSTOM, + value: config.approvalModes.CUSTOM, + isSelected: selectedApprovalMode === config.approvalModes.CUSTOM, + }, + ]; + const selectedApprovalModeKey = approvalModeOptions.find((option) => option.isSelected)?.keyForList; + + const saveApprovalMode = () => { + if (!draftApprovalMode) { + return; + } + + config.handleSave({policyID, draftApprovalMode, currentApprovalMode, connectionSyncProgress}); + Navigation.goBack(); + }; + + const confirmSaveApprovalMode = () => { + showConfirmModal({ + title: translate('workspace.hr.approvalModeWarningTitle'), + prompt: ( + + + + ), + confirmText: translate('workspace.hr.approvalModeWarningConfirm'), + cancelText: translate('common.cancel'), + }).then((result) => { + if (result?.action !== ModalActions.CONFIRM) { + return; + } + saveApprovalMode(); + }); + }; + + return ( + + + Navigation.goBack()} + /> + + {translate('workspace.hr.approvalModeDescription', providerName)} + setDraftApprovalMode(option.value)} + shouldSingleExecuteRowSelect + initiallyFocusedItemKey={selectedApprovalModeKey} + alternateNumberOfSupportedLines={3} + showScrollIndicator={false} + /> + +