Skip to content
Open
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
25 changes: 22 additions & 3 deletions src/components/ApprovalWorkflowSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,37 @@ type ApprovalWorkflowSectionProps = {

/** Whether the workflow should be shown as read-only */
isDisabled?: boolean;

/** HR provider display name, used in manager mode to show "Manager (from {provider})" */
hrProviderName?: string;

/** When true, uses HR manager mode labels: "Manager (from {provider})" then "Final approver" */
isHRManagerMode?: boolean;
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.

The term "manager mode" is kind of confusing because there are three canonical approval modes in our backend:

  • Basic
  • Advanced (I think this is what we're calling "manager mode")
  • Manual

imo it would be better to use the same terms in the front-end and the back-end in the code, even though the product shows the modes as Basic, Manager, and Custom.

};

function ApprovalWorkflowSection({approvalWorkflow, onPress, currency = CONST.CURRENCY.USD, isDisabled = false}: ApprovalWorkflowSectionProps) {
function ApprovalWorkflowSection({approvalWorkflow, onPress, currency = CONST.CURRENCY.USD, isDisabled = false, hrProviderName, isHRManagerMode = false}: ApprovalWorkflowSectionProps) {
const icons = useMemoizedLazyExpensifyIcons(['ArrowRight', 'Lightbulb', 'Users', 'UserCheck']);
const styles = useThemeStyles();
const theme = useTheme();
const {translate, toLocaleOrdinal, localeCompare} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const {shouldUseNarrowLayout} = useResponsiveLayout();

const approverTitle = (index: number) =>
approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`;
const fromProviderSuffix = hrProviderName ? ` (${translate('workflowsPage.approverFromProvider', {provider: hrProviderName})})` : '';
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.

I think we should define this right before it's being used here


const approverTitle = (index: number) => {
if (isHRManagerMode) {
if (approvalWorkflow.approvers.length <= 1) {
return translate('workflowsPage.approver');
}
const isLastApprover = index === approvalWorkflow.approvers.length - 1;
if (isLastApprover && approvalWorkflow.approvers.length > 1) {
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.

approvalWorkflow.approvers.length > 1 is always true

return translate('workflowsPage.finalApprover');
}
Comment on lines +56 to +59
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 Label last approver as final only when final approver exists

In HR manager mode this unconditionally labels the last row as Final approver, but convertPolicyEmployeesToApprovalWorkflows() only appends a real final approver when mergeConfig.finalApprover is set. During setup/migration states where manager mode is enabled but finalApprover is still null, the chain can end with a manager and this UI will mislabel that manager as the final approver, which is misleading for admins reviewing routing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

return `${translate('workflowsPage.manager')}${fromProviderSuffix}`;
}
return approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : translate('workflowsPage.approver');
};

const sortedMembers = approvalWorkflow.isDefault ? [] : sortAlphabetically(approvalWorkflow.members, 'displayName', localeCompare);

Expand Down
3 changes: 3 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2617,6 +2617,9 @@ ${amount} für ${merchant} – ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Genehmigungen werden über deine ${provider}-Integration verwaltet. Um deinen Genehmigungsworkflow zu aktualisieren, gehe zu deinen ${provider}-Verbindungseinstellungen.`,
goToHRSettings: ({provider}: {provider: string}) => `Zu den ${provider}-Einstellungen gehen`,
approverFromProvider: ({provider}: {provider: string}) => `von ${provider}`,
finalApprover: 'Letzte*r Genehmigende*r',
manager: 'Manager',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: 'Sendehäufigkeit konnte nicht geändert werden. Bitte versuche es erneut oder kontaktiere den Support.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2624,6 +2624,9 @@ const translations = {
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Approvals are managed by your ${provider} integration. To update your approval workflow, head to your ${provider} connection settings.`,
goToHRSettings: ({provider}: {provider: string}) => `Go to ${provider} settings`,
approverFromProvider: ({provider}: {provider: string}) => `from ${provider}`,
finalApprover: 'Final approver',
manager: 'Manager',
makeOrTrackPaymentsTitle: 'Payments',
makeOrTrackPaymentsDescription: 'Add an authorized payer for payments made in Expensify or track payments made elsewhere.',
customApprovalWorkflowEnabled:
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2440,6 +2440,9 @@ ${amount} para ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Las aprobaciones se gestionan mediante tu integración de ${provider}. Para actualizar tu flujo de aprobación, ve a la configuración de conexión de ${provider}.`,
goToHRSettings: ({provider}: {provider: string}) => `Ir a la configuración de ${provider}`,
approverFromProvider: ({provider}: {provider: string}) => `de ${provider}`,
finalApprover: 'Aprobador final',
manager: 'Responsable',
makeOrTrackPaymentsTitle: 'Realizar o seguir pagos',
makeOrTrackPaymentsDescription: 'Añade un pagador autorizado para los pagos realizados en Expensify o realiza un seguimiento de los pagos realizados en otro lugar.',
customApprovalWorkflowEnabled:
Expand Down
3 changes: 3 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2622,6 +2622,9 @@ ${amount} pour ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Les validations sont gérées par votre intégration ${provider}. Pour mettre à jour votre workflow de validation, accédez aux paramètres de connexion ${provider}.`,
goToHRSettings: ({provider}: {provider: string}) => `Aller aux paramètres ${provider}`,
approverFromProvider: ({provider}: {provider: string}) => `de la part de ${provider}`,
finalApprover: 'Approbateur final',
manager: 'Manager',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: "La fréquence de soumission n'a pas pu être modifiée. Veuillez réessayer ou contacter l'assistance.",
Expand Down
3 changes: 3 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,9 @@ ${amount} per ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Le approvazioni sono gestite dalla tua integrazione con ${provider}. Per aggiornare il flusso di approvazione, vai alle impostazioni di connessione di ${provider}.`,
goToHRSettings: ({provider}: {provider: string}) => `Vai alle impostazioni di ${provider}`,
approverFromProvider: ({provider}: {provider: string}) => `da ${provider}`,
finalApprover: 'Approvazione finale',
manager: 'Responsabile',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: 'Impossibile modificare la frequenza di invio. Riprova oppure contatta l’assistenza.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,9 @@ ${date} の ${merchant} への ${amount}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`承認は${provider}連携によって管理されています。承認ワークフローを更新するには、${provider}接続設定に移動してください。`,
goToHRSettings: ({provider}: {provider: string}) => `${provider}設定に移動`,
approverFromProvider: ({provider}: {provider: string}) => `${provider}から`,
finalApprover: '最終承認者',
manager: 'マネージャー',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: '提出頻度を変更できませんでした。もう一度お試しいただくか、サポートまでご連絡ください。',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,9 @@ ${amount} voor ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Goedkeuringen worden beheerd door je ${provider}-integratie. Ga naar je ${provider}-verbindingsinstellingen om je goedkeuringsworkflow bij te werken.`,
goToHRSettings: ({provider}: {provider: string}) => `Ga naar ${provider}-instellingen`,
approverFromProvider: ({provider}: {provider: string}) => `van ${provider}`,
finalApprover: 'Laatste fiatteur',
manager: 'Manager',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: 'Indienfrequentie kon niet worden gewijzigd. Probeer het opnieuw of neem contact op met support.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2605,6 +2605,9 @@ ${amount} dla ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`Zatwierdzanie jest zarządzane przez Twoją integrację z ${provider}. Aby zaktualizować swój proces zatwierdzania, przejdź do ustawień połączenia z ${provider}.`,
goToHRSettings: ({provider}: {provider: string}) => `Przejdź do ustawień ${provider}`,
approverFromProvider: ({provider}: {provider: string}) => `od ${provider}`,
finalApprover: 'Ostateczny akceptujący',
manager: 'Menedżer',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: 'Nie udało się zmienić częstotliwości wysyłania. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2603,6 +2603,9 @@ ${amount} para ${merchant} - ${date}`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) =>
`As aprovações são gerenciadas pela sua integração com o ${provider}. Para atualizar seu fluxo de aprovação, vá até as configurações de conexão do ${provider}.`,
goToHRSettings: ({provider}: {provider: string}) => `Ir para as configurações do ${provider}`,
approverFromProvider: ({provider}: {provider: string}) => `de ${provider}`,
finalApprover: 'Aprovador final',
manager: 'Gerente',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: 'Não foi possível alterar a frequência de envio. Tente novamente ou entre em contato com o suporte.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2535,6 +2535,9 @@ ${amount},商户:${merchant} - 日期:${date}`,
configureViaHR: ({provider}: {provider: string}) => `通过 ${provider} 配置。`,
hrApprovalWorkflowLockedPrompt: ({provider}: {provider: string}) => `审批由你的 ${provider} 集成管理。若要更新审批流程,请前往 ${provider} 连接设置。`,
goToHRSettings: ({provider}: {provider: string}) => `前往 ${provider} 设置`,
approverFromProvider: ({provider}: {provider: string}) => `来自 ${provider}`,
finalApprover: '最终审批人',
manager: '经理',
},
workflowsDelayedSubmissionPage: {
autoReportingFrequencyErrorMessage: '提交频率无法更改。请重试或联系支持团队。',
Expand Down
22 changes: 22 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,26 @@ function getApprovalWorkflow(policy: OnyxEntry<Policy>): ValueOf<typeof CONST.PO
return policy?.approvalMode ?? CONST.POLICY.APPROVAL_MODE.ADVANCED;
}

/** Returns the Merge HR finalApprover when the integration is in "basic" approval mode, or null otherwise. */
function getMergeHRBasicModeFinalApprover(policy: OnyxEntry<Policy>): string | null {
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.

NAB: this file has become so large that syntax highlighting has stopped working. Put Merge.dev / HR sync stuff in a new dedicated file.

const mergeConfig = policy?.connections?.merge_hris?.config;
if (mergeConfig?.approvalMode === CONST.MERGE_HR.APPROVAL_MODE.BASIC && mergeConfig.finalApprover) {
return mergeConfig.finalApprover;
}

return null;
}

/** Returns the Merge HR finalApprover when the integration is in basic or manager mode, or null otherwise. */
function getMergeHRFinalApprover(policy: OnyxEntry<Policy>): string | null {
const mergeConfig = policy?.connections?.merge_hris?.config;
if ((mergeConfig?.approvalMode === CONST.MERGE_HR.APPROVAL_MODE.BASIC || mergeConfig?.approvalMode === CONST.MERGE_HR.APPROVAL_MODE.MANAGER) && mergeConfig?.finalApprover) {
return mergeConfig.finalApprover;
}

return null;
}

function getDefaultApprover(policy: OnyxEntry<Policy>): string {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return policy?.approver || policy?.owner || '';
Expand Down Expand Up @@ -2485,6 +2505,8 @@ export {
getCurrentConnectionName,
getCustomersOrJobsLabelNetSuite,
getDefaultApprover,
getMergeHRBasicModeFinalApprover,
getMergeHRFinalApprover,
getApprovalWorkflow,
getReimburserAccountID,
isControlPolicy,
Expand Down
45 changes: 38 additions & 7 deletions src/libs/WorkflowUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type Policy from '@src/types/onyx/Policy';
import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import type {PolicyEmployeeList} from '@src/types/onyx/PolicyEmployee';
import {isBankAccountPartiallySetup} from './BankAccountUtils';
import {getDefaultApprover, isExpensifyTeam, shouldFilterExpensifyTeam} from './PolicyUtils';
import {getDefaultApprover, getMergeHRFinalApprover, isExpensifyTeam, shouldFilterExpensifyTeam} from './PolicyUtils';

const INITIAL_APPROVAL_WORKFLOW: ApprovalWorkflowOnyx = {
members: [],
Expand Down Expand Up @@ -150,7 +150,7 @@ function findFirstNonExpensifyApprover(employees: PolicyEmployeeList, startEmail
/** Convert a list of policy employees to a list of approval workflows */
function convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, firstApprover, localeCompare, currentUserLogin}: PolicyConversionParams): PolicyConversionResult {
const employees = policy?.employeeList ?? {};
const defaultApprover = getDefaultApprover(policy);
const defaultApprover = getMergeHRFinalApprover(policy) ?? getDefaultApprover(policy);
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 Keep default approver resolvable in Merge HR modes

Using getMergeHRFinalApprover(policy) as defaultApprover can set the default workflow anchor to an email that is not present in employeeList; when that happens, no workflow is created for that approver and the later default-workflow insertion path ends up with calculateApprovers(...) returning an empty approver chain. This produces an empty "Everyone" workflow in read-only HR basic/manager setups instead of showing the required approver, so admins lose the routing visibility this change is trying to provide.

Useful? React with 👍 / 👎.

const approvalWorkflows: Record<string, ApprovalWorkflow> = {};
const shouldFilterOutExpensifyTeam = shouldFilterExpensifyTeam(policy?.owner, currentUserLogin);

Expand All @@ -161,6 +161,7 @@ function convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, fir
personalDetailsByEmail[value?.login ?? key] = value;
}
const availableMembers: Member[] = [];
const isMergeHRManagerMode = policy?.connections?.merge_hris?.config?.approvalMode === CONST.MERGE_HR.APPROVAL_MODE.MANAGER;

for (const employee of Object.values(employees)) {
const {email, submitsTo, pendingAction} = employee;
Expand All @@ -179,19 +180,30 @@ function convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, fir
availableMembers.push(member);
}

if (!submitsTo || !employees[submitsTo]) {
if (!submitsTo) {
continue;
}

// If submitsTo is an Expensify team member, find the first non-Expensify approver in the chain
const effectiveSubmitsTo = shouldFilterOutExpensifyTeam ? (findFirstNonExpensifyApprover(employees, submitsTo) ?? submitsTo) : submitsTo;

if (!employees[effectiveSubmitsTo]) {
if (!employees[submitsTo] && !isMergeHRManagerMode) {
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.

NAB: we have two back-to-back if statements that just do continue. Maybe it would be cleaner to combine them.

continue;
}

// If submitsTo is an Expensify team member, find the first non-Expensify approver in the chain
const effectiveSubmitsTo = shouldFilterOutExpensifyTeam && employees[submitsTo] ? (findFirstNonExpensifyApprover(employees, submitsTo) ?? submitsTo) : submitsTo;

if (!approvalWorkflows[effectiveSubmitsTo]) {
let approvers = calculateApprovers({employees, firstEmail: effectiveSubmitsTo, personalDetailsByEmail});
if (approvers.length === 0) {
approvers = [
{
email: effectiveSubmitsTo,
forwardsTo: undefined,
avatar: personalDetailsByEmail[effectiveSubmitsTo]?.avatar,
displayName: personalDetailsByEmail[effectiveSubmitsTo]?.displayName ?? effectiveSubmitsTo,
isCircularReference: false,
},
];
}
if (shouldFilterOutExpensifyTeam) {
approvers = approvers.filter((approver) => !isExpensifyTeam(approver.email));
}
Expand Down Expand Up @@ -225,6 +237,25 @@ function convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, fir
}
}

// In Merge HR manager mode, append the finalApprover to each chain if not already present
const mergeConfig = policy?.connections?.merge_hris?.config;
if (isMergeHRManagerMode && mergeConfig?.finalApprover) {
const finalApproverEmail = mergeConfig.finalApprover;
for (const workflow of Object.values(approvalWorkflows)) {
const lastApprover = workflow.approvers.at(-1);
if (lastApprover && lastApprover.email !== finalApproverEmail) {
workflow.approvers.push({
email: finalApproverEmail,
forwardsTo: undefined,
avatar: personalDetailsByEmail[finalApproverEmail]?.avatar,
displayName: personalDetailsByEmail[finalApproverEmail]?.displayName ?? finalApproverEmail,
isCircularReference: false,
});
usedApproverEmails.add(finalApproverEmail);
}
}
}

// Sort the workflows by the first approver's name (default workflow has priority)
const sortedApprovalWorkflows = Object.values(approvalWorkflows).sort((a, b) => {
if (a.isDefault) {
Expand Down
8 changes: 7 additions & 1 deletion src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,10 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) {
Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EXPENSES_FROM.getRoute(route.params.policyID));
}, [policy, route.params.policyID, availableMembers, usedApproverEmails, canAccessSubmit2026Features, navigateToSubmitWorkspaceApprovalsUpgrade]);

const isMergeHRManagerMode = policy?.connections?.merge_hris?.config?.approvalMode === CONST.MERGE_HR.APPROVAL_MODE.MANAGER;

const filteredApprovalWorkflows =
policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.ADVANCED || policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.DYNAMICEXTERNAL
policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.ADVANCED || policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.DYNAMICEXTERNAL || isMergeHRManagerMode
? approvalWorkflows
: approvalWorkflows.filter((workflow) => workflow.isDefault);

Expand Down Expand Up @@ -458,6 +460,8 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) {
}
currency={policy?.outputCurrency}
isDisabled={shouldBlockApprovalWorkflowEditing}
hrProviderName={isHRConnected ? hrProviderName : undefined}
isHRManagerMode={isMergeHRManagerMode}
/>
</OfflineWithFeedback>
))}
Expand Down Expand Up @@ -657,6 +661,8 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) {
onPressAutoReportingFrequency,
isSmartLimitEnabled,
isHRConnected,
hrProviderName,
isMergeHRManagerMode,
shouldBlockApprovalWorkflowEditing,
approvalSubtitle,
navigateToSubmitWorkspaceApprovalsUpgrade,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types';
import {canEditWorkspaceSettings, goBackFromInvalidPolicy, isPendingDeletePolicy} from '@libs/PolicyUtils';
import {canEditWorkspaceSettings, goBackFromInvalidPolicy, isAnyHRReadOnlyWorkflowMode, isPendingDeletePolicy} from '@libs/PolicyUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading';
import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading';
Expand All @@ -37,7 +37,8 @@ function WorkspaceWorkflowsApprovalsCreatePage({policy, isLoadingReportData = tr
const [addExpenseApprovalsTaskReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${addExpenseApprovalsTaskReportID}`);
const formRef = useRef<ScrollView>(null);

const shouldShowNotFoundView = (isEmptyObject(policy) && !isLoadingReportData) || !canEditWorkspaceSettings(policy) || isPendingDeletePolicy(policy);
const shouldShowNotFoundView =
(isEmptyObject(policy) && !isLoadingReportData) || !canEditWorkspaceSettings(policy) || isPendingDeletePolicy(policy) || isAnyHRReadOnlyWorkflowMode(policy);

const createApprovalWorkflow = useCallback(() => {
if (!approvalWorkflow) {
Expand Down
Loading
Loading