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}
/> :