From fe59adf2b0ba13ce143c138aa65a8a89d70ef265 Mon Sep 17 00:00:00 2001 From: Ryan Berger Date: Fri, 24 Jan 2025 15:32:57 -0500 Subject: [PATCH] Add ability to add/remove roles for user's affiliations --- CHANGELOG.md | 1 + .../EditUserRoles/EditUserRoles.js | 41 ++++++++++++++++--- src/components/Wrappers/withUserRoles.js | 9 ++-- src/hooks/useAllRolesData/useAllRolesData.js | 9 ++-- src/views/UserEdit/UserEdit.js | 4 ++ src/views/UserEdit/UserForm.js | 3 ++ 6 files changed, 55 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ee6b50b2..3715c298e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Update fee/fine actions column UX for accessibility. Refs UIU-3027. * Rename permission after BE changes. Refs UIU-3309. * Change import of `exportToCsv` from `stripes-util` to `stripes-components`. Refs UIU-3202. +* Add ability to create/edit role assignments for all of a user's affiliations. Refs UIU-3179. ## [11.0.10](https://github.com/folio-org/ui-users/tree/v11.0.10) (2025-01-10) [Full Changelog](https://github.com/folio-org/ui-users/compare/v11.0.9...v11.0.10) diff --git a/src/components/EditSections/EditUserRoles/EditUserRoles.js b/src/components/EditSections/EditUserRoles/EditUserRoles.js index 9ad7a544b..cb2cda71c 100644 --- a/src/components/EditSections/EditUserRoles/EditUserRoles.js +++ b/src/components/EditSections/EditUserRoles/EditUserRoles.js @@ -1,22 +1,41 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useIntl, FormattedMessage } from 'react-intl'; import { withRouter } from 'react-router'; import PropTypes from 'prop-types'; import { isEmpty } from 'lodash'; import { FieldArray } from 'react-final-form-arrays'; import { OnChange } from 'react-final-form-listeners'; -import { IfPermission } from '@folio/stripes/core'; + +import { IfPermission, useStripes } from '@folio/stripes/core'; import { Accordion, Headline, Badge, Row, Col, List, Button, Icon, ConfirmationModal } from '@folio/stripes/components'; -import { useAllRolesData } from '../../../hooks'; + +import { useAllRolesData, useUserAffiliations } from '../../../hooks'; +import AffiliationsSelect from '../../AffiliationsSelect/AffiliationsSelect'; +import IfConsortium from '../../IfConsortium'; +import IfConsortiumPermission from '../../IfConsortiumPermission'; import UserRolesModal from './components/UserRolesModal/UserRolesModal'; +import { isAffiliationsEnabled } from '../../util/util'; import { filtersConfig } from './helpers'; -function EditUserRoles({ accordionId, form:{ change }, setAssignedRoleIds, assignedRoleIds }) { +function EditUserRoles({ accordionId, form:{ change }, user, setAssignedRoleIds, assignedRoleIds, setTenantId, tenantId }) { + const stripes = useStripes(); const [isOpen, setIsOpen] = useState(false); const [unassignModalOpen, setUnassignModalOpen] = useState(false); const intl = useIntl(); - const { isLoading: isAllRolesDataLoading, allRolesMapStructure } = useAllRolesData(); + const { + affiliations, + isFetching: isAffiliationsFetching, + } = useUserAffiliations({ userId: user.id }, { enabled: isAffiliationsEnabled(user) }); + + const { isLoading: isAllRolesDataLoading, allRolesMapStructure, refetch } = useAllRolesData({ tenantId }); + + useEffect(() => { + if (!affiliations.some(({ tenantId: assigned }) => tenantId === assigned)) { + setTenantId(stripes.okapi.tenant); + } + refetch(); + }, [affiliations, stripes.okapi.tenant, tenantId]); const changeUserRoles = (roleIds) => { change('assignedRoleIds', roleIds); @@ -101,6 +120,18 @@ function EditUserRoles({ accordionId, form:{ change }, setAssignedRoleIds, assig displayWhenClosed={{assignedRoleIds.length}} > + + + {Boolean(affiliations?.length) && ( + + )} + + {renderUserRoles()} diff --git a/src/components/Wrappers/withUserRoles.js b/src/components/Wrappers/withUserRoles.js index a25317728..9f6693b42 100644 --- a/src/components/Wrappers/withUserRoles.js +++ b/src/components/Wrappers/withUserRoles.js @@ -10,13 +10,14 @@ const withUserRoles = (WrappedComponent) => (props) => { const { okapi, config } = useStripes(); // eslint-disable-next-line react/prop-types const userId = props.match.params.id; + const [tenantId, setTenantId] = useState(okapi.tenant); const [assignedRoleIds, setAssignedRoleIds] = useState([]); const [initialAssignedRoleIds, setInitialAssignedRoleIds] = useState([]); const [isCreateKeycloakUserConfirmationOpen, setIsCreateKeycloakUserConfirmationOpen] = useState(false); const callout = useCallout(); const sendErrorCallout = error => showErrorCallout(error, callout.sendCallout); - const { mutateAsync: createKeycloakUser } = useCreateAuthUserKeycloak(sendErrorCallout, { tenantId: okapi.tenant }); + const { mutateAsync: createKeycloakUser } = useCreateAuthUserKeycloak(sendErrorCallout, { tenantId }); const { isLoading: isAllRolesDataLoading, allRolesMapStructure } = useAllRolesData(); @@ -28,7 +29,7 @@ const withUserRoles = (WrappedComponent) => (props) => { const ky = useOkapiKy(); const api = ky.extend({ hooks: { - beforeRequest: [(req) => req.headers.set('X-Okapi-Tenant', okapi.tenant)] + beforeRequest: [(req) => req.headers.set('X-Okapi-Tenant', tenantId)] } }); @@ -57,7 +58,7 @@ const withUserRoles = (WrappedComponent) => (props) => { }, // Adding api, searchParams to deps causes infinite callback call. Listed deps are enough to track changes. // eslint-disable-next-line react-hooks/exhaustive-deps - [userId, isAllRolesDataLoading, setAssignedRoleIdsOnLoad]); + [userId, isAllRolesDataLoading, setAssignedRoleIdsOnLoad, tenantId]); const updateUserRoles = (roleIds) => api.put( `roles/users/${userId}`, { json: { @@ -116,6 +117,8 @@ const withUserRoles = (WrappedComponent) => (props) => { return { + const { data, isLoading, isSuccess, refetch } = useQuery([namespace, 'user-roles'], () => { return ky.get(`roles?limit=${stripes.config.maxUnpagedResourceCount}&query=cql.allRecords=1 sortby name`).json(); }, { enabled: stripes.hasInterface('roles') }); @@ -32,7 +33,7 @@ function useAllRolesData() { return rolesMap; }, [data]); - return { data, isLoading, allRolesMapStructure, isSuccess }; + return { data, isLoading, allRolesMapStructure, isSuccess, refetch }; } export default useAllRolesData; diff --git a/src/views/UserEdit/UserEdit.js b/src/views/UserEdit/UserEdit.js index 72dde2f43..63ae4ba32 100644 --- a/src/views/UserEdit/UserEdit.js +++ b/src/views/UserEdit/UserEdit.js @@ -459,6 +459,8 @@ class UserEdit extends React.Component { location, match: { params }, isCreateKeycloakUserConfirmationOpen, + setTenantId, + tenantId, setAssignedRoleIds, assignedRoleIds } = this.props; @@ -497,6 +499,8 @@ class UserEdit extends React.Component { isCreateKeycloakUserConfirmationOpen={isCreateKeycloakUserConfirmationOpen} onCancelKeycloakConfirmation={this.onCompleteEdit} confirmCreateKeycloakUser={() => this.props.confirmCreateKeycloakUser(this.onCompleteEdit)} + setTenantId={setTenantId} + tenantId={tenantId} setAssignedRoleIds={setAssignedRoleIds} assignedRoleIds={assignedRoleIds} /> diff --git a/src/views/UserEdit/UserForm.js b/src/views/UserEdit/UserForm.js index 5cb0118e2..e58607ce7 100644 --- a/src/views/UserEdit/UserForm.js +++ b/src/views/UserEdit/UserForm.js @@ -472,6 +472,9 @@ class UserForm extends React.Component { setButtonRef={this.setButtonRef} /> :