Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[$250] Hide current user in approver selector when 'prevent self approvals' is enabled #53612

Open
marcaaron opened this issue Dec 4, 2024 · 21 comments
Assignees
Labels
External Added to denote the issue can be worked on by a contributor NewFeature Something to build that is a new item. Reviewing Has a PR in review Weekly KSv2

Comments

@marcaaron
Copy link
Contributor

marcaaron commented Dec 4, 2024

cc @JmillsExpensify coming from our DM

While we have the ability to set "Prevent self approval" on a workspace. There are some ways in which this feature works unexpectedly in NewDot. Specifically, the following scenarios seem possible and should not be:

Incorrect workflow setup restrictions

Problem

  • User tries to create a new workflow with themselves as approver when prevent self approval is enabled.
  • This should not be possible. The OldDot behavior is to show a "problem" that needs to be fixed like this:

image

In NewDot, we just let it happen:

Screenshot 2024-12-04 at 1 24 36 PM
Screenshot 2024-12-04 at 1 24 58 PM

Solution:

  • Ideally, we would just remove this person from the list of approvers when giving the option to select an approver
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021866263734682899164
  • Upwork Job ID: 1866263734682899164
  • Last Price Increase: 2024-12-09
  • Automatic offers:
    • brunovjk | Reviewer | 105298681
    • Krishna2323 | Contributor | 105298682
Issue OwnerCurrent Issue Owner: @brunovjk
@marcaaron marcaaron added the NewFeature Something to build that is a new item. label Dec 4, 2024
Copy link

melvin-bot bot commented Dec 4, 2024

Triggered auto assignment to @anmurali (NewFeature), see https://stackoverflowteams.com/c/expensify/questions/14418#:~:text=BugZero%20process%20steps%20for%20feature%20requests for more details. Please add this Feature request to a GH project, as outlined in the SO.

@melvin-bot melvin-bot bot added the Weekly KSv2 label Dec 4, 2024
Copy link

melvin-bot bot commented Dec 4, 2024

⚠️ It looks like this issue is labelled as a New Feature but not tied to any GitHub Project. Keep in mind that all new features should be tied to GitHub Projects in order to properly track external CAP software time ⚠️

Copy link

melvin-bot bot commented Dec 4, 2024

Triggered auto assignment to Design team member for new feature review - @shawnborton (NewFeature)

@marcaaron
Copy link
Contributor Author

I think this part: Incorrect workflow setup restrictions can be external as it's straightforward enough.

How to handle the users who are already self-approvers is less clear to me and probably requires a non-trivial backend change and maybe better to discuss in #product first. I feel like this is an edge case related to Workflows feature (so cc'ing @tgolen in case he has thoughts on this one as well).

@Krishna2323
Copy link
Contributor

Krishna2323 commented Dec 5, 2024

Edited by proposal-police: This proposal was edited at 2024-12-05 00:52:32 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

Figure out how Prevent Self Approvals should work in NewDot and implement it

What is the root cause of that problem?

  • Improvement -- currently we do not filter out the members when policy?.preventSelfApproval is true and approvalWorkflow?.members include the approver (employee)
    const sections: ApproverSection[] = useMemo(() => {
    const approvers: SelectionListApprover[] = [];
    if (employeeList) {
    const availableApprovers = Object.values(employeeList)
    .map((employee): SelectionListApprover | null => {
    const isAdmin = employee?.role === CONST.REPORT.ROLE.ADMIN;
    const email = employee.email;
    if (!email) {
    return null;
    }
    // Do not allow the same email to be added twice
    const isEmailAlreadyInApprovers = approversFromWorkflow?.some((approver, index) => approver?.email === email && index !== approverIndex);
    if (isEmailAlreadyInApprovers && selectedApproverEmail !== email) {
    return null;
    }
    // Do not allow the default approver to be added as the first approver
    if (!isDefault && approverIndex === 0 && defaultApprover === email) {
    return null;
    }
    const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(employeeList);
    const accountID = Number(policyMemberEmailsToAccountIDs[email] ?? '');
    const {avatar, displayName = email} = personalDetails?.[accountID] ?? {};
    return {
    text: displayName,
    alternateText: email,
    keyForList: email,
    isSelected: selectedApproverEmail === email,
    login: email,
    icons: [{source: avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: displayName, id: accountID}],
    rightElement: isAdmin ? <Badge text={translate('common.admin')} /> : undefined,
    };
    })
    .filter((approver): approver is SelectionListApprover => !!approver);
    approvers.push(...availableApprovers);
    // eslint-disable-next-line react-compiler/react-compiler
    setAllApprovers(approvers);
    }
    const filteredApprovers =
    debouncedSearchTerm !== ''
    ? approvers.filter((option) => {
    const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(debouncedSearchTerm);
    const isPartOfSearchTerm = !!option.text?.toLowerCase().includes(searchValue) || !!option.login?.toLowerCase().includes(searchValue);
    return isPartOfSearchTerm;
    })
    : approvers;
    const data = OptionsListUtils.sortAlphabetically(filteredApprovers, 'text');
    return [
    {
    title: undefined,
    data,
    shouldShow: true,
    },
    ];
    }, [approversFromWorkflow, isDefault, approverIndex, debouncedSearchTerm, defaultApprover, personalDetails, employeeList, selectedApproverEmail, translate]);

What changes do you think we should make in order to solve the problem?

  • We can add another condition to filter out the logins which are included in the members list and policy?.preventSelfApproval is true.
Pseudocode
    const membersEmail = useMemo(() => approvalWorkflow?.members.map((mem) => mem.email), [approvalWorkflow?.members]);
    const sections: ApproverSection[] = useMemo(() => {
        const approvers: SelectionListApprover[] = [];

        if (employeeList) {
            const availableApprovers = Object.values(employeeList)
                .map((employee): SelectionListApprover | null => {
                    const isAdmin = employee?.role === CONST.REPORT.ROLE.ADMIN;
                    const email = employee.email;

                    if (!email) {
                        return null;
                    }

                    if (policy?.preventSelfApproval && membersEmail?.includes(email)) {
                        return null;
                    }
                    // Do not allow the same email to be added twice
                    const isEmailAlreadyInApprovers = approversFromWorkflow?.some((approver, index) => approver?.email === email && index !== approverIndex);
                    if (isEmailAlreadyInApprovers && selectedApproverEmail !== email) {
                        return null;
                    }

                    // Do not allow the default approver to be added as the first approver
                    if (!isDefault && approverIndex === 0 && defaultApprover === email) {
                        return null;
                    }

                    const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(employeeList);
                    const accountID = Number(policyMemberEmailsToAccountIDs[email] ?? '');
                    const {avatar, displayName = email} = personalDetails?.[accountID] ?? {};

                    return {
                        text: displayName,
                        alternateText: email,
                        keyForList: email,
                        isSelected: selectedApproverEmail === email,
                        login: email,
                        icons: [{source: avatar ?? FallbackAvatar, type: CONST.ICON_TYPE_AVATAR, name: displayName, id: accountID}],
                        rightElement: isAdmin ? <Badge text={translate('common.admin')} /> : undefined,
                    };
                })
                .filter((approver): approver is SelectionListApprover => !!approver);

            approvers.push(...availableApprovers);
            // eslint-disable-next-line react-compiler/react-compiler
            setAllApprovers(approvers);
        }

        const filteredApprovers =
            debouncedSearchTerm !== ''
                ? approvers.filter((option) => {
                      const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(debouncedSearchTerm);
                      const isPartOfSearchTerm = !!option.text?.toLowerCase().includes(searchValue) || !!option.login?.toLowerCase().includes(searchValue);
                      return isPartOfSearchTerm;
                  })
                : approvers;

        const data = OptionsListUtils.sortAlphabetically(filteredApprovers, 'text');
        return [
            {
                title: undefined,
                data,
                shouldShow: true,
            },
        ];
    }, [
        approversFromWorkflow,
        isDefault,
        approverIndex,
        debouncedSearchTerm,
        defaultApprover,
        personalDetails,
        employeeList,
        selectedApproverEmail,
        translate,
        policy?.preventSelfApproval,
        membersEmail,
    ]);

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?


  • We can render the App and WorkspaceWorkflowsApprovalsApproverPage and set the onyx data with few fake policies and provide some employees list and dummy approvalWorkflow data.
  • Then we can verify that if policy?.preventSelfApproval is true in a policy, it filters out the selected members (expense from) from approvers list. It can be verified by checking the length of the options or verifying each email which should be there according to the members list value.

What alternative solutions did you explore? (Optional)

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels Dec 5, 2024
@marcaaron marcaaron self-assigned this Dec 5, 2024
@marcaaron
Copy link
Contributor Author

@Krishna2323 looks good, but not ready for proposals just yet.

@Krishna2323
Copy link
Contributor

Hid the proposal 😅. Will add it back when we're ready for proposals.

@JmillsExpensify
Copy link

Ideally, we would just remove this person from the list of approvers when giving the option to select an approver here

I would think for this case, we:

  • Show a centered modal clarifying the workspace is configured with workflows that violate this configuration, and as a result, they will be reset to the workspace owner.
  • Then we potentially highlight the specific workflows that have changed (and if there are several, then we make sure at least one is within the scroll view and is highlighted.

@marcaaron
Copy link
Contributor Author

Ah, so, maybe it is a bit easier to have them set back to whatever the default approver here is?

Screenshot 2024-12-05 at 11 45 52 AM

Or are we wanting to update each custom workflow to the workspace owner with some kind of note?

Either way, I think we may need a backend change since all the affected reports would need to have their managerID set to a new approver (and potentially Onyx updates sent out for this change).

potentially highlight the specific workflows that have changed (and if there are several, then we make sure at least one is within the scroll view and is highlighted

The user updating the prevent self approval setting would be on the "Rules" page. I think it would be easier to update them without having any kind of highlighting, but I might be missing why that's important.

@JmillsExpensify
Copy link

Yeah great call! Let's set them back to the default approver. In that case, also no need for highlighting.

@melvin-bot melvin-bot bot added the Overdue label Dec 9, 2024
@JmillsExpensify
Copy link

@marcaaron do you think we're ready to open up to proposals following our discussion last week?

@melvin-bot melvin-bot bot removed the Overdue label Dec 9, 2024
@Krishna2323
Copy link
Contributor

Show a centered modal clarifying the workspace is configured with workflows that violate this configuration, and as a result, they will be reset to the workspace owner.

@JmillsExpensify, do we want to show the modal or we just want to exclude the members from the approvers list?

@marcaaron
Copy link
Contributor Author

Yeah. Ok, I think for clarity I am going to split this into two separate issues and we will make this one External and only focus on the exclusion of the user from the person selector page.

@JmillsExpensify, do we want to show the modal or we just want to exclude the members from the approvers list?

Let's chat about this in the next issue.

@marcaaron marcaaron changed the title Figure out how Prevent Self Approvals should work in NewDot and implement it Hide current user in approver selector when 'prevent self approvals' is enabled Dec 9, 2024
@marcaaron marcaaron added the External Added to denote the issue can be worked on by a contributor label Dec 9, 2024
@melvin-bot melvin-bot bot changed the title Hide current user in approver selector when 'prevent self approvals' is enabled [$250] Hide current user in approver selector when 'prevent self approvals' is enabled Dec 9, 2024
Copy link

melvin-bot bot commented Dec 9, 2024

Job added to Upwork: https://www.upwork.com/jobs/~021866263734682899164

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Dec 9, 2024
Copy link

melvin-bot bot commented Dec 9, 2024

Triggered auto assignment to Contributor-plus team member for initial proposal review - @brunovjk (External)

@brunovjk
Copy link
Contributor

only focus on the exclusion of the user from the person selector page

With that I think we can go with @Krishna2323's proposal

🎀👀🎀 C+ reviewed

Copy link

melvin-bot bot commented Dec 10, 2024

Current assignee @marcaaron is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.

@flaviadefaria flaviadefaria moved this to First Cohort - MEDIUM or LOW in [#whatsnext] #migrate Dec 10, 2024
@melvin-bot melvin-bot bot removed the Help Wanted Apply this label when an issue is open to proposals by contributors label Dec 11, 2024
Copy link

melvin-bot bot commented Dec 11, 2024

📣 @brunovjk 🎉 An offer has been automatically sent to your Upwork account for the Reviewer role 🎉 Thanks for contributing to the Expensify app!

Offer link
Upwork job

Copy link

melvin-bot bot commented Dec 11, 2024

📣 @Krishna2323 🎉 An offer has been automatically sent to your Upwork account for the Contributor role 🎉 Thanks for contributing to the Expensify app!

Offer link
Upwork job
Please accept the offer and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review 🧑‍💻
Keep in mind: Code of Conduct | Contributing 📖

@Krishna2323
Copy link
Contributor

@brunovjk PR ready for review ^

@brunovjk
Copy link
Contributor

brunovjk commented Dec 20, 2024

I think the automation failed to update the status. The PR was deployed to production on December 18th.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
External Added to denote the issue can be worked on by a contributor NewFeature Something to build that is a new item. Reviewing Has a PR in review Weekly KSv2
Projects
Status: First Cohort - MEDIUM or LOW
Development

No branches or pull requests

6 participants