diff --git a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx index 8031de0d51..c3c846c625 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx @@ -9,7 +9,7 @@ import { fetchRolesWithTarget, } from "../../../../slices/aclSlice"; import { FormikProps } from "formik"; -import { filterRoles, policiesFiltered, rolesFilteredbyPolicies } from "../../../../utils/aclUtils"; +import { policiesFiltered } from "../../../../utils/aclUtils"; import { useAppDispatch, useAppSelector } from "../../../../store"; import { fetchSeriesDetailsAcls } from "../../../../slices/seriesDetailsSlice"; import { getSeriesDetailsAcl } from "../../../../selectors/seriesDetailsSelectors"; @@ -122,7 +122,6 @@ const NewAccessPage = ({ ({ ({ ({ optionHeight = 25, customCSS, fetchOptions, + isAclDropDown = false, }: { ref?: React.RefObject> | null> value: T @@ -66,7 +67,8 @@ const DropDown = ({ optionPaddingTop?: number, optionLineHeight?: string }, - fetchOptions?: (inputValue: string) => Promise<{ label: string, value: string }[]> + fetchOptions?: (inputValue: string) => Promise<{ label: string, value: string }[]>, + isAclDropDown?: boolean; }) => { const { t } = useTranslation(); @@ -182,7 +184,6 @@ const DropDown = ({ callback(formatOptions(filterOptions(_inputValue), required)); }; - const commonProps: Props = { tabIndex: tabIndex, theme: theme => (dropDownSpacingTheme(theme)), @@ -210,6 +211,15 @@ const DropDown = ({ // @ts-expect-error: React-Select typing does not account for the typing of option it itself requires components: { MenuList }, + filterOption: createFilter({ ignoreAccents: false }), // To improve performance on filtering + + isOptionSelected: (option, _selectValue) => { + const typedOption = option as DropDownOption; + if (isAclDropDown) { + return false; + } + return typedOption.value === value; + }, }; return creatable ? ( diff --git a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx index 9988683826..beaac4e138 100644 --- a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx +++ b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx @@ -18,7 +18,7 @@ import { import { getUserInformation } from "../../../selectors/userInfoSelectors"; import { hasAccess } from "../../../utils/utils"; import DropDown from "../DropDown"; -import { filterRoles, getAclTemplateText, handleTemplateChange, policiesFiltered, rolesFilteredbyPolicies } from "../../../utils/aclUtils"; +import { filterRoles, getAclTemplateText, handleTemplateChange, policiesFiltered } from "../../../utils/aclUtils"; import { useAppDispatch, useAppSelector } from "../../../store"; import { removeNotificationWizardForm, addNotification } from "../../../slices/notificationSlice"; import { useTranslation } from "react-i18next"; @@ -311,7 +311,6 @@ const ResourceDetailsAccessPolicyTab = ({ ({ isUserTable, policiesFiltered, - rolesFilteredbyPolicies, header, firstColumnHeader, createLabel, @@ -411,7 +407,6 @@ export const AccessPolicyTable = ({ }: { isUserTable: boolean policiesFiltered: TransformedAcl[] - rolesFilteredbyPolicies: Role[] header?: ParseKeys firstColumnHeader: ParseKeys createLabel: ParseKeys, @@ -538,7 +533,7 @@ export const AccessPolicyTable = ({ text={createPolicyLabel(policy)} options={ roles.length > 0 - ? formatAclRolesForDropdown(rolesFilteredbyPolicies) + ? formatAclRolesForDropdown(filterRoles(roles, formik.values.policies, policy.role)) : [] } required={true} @@ -565,6 +560,7 @@ export const AccessPolicyTable = ({ skipTranslate optionHeight={35} customCSS={{ width: "100%", optionPaddingTop: 5 }} + isAclDropDown /> ) : (

{policy.role}

@@ -794,6 +790,7 @@ export const TemplateSelector = ({ } }} placeholder={t(buttonText)} + isAclDropDown /> )} {!(aclTemplates.length > 0) && diff --git a/src/components/users/partials/wizard/AclAccessPage.tsx b/src/components/users/partials/wizard/AclAccessPage.tsx index 27cdb88f9c..a10ab986ba 100644 --- a/src/components/users/partials/wizard/AclAccessPage.tsx +++ b/src/components/users/partials/wizard/AclAccessPage.tsx @@ -9,7 +9,7 @@ import { fetchAclTemplates, fetchRolesWithTarget, } from "../../../../slices/aclSlice"; -import { filterRoles, policiesFiltered, rolesFilteredbyPolicies } from "../../../../utils/aclUtils"; +import { policiesFiltered } from "../../../../utils/aclUtils"; import { useAppDispatch } from "../../../../store"; import { TransformedAcl } from "../../../../slices/aclDetailsSlice"; import { AccessPolicyTable, TemplateSelector } from "../../../shared/modals/ResourceDetailsAccessPolicyTab"; @@ -88,7 +88,6 @@ const AclAccessPage = ({ ({ ({ { - return roles.filter( - role => !policies.find(policy => policy.role === role.name), - ); +export const filterRoles = (roles: Role[], policies: TransformedAcl[], currentRole?: string) => { + // Collect all roles currently selected in policies + const selectedRoles = policies + .map(p => p.role) + .filter(Boolean); + + return roles.filter(r => + // keep it if it's not already selected + !selectedRoles.includes(r.name) + // OR it is the current role for this dropdown row + || r.name === currentRole, + ); }; // Get all policies that have user information, or all policies that do not have user information @@ -42,20 +50,6 @@ export const policiesFiltered = ( } }; -// Get all roles that have user information, or all policies that do not have user information -export const rolesFilteredbyPolicies = ( -roles: Role[], -policies: TransformedAcl[], -byUser: boolean, -) => { - roles = filterRoles(roles, policies); - if (byUser) { - return roles.filter(role => role.user !== undefined); - } else { - return roles.filter(role => role.user === undefined); - } -}; - /* fetches the policies for the chosen template and sets the policies in the formik form to those policies */ export const handleTemplateChange = async ( templateId: string,