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
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import type {
PaySomeoneParams,
ReimbursementRateParams,
RemovedTheRequestParams,
RemoveMembersWarningPrompt,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
ReportArchiveReasonsMergedParams,
Expand Down Expand Up @@ -2369,6 +2370,8 @@ export default {
people: {
genericFailureMessage: 'An error occurred removing a user from the workspace, please try again.',
removeMembersPrompt: 'Are you sure you want to remove these members?',
removeMembersWarningPrompt: ({memberName, ownerName}: RemoveMembersWarningPrompt) =>
`${memberName} is an approver in this workspace. When you unshare this workspace with them, we’ll replace them in the approval workflow with the workspace owner, ${ownerName}`,
removeMembersTitle: 'Remove members',
removeMemberButtonTitle: 'Remove from workspace',
removeMemberGroupButtonTitle: 'Remove from group',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import type {
PaySomeoneParams,
ReimbursementRateParams,
RemovedTheRequestParams,
RemoveMembersWarningPrompt,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
ReportArchiveReasonsMergedParams,
Expand Down Expand Up @@ -2404,6 +2405,8 @@ export default {
people: {
genericFailureMessage: 'Se ha producido un error al intentar eliminar a un miembro del espacio de trabajo. Por favor, inténtalo más tarde.',
removeMembersPrompt: '¿Estás seguro de que deseas eliminar a estos miembros?',
removeMembersWarningPrompt: ({memberName, ownerName}: RemoveMembersWarningPrompt) =>
`${memberName} es un aprobador en este espacio de trabajo. Cuando lo elimine de este espacio de trabajo, los sustituiremos en el flujo de trabajo de aprobación por el propietario del espacio de trabajo, ${ownerName}`,
removeMembersTitle: 'Eliminar miembros',
removeMemberButtonTitle: 'Quitar del espacio de trabajo',
removeMemberGroupButtonTitle: 'Quitar del grupo',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ type DistanceRateOperationsParams = {count: number};

type ReimbursementRateParams = {unit: Unit};

type RemoveMembersWarningPrompt = {
memberName: string;
ownerName: string;
};

export type {
AddressLineParams,
AdminCanceledRequestParams,
Expand Down Expand Up @@ -402,4 +407,5 @@ export type {
WelcomeNoteParams,
WelcomeToRoomParams,
ZipCodeExampleFormatParams,
RemoveMembersWarningPrompt,
};
45 changes: 45 additions & 0 deletions src/libs/actions/Policy/Member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ Onyx.connect({
},
});

/** Check if the passed employee is an approver in the policy's employeeList */
function isApprover(policy: OnyxEntry<Policy>, employeeAccountID: number) {
const employeeLogin = allPersonalDetails?.[employeeAccountID]?.login;
return Object.values(policy?.employeeList ?? {}).some(
(employee) => employee?.submitsTo === employeeLogin || employee?.forwardsTo === employeeLogin || employee?.overLimitForwardsTo === employeeLogin,
);
}

/**
* Returns the policy of the report
*/
Expand Down Expand Up @@ -243,6 +251,42 @@ function removeMembers(accountIDs: number[], policyID: string) {
failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericRemove')};
});

Object.keys(policy?.employeeList ?? {}).forEach((employeeEmail) => {
const employee = policy?.employeeList?.[employeeEmail];
optimisticMembersState[employeeEmail] = optimisticMembersState[employeeEmail] ?? {};
failureMembersState[employeeEmail] = failureMembersState[employeeEmail] ?? {};
if (employee?.submitsTo && emailList.includes(employee?.submitsTo)) {
optimisticMembersState[employeeEmail] = {
...optimisticMembersState[employeeEmail],
submitsTo: policy?.owner,
};
failureMembersState[employeeEmail] = {
...failureMembersState[employeeEmail],
submitsTo: employee?.submitsTo,
};
}
if (employee?.forwardsTo && emailList.includes(employee?.forwardsTo)) {
optimisticMembersState[employeeEmail] = {
...optimisticMembersState[employeeEmail],
forwardsTo: policy?.owner,
};
failureMembersState[employeeEmail] = {
...failureMembersState[employeeEmail],
forwardsTo: employee?.forwardsTo,
};
}
if (employee?.overLimitForwardsTo && emailList.includes(employee?.overLimitForwardsTo)) {
optimisticMembersState[employeeEmail] = {
...optimisticMembersState[employeeEmail],
overLimitForwardsTo: policy?.owner,
};
failureMembersState[employeeEmail] = {
...failureMembersState[employeeEmail],
overLimitForwardsTo: employee?.overLimitForwardsTo,
};
}
});

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand Down Expand Up @@ -801,6 +845,7 @@ export {
inviteMemberToWorkspace,
acceptJoinRequest,
declineJoinRequest,
isApprover,
};

export type {NewCustomUnit};
14 changes: 13 additions & 1 deletion src/pages/workspace/WorkspaceMembersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type {FullScreenNavigatorParamList} from '@libs/Navigation/types';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import {getDisplayNameForParticipant} from '@libs/ReportUtils';
import * as Member from '@userActions/Policy/Member';
import * as Policy from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -95,6 +96,17 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
const selectionListRef = useRef<SelectionListHandle>(null);
const isFocused = useIsFocused();
const policyID = route.params.policyID;

const confirmModalPrompt = useMemo(() => {
const approverAccountID = selectedEmployees.find((selectedEmployee) => Member.isApprover(policy, selectedEmployee));
if (!approverAccountID) {
return translate('workspace.people.removeMembersPrompt');
}
return translate('workspace.people.removeMembersWarningPrompt', {
memberName: getDisplayNameForParticipant(approverAccountID),
ownerName: getDisplayNameForParticipant(policy?.ownerAccountID),
});
}, [selectedEmployees, policy, translate]);
/**
* Get filtered personalDetails list with current employeeList
*/
Expand Down Expand Up @@ -549,7 +561,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
isVisible={removeMembersConfirmModalVisible}
onConfirm={removeUsers}
onCancel={() => setRemoveMembersConfirmModalVisible(false)}
prompt={translate('workspace.people.removeMembersPrompt')}
prompt={confirmModalPrompt}
confirmText={translate('common.remove')}
cancelText={translate('common.cancel')}
onModalHide={() => {
Expand Down
10 changes: 9 additions & 1 deletion src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
const isCurrentUserAdmin = policy?.employeeList?.[personalDetails?.[currentUserPersonalDetails?.accountID]?.login ?? '']?.role === CONST.POLICY.ROLE.ADMIN;
const isCurrentUserOwner = policy?.owner === currentUserPersonalDetails?.login;

const confirmModalPrompt = useMemo(() => {
const isApprover = Member.isApprover(policy, accountID);
if (!isApprover) {
translate('workspace.people.removeMemberPrompt', {memberName: displayName});
Copy link
Contributor

Choose a reason for hiding this comment

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

We should have returned the text here if the user was not a approver. We first set a message but later returned the approver message, this is kind of funny overlook but happens with the best of us.

This caused #45510

}
return translate('workspace.people.removeMembersWarningPrompt', {memberName: displayName, ownerName: policy?.owner ?? ''});
}, [accountID, policy, displayName, translate]);

const roleItems: ListItemType[] = useMemo(
() => [
{
Expand Down Expand Up @@ -188,7 +196,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
isVisible={isRemoveMemberConfirmModalVisible}
onConfirm={removeUser}
onCancel={() => setIsRemoveMemberConfirmModalVisible(false)}
prompt={translate('workspace.people.removeMemberPrompt', {memberName: displayName})}
prompt={confirmModalPrompt}
confirmText={translate('common.remove')}
cancelText={translate('common.cancel')}
/>
Expand Down
8 changes: 7 additions & 1 deletion src/types/onyx/PolicyEmployee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ type PolicyEmployee = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Email of the user */
email?: string;

/** Email of the user this user forwards all approved reports to */
/** Determines if this employee should approve a report. If report total > approvalLimit, next approver will be 'overLimitForwardsTo', otherwise 'forwardsTo' */
approvalLimit?: number;

/** Email of the user this user forwards all approved reports to (when report total under 'approvalLimit' or when 'overLimitForwardsTo' is not set) */
forwardsTo?: string;

/** Email of the user this user submits all reports to */
submitsTo?: string;

/** Email of the user this user forwards all reports to when the report total is over the 'approvalLimit' */
overLimitForwardsTo?: string;

/**
* Errors from api calls on the specific user
* {<timestamp>: 'error message', <timestamp2>: 'error message 2'}
Expand Down