Skip to content

Add People Admin workspace approval access#92875

Open
ShridharGoel wants to merge 19 commits into
Expensify:mainfrom
ShridharGoel:ws1.5
Open

Add People Admin workspace approval access#92875
ShridharGoel wants to merge 19 commits into
Expensify:mainfrom
ShridharGoel:ws1.5

Conversation

@ShridharGoel

@ShridharGoel ShridharGoel commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

This PR adds the frontend permission handling for the People Admin workspace role.

People Admins can access Overview, Members, and Workflows. They can invite and remove members, but cannot assign elevated workspace roles or remove Workspace Admins. In Workflows, they can edit approval workflows while payment-related workflow settings stay restricted.

This also updates the approval workflow RHP pages so they use scoped WORKFLOWS_APPROVALS write access instead of checking for full Workspace Admin access.

Depends on https://github.com/Expensify/Web-Expensify/pull/53610

Fixed Issues

$ #90502, #90503
PROPOSAL:

Tests

  1. Sign in as a Workspace Admin on a control policy.
  2. Go to Workspace > Members.
  3. Change a member role to People Admin.
  4. Sign in as the People Admin.
  5. Verify only Overview, Members, and Workflows are visible in workspace settings.
  6. Go to Members and verify the People Admin can invite members.
  7. Verify the invite role remains Member and cannot be changed to an elevated role.
  8. Verify the People Admin can remove a normal member.
  9. Verify the People Admin cannot remove a Workspace Admin.
  10. Go to Workflows > Approvals.
  11. Create or edit an approval workflow.
  12. Verify the People Admin can open the approver page from the Approver row.
  13. Verify selecting an approver does not show Not Found and returns to the approval limit page.
  14. Verify the People Admin can open the Additional approver page.
  15. Verify selecting an additional approver does not show Not Found and returns to the approval limit page.
  16. Verify payment-related workflow settings remain blocked for the People Admin.
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

Same as tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@melvin-bot

melvin-bot Bot commented Jun 7, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx ts-node ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@codecov

codecov Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/CONST/index.ts 94.02% <ø> (+2.98%) ⬆️
src/components/ApproverSelectionList.tsx 90.90% <100.00%> (+0.21%) ⬆️
src/pages/workspace/DynamicWorkspaceInvitePage.tsx 0.00% <ø> (ø)
src/pages/workspace/MemberRightIcon.tsx 93.75% <100.00%> (ø)
src/pages/workspace/members/ImportMembersPage.tsx 0.00% <ø> (ø)
...rkspace/rules/RulesAutoApproveReportsUnderPage.tsx 0.00% <ø> (ø)
.../approvals/WorkspaceWorkflowsApprovalsEditPage.tsx 74.57% <100.00%> (+0.43%) ⬆️
src/components/WorkspaceMemberRoleList.tsx 0.00% <0.00%> (ø)
src/pages/workspace/WorkspaceMembersPage.tsx 60.00% <93.54%> (+1.97%) ⬆️
...ges/workspace/rules/RulesRandomReportAuditPage.tsx 0.00% <0.00%> (ø)
... and 15 more
... and 229 files with indirect coverage changes

# Conflicts:
#	src/languages/de.ts
#	src/languages/es.ts
#	src/languages/fr.ts
#	src/languages/it.ts
#	src/languages/ja.ts
#	src/languages/nl.ts
#	src/languages/pl.ts
#	src/languages/pt-BR.ts
#	src/languages/zh-hans.ts
#	src/libs/actions/Policy/Member.ts
#	src/pages/workspace/WorkspaceMembersPage.tsx
#	src/pages/workspace/members/ImportedMembersPage.tsx
#	src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
#	src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsEditPage.tsx
#	src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx
@ShridharGoel ShridharGoel marked this pull request as ready for review June 15, 2026 16:50
@ShridharGoel ShridharGoel requested review from a team as code owners June 15, 2026 16:50
@melvin-bot melvin-bot Bot requested review from Pujan92 and heyjennahay June 15, 2026 16:50
@melvin-bot

melvin-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown

@Pujan92 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot melvin-bot Bot removed request for a team June 15, 2026 16:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03a25dbf24

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx
Comment thread src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx Outdated
Comment thread src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx Outdated
Comment thread src/pages/workspace/members/ImportedMembersPage.tsx
Comment thread src/pages/workspace/members/ImportedMembersConfirmationPage.tsx
Comment thread src/pages/workspace/DynamicWorkspaceInviteMessageRolePage.tsx
Comment thread src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx Outdated
@ShridharGoel ShridharGoel changed the title Add People Admin workspace approval access [HOLD] Add People Admin workspace approval access Jun 16, 2026
# Conflicts:
#	src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx
#	src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx
@Pujan92

Pujan92 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-06-30.at.17.52.14.mov

@Pujan92

Pujan92 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot review

@MelvinBot

Copy link
Copy Markdown
Contributor

Review — People Admin workspace approval access

Solid, well-structured permission refactor. The role model is centralized in ROLE_PERMISSION_BUNDLES and the page-level gating consistently derives from it via canMemberWrite / canMemberRead. Bulk-action gating, checkbox disabling, and the new canMemberManageRole / canMemberManageMemberWithRole helpers all line up correctly (People Admin can only manage user/auditor, never elevate). CI is green (typecheck, ESLint, tests), and the added unit + UI tests cover the core People Admin paths. No blocking issues found.

Notes (non-blocking)

1. The Codex bot comment on WorkspaceMemberDetailsPage is stale/resolved. It claims the wrapper "relies on the default policyFeatureAccess of read," but the current code explicitly passes WRITE at WorkspaceMemberDetailsPage.tsx:278-279, which routes to canMemberWrite(policy, login, MEMBERS) in AccessOrNotFoundWrapper.tsx:204-206. A read-only member can't deep-link past this guard. No change needed.

2. Confirm the submission-frequency visibility asymmetry is intended. shouldShowSubmissionFrequency = canWriteWorkflows || !canWriteApprovals hides the submission-frequency row for a People Admin (workflows=read, approvals=write → false || !true = false), yet a plain auditor/read-only viewer (false || true = true) still sees it read-only. So a People Admin — who has more access — sees fewer rows. Reasonable given their scope is approvals-only, but worth a deliberate confirmation.

3. Minor DRY (flagged by the consistency bot). Several files repeat currentUserPersonalDetails.login ?? '' inline (e.g. WorkspaceMembersPage, WorkspaceInviteMessageComponent). Most new files already destructure {login: currentUserLogin = ''} — applying that uniformly would clean these up. Optional.

Reminder given the [HOLD]

This is frontend-only gating; real enforcement lives in the dependency Web-Expensify#53610. Before un-holding, double-check the backend role permissions match ROLE_PERMISSION_BUNDLES exactly — specifically People Admin = members:write, workflowsApprovals:write, workflows:read, and no assignElevatedRoles. Any divergence would let the UI and backend disagree on what a People Admin can do.

Comment thread src/libs/PolicyUtils.ts
Comment thread src/pages/workspace/WorkspaceMembersPage.tsx Outdated
Comment thread src/libs/PolicyUtils.ts
Comment on lines +215 to +221
return (
policy?.type === CONST.POLICY.TYPE.CORPORATE &&
canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.MEMBERS) &&
getPolicyRole(policy, login) === CONST.POLICY.ROLE.PEOPLE_ADMIN &&
(role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR)
);
}

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.

Suggested change
return (
policy?.type === CONST.POLICY.TYPE.CORPORATE &&
canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.MEMBERS) &&
getPolicyRole(policy, login) === CONST.POLICY.ROLE.PEOPLE_ADMIN &&
(role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR)
);
}
// a People Admin who manages members can only assign the User or Auditor role
return (
getPolicyRole(policy, login) === CONST.POLICY.ROLE.PEOPLE_ADMIN &&
(role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR)
);
}

Will this work?

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.

It removes the corporate check and canMemberWrite(...MEMBERS) guard. That is needed because PEOPLE_ADMIN is a control-policy-only role, and canMemberWrite ensures that is followed through hasPolicyFeaturePermission

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.

Ok, but getPolicyRole(policy, login) === CONST.POLICY.ROLE.PEOPLE_ADMIN isn't itself enough to derive that it has access here. Bcoz of that, I thought we don't need canMemberWrite check here.

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.

Actually canMemberWrite also has control-policy-only role validation and any future common changes will be added to this method. So, don't you think we should keep it along with getPolicyRole(policy, login) === CONST.POLICY.ROLE.PEOPLE_ADMIN ?

Comment thread src/libs/PolicyUtils.ts Outdated
# Conflicts:
#	src/pages/workspace/WorkspaceMembersPage.tsx
#	src/pages/workspace/members/ImportedMembersPage.tsx
@ShridharGoel ShridharGoel changed the title [HOLD] Add People Admin workspace approval access Add People Admin workspace approval access Jun 18, 2026
# Conflicts:
#	src/pages/workspace/WorkspaceMembersPage.tsx
#	src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx
@Pujan92

Pujan92 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@ShridharGoel let's fix the conflicts

- Languages: keep both peopleAdmins and members keys
- WorkspaceMembersTable: add People Admins role filter, rename selection prop to canSelectMembers
- WorkspaceMembersPage: port People Admin permission gating and Make People Admin bulk action onto the Table-based page
- WorkspaceWorkflowsPage: gate submission frequency and payments sections by People Admin access
@ShridharGoel

Copy link
Copy Markdown
Contributor Author

@Pujan92 Updated

@Pujan92 Pujan92 left a comment

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.

LGTM!

@melvin-bot melvin-bot Bot requested a review from flodnv June 30, 2026 12:26
@Pujan92

Pujan92 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot review

@Pujan92

This comment was marked as outdated.

Comment thread src/libs/PolicyUtils.ts Outdated
Comment thread src/components/WorkspaceMemberRoleList.tsx Outdated
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]}
policyFeature={CONST.POLICY.POLICY_FEATURE.MEMBERS}
policyFeatureAccess={CONST.POLICY.POLICY_FEATURE_ACCESS.WRITE}
shouldBeBlocked={!canMemberAssignRole(policy, currentUserLogin, CONST.POLICY.ROLE.AUDITOR)}

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.

Why are we passing AUDITOR here?

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.

This means "can this user assign the auditor role to someone else." That capability is true only for Admins and People Admins on a Control policy.

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.

But replaced these canMemberAssignRole(..., AUDITOR) gates with canMemberAssignElevatedRole(policy, login)

Comment thread src/pages/workspace/members/ImportedMembersConfirmationPage.tsx Outdated
Comment thread src/pages/workspace/WorkspaceMembersPage.tsx Outdated
- canMemberAssignRole: drop the hardcoded PEOPLE_ADMIN check (members:write covers it, works for future custom roles) and reject control-only roles on non-control policies
- WorkspaceMemberRoleList: rely on canMemberAssignRole instead of per-role isControlPolicy checks
- Rename isPolicyAdmin -> canEditWorkspaceSettings in WorkspaceMembersPage to match what the value represents
- Add canMemberAssignElevatedRole helper and use it for the elevated-role gates in the invite/import flows so Collect admins keep access
…canMemberAssignRole

isControlPolicy expects OnyxEntry<Policy> but canMemberAssignRole receives the wider OnyxInputOrEntry<Policy>. Inline the type check to match the existing CORPORATE check in the same function.
@ShridharGoel ShridharGoel requested a review from flodnv June 30, 2026 15:37
The page now renders the Table component (from main) instead of SelectionList, so the old selection-button testID no longer exists. Use selectCheckboxByMemberName like the other Table-based tests.
Comment thread src/libs/PolicyUtils.ts
Comment on lines +207 to +225
function canMemberAssignRole(policy: OnyxInputOrEntry<Policy>, login: string, role: string | undefined): boolean {
if (!role) {
return false;
}

if (isControlPolicyOnlyRole(role) && policy?.type !== CONST.POLICY.TYPE.CORPORATE) {
return false;
}

if (canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.ASSIGN_ELEVATED_ROLES)) {
return true;
}

return (
policy?.type === CONST.POLICY.TYPE.CORPORATE &&
canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.MEMBERS) &&
(role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR)
);
}

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.

Following this logic is not that easy... how about this? Is it better?

Suggested change
function canMemberAssignRole(policy: OnyxInputOrEntry<Policy>, login: string, role: string | undefined): boolean {
if (!role) {
return false;
}
if (isControlPolicyOnlyRole(role) && policy?.type !== CONST.POLICY.TYPE.CORPORATE) {
return false;
}
if (canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.ASSIGN_ELEVATED_ROLES)) {
return true;
}
return (
policy?.type === CONST.POLICY.TYPE.CORPORATE &&
canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.MEMBERS) &&
(role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR)
);
}
function canMemberAssignRole(policy: OnyxInputOrEntry<Policy>, login: string, role: string | undefined): boolean {
if (!role) {
return false;
}
const isCorporatePolicy = policy?.type === CONST.POLICY.TYPE.CORPORATE;
if (isControlPolicyOnlyRole(role) && !isCorporatePolicy) {
return false;
}
if (canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.ASSIGN_ELEVATED_ROLES)) {
return true;
}
// Non-control-only roles (USER, AUDITOR) can reach this point on non-corporate policies,
// but only corporate policies allow assigning them via the MEMBERS permission.
const isNonElevatedRole = role === CONST.POLICY.ROLE.USER || role === CONST.POLICY.ROLE.AUDITOR;
return isCorporatePolicy && canMemberWrite(policy, login, CONST.POLICY.POLICY_FEATURE.MEMBERS) && isNonElevatedRole;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants