diff --git a/src/hooks/useApproveOnlyRoleBlockedModal.ts b/src/hooks/useApproveOnlyRoleBlockedModal.ts index 37f1e61cb985..5f14c88a0951 100644 --- a/src/hooks/useApproveOnlyRoleBlockedModal.ts +++ b/src/hooks/useApproveOnlyRoleBlockedModal.ts @@ -2,7 +2,7 @@ * Subscribes to BLOCKED_ROLE_CHANGE in Onyx and shows the blocked-reasons modal on the global modal stack * when the backend refuses an approve-only role change. */ -import {clearBlockedRoleChange, clearUpdateMemberRoleError} from '@userActions/Policy/Member'; +import {clearBlockedRoleChange} from '@userActions/Policy/Member'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -47,17 +47,7 @@ function useApproveOnlyRoleBlockedModal() { lastShownRef.current = blockedRoleChange; isModalOpenRef.current = true; - const {policyID, email, blockedReasons = []} = blockedRoleChange; - - // `OnyxUpdates.apply` runs `request.failureData` on the microtask after `response.onyxData`, which sets a - // generic inline error on the member row. Clear that row error once `failureData` has settled so the modal - // is the only feedback shown for a blocked role change. - if (policyID && email) { - Promise.resolve().then(() => { - clearUpdateMemberRoleError(policyID, email); - }); - } - + const {blockedReasons = []} = blockedRoleChange; const reasonLines = blockedReasons.filter((reason) => reason in REASON_TRANSLATION_KEYS).map((reason) => `• ${translate(REASON_TRANSLATION_KEYS[reason])}`); const hasReasonLines = reasonLines.length > 0; diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 384ee06abfc8..3a6dd2832a59 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -648,6 +648,8 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policy: OnyxEntry, sele }, ]; + // Roles that cannot create expenses show a dedicated modal on refusal, so skip inline row errors. + const canCreateExpenses = canRoleCreateExpenses(newRole); const failureData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -658,8 +660,8 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policy: OnyxEntry, sele // eslint-disable-next-line no-param-reassign member[current.email] = { ...(previousEmployeeList[current.email] ?? {}), - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.editor.genericFailureMessage'), + pendingAction: canCreateExpenses ? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE : null, + ...(canCreateExpenses && {errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.editor.genericFailureMessage')}), }; return member; }, {}), diff --git a/tests/actions/PolicyMemberTest.ts b/tests/actions/PolicyMemberTest.ts index 332f9f26b14e..85dbcb778ddc 100644 --- a/tests/actions/PolicyMemberTest.ts +++ b/tests/actions/PolicyMemberTest.ts @@ -213,6 +213,64 @@ describe('actions/PolicyMember', () => { }); }); }); + + it('does not set errors in failureData when changing role to approve-only', () => { + const member1 = 'member1@test.com'; + const member2 = 'member2@test.com'; + const fakePolicy: PolicyType = { + ...createRandomPolicy(0, CONST.POLICY.TYPE.CORPORATE), + employeeList: { + [member1]: { + email: member1, + role: CONST.POLICY.ROLE.USER, + }, + [member2]: { + email: member2, + role: CONST.POLICY.ROLE.USER, + }, + }, + }; + + const approveOnlyData = Member.buildUpdateWorkspaceMembersRoleOnyxData(fakePolicy, [member1, member2], [1, 2], CONST.POLICY.ROLE.APPROVE_ONLY); + expect(approveOnlyData.failureData).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: `${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, + value: expect.objectContaining({ + employeeList: { + [member1]: { + email: member1, + role: CONST.POLICY.ROLE.USER, + pendingAction: null, + }, + [member2]: { + email: member2, + role: CONST.POLICY.ROLE.USER, + pendingAction: null, + }, + }, + }), + }), + ]), + ); + + const adminData = Member.buildUpdateWorkspaceMembersRoleOnyxData(fakePolicy, [member1], [1], CONST.POLICY.ROLE.ADMIN); + expect(adminData.failureData).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: `${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, + value: expect.objectContaining({ + employeeList: expect.objectContaining({ + [member1]: expect.objectContaining({ + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + errors: TestHelper.anyObject, + }), + }), + }), + }), + ]), + ); + }); }); describe('requestWorkspaceOwnerChange', () => { it('Change the workspace`s owner', async () => {