From 692a607e804c1153f88fb142d62d8743cf6a6c4b Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Mon, 18 Sep 2023 18:19:24 +0500 Subject: [PATCH 01/14] UIU-2942: Assign/unassign a users affiliations adjustments --- CHANGELOG.md | 1 + .../AffiliationsManagerModal.js | 8 +++- .../useAffiliationsAssignment.js | 6 ++- src/views/UserDetail/UserDetail.js | 44 +++++++++---------- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6de427b..01212fc94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ * Add dropdown to specify user type: Patron or Staff. Refs UIU-2936. * *BREAKING* bump `react-intl` to `v6.4.4`. Refs UIU-2946. * Generate "Create request" url for users without barcode. Refs UIU-2869. +* Assign/unassign a users affiliations adjustments. Refs UIU-2942. ## [9.0.0](https://github.com/folio-org/ui-users/tree/v9.0.0) (2023-02-20) [Full Changelog](https://github.com/folio-org/ui-users/compare/v8.1.0...v9.0.0) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index e35fc1a44..8cb9be4fc 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -115,12 +115,16 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { orderBy( filtersConfig .reduce((filtered, config) => config.filter(filtered, activeFilters, assignment), tenants) - .filter(({ name }) => (searchQuery ? name.toLowerCase().includes(searchQuery.toLowerCase()) : true)), + .filter(({ name, isCentral, id }) => { + if (isCentral || !affiliationIds.includes(id)) return false; + + return (searchQuery ? name.toLowerCase().includes(searchQuery.toLowerCase()) : true); + }), sorters[sortOrder], sortDirection.name, ) ); - }, [assignment, filters, sortDirection.name, sortOrder, sorters, tenants]); + }, [affiliationIds, assignment, filters, sortDirection.name, sortOrder, sorters, tenants]); return ( { setAssignment(() => { const affiliationsMap = keyBy(affiliations, 'tenantId'); - return tenants.reduce((acc, { id }) => { - acc[id] = Boolean(affiliationsMap[id]); + return tenants.reduce((acc, { id, isCentral }) => { + if (!isCentral) { + acc[id] = Boolean(affiliationsMap[id]); + } return acc; }, {}); diff --git a/src/views/UserDetail/UserDetail.js b/src/views/UserDetail/UserDetail.js index d28be72b5..f547abf62 100644 --- a/src/views/UserDetail/UserDetail.js +++ b/src/views/UserDetail/UserDetail.js @@ -50,29 +50,22 @@ import { UserAffiliations, UserServicePoints, } from '../../components/UserDetailSections'; - import HelperApp from '../../components/HelperApp'; import IfConsortium from '../../components/IfConsortium'; -import { - PatronBlockMessage -} from '../../components/PatronBlock'; -import { - getFormAddressList, -} from '../../components/data/converters/address'; -import { - getFullName, -} from '../../components/util'; +import { PatronBlockMessage } from '../../components/PatronBlock'; +import { getFormAddressList } from '../../components/data/converters/address'; +import { getFullName } from '../../components/util'; import RequestFeeFineBlockButtons from '../../components/RequestFeeFineBlockButtons'; import { departmentsShape } from '../../shapes'; - -import OpenTransactionModal from './components/OpenTransactionModal'; -import DeleteUserModal from './components/DeleteUserModal'; -import ExportFeesFinesReportButton from './components'; import ErrorPane from '../../components/ErrorPane'; -import ActionMenuEditButton from './components/ActionMenuEditButton'; -import ActionMenuDeleteButton from './components/ActionMenuDeleteButton'; import LostItemsLink from '../../components/LostItemsLink'; import IfConsortiumPermission from '../../components/IfConsortiumPermission'; +import { USER_TYPES } from '../../constants'; +import ActionMenuEditButton from './components/ActionMenuEditButton'; +import ActionMenuDeleteButton from './components/ActionMenuDeleteButton'; +import OpenTransactionModal from './components/OpenTransactionModal'; +import DeleteUserModal from './components/DeleteUserModal'; +import ExportFeesFinesReportButton from './components'; class UserDetail extends React.Component { static propTypes = { @@ -621,6 +614,7 @@ class UserDetail extends React.Component { const userDepartments = (user?.departments || []) .map(departmentId => departments.find(({ id }) => id === departmentId)?.name); const accounts = resources?.accounts; + const isAffiliationEnabled = user?.type !== USER_TYPES.PATRON; if (this.userNotFound()) { return ( @@ -702,13 +696,17 @@ class UserDetail extends React.Component { - + { + isAffiliationEnabled && ( + + ) + } From e6e4da76b23f176b65689aa02110632fb8afc8ff Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 19 Sep 2023 22:55:22 +0500 Subject: [PATCH 02/14] UIU-2943: ECS - Filter users by "User Type" (#2555) * UIU-2943: ECS - Filter users by "User Type" * tests: add tests * update: revert UIU-2933 manually * refactor: fix naming issues and minor improvements * tests: fix test labeling issue * tests: improve test cases --- CHANGELOG.md | 1 + src/components/util/util.js | 9 +++- src/components/util/util.test.js | 38 +++++++++++++++ src/routes/UserSearchContainer.js | 15 ++---- src/routes/UserSearchContainer.test.js | 20 +++++--- src/routes/filterConfig.js | 6 +++ src/views/UserSearch/Filters.js | 65 ++++++++++++++++++++----- src/views/UserSearch/Filters.test.js | 19 ++++++++ src/views/UserSearch/UserSearch.js | 13 ++++- src/views/UserSearch/UserSearch.test.js | 52 ++++++++++++++++++++ translations/ui-users/en.json | 1 + 11 files changed, 207 insertions(+), 32 deletions(-) create mode 100644 src/views/UserSearch/UserSearch.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5a51fa3..c06ad9b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ * *BREAKING* bump `react-intl` to `v6.4.4`. Refs UIU-2946. * Generate "Create request" url for users without barcode. Refs UIU-2869. * Add auto focus to textarea on staff and patron info modal. Fixes UIU-2932. +* ECS - Filter users by "User Type". Refs UIU-2943. * Assign/unassign a users affiliations adjustments. Refs UIU-2942. ## [9.0.0](https://github.com/folio-org/ui-users/tree/v9.0.0) (2023-02-20) diff --git a/src/components/util/util.js b/src/components/util/util.js index 52d58b736..53a4da7ba 100644 --- a/src/components/util/util.js +++ b/src/components/util/util.js @@ -2,6 +2,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { every } from 'lodash'; import queryString from 'query-string'; + import { NoValue } from '@folio/stripes/components'; import { @@ -176,11 +177,15 @@ export function checkUserActive(user) { export const getContributors = (account, instance) => { const contributors = account?.contributors || instance?.contributors; - return contributors && contributors.map(({ name }) => name); + return contributors?.map(({ name }) => name); }; export const isConsortiumEnabled = stripes => { - return stripes.hasInterface('consortia'); + return stripes?.hasInterface('consortia'); +}; + +export const getCentralTenantId = stripes => { + return stripes?.user?.user?.consortium?.centralTenantId; }; export const getRequestUrl = (barcode, userId) => { diff --git a/src/components/util/util.test.js b/src/components/util/util.test.js index 1279351ab..8418dd7c5 100644 --- a/src/components/util/util.test.js +++ b/src/components/util/util.test.js @@ -18,9 +18,23 @@ import { retrieveNoteReferredEntityDataFromLocationState, getClosedRequestStatusesFilterString, getOpenRequestStatusesFilterString, + getCentralTenantId, + isConsortiumEnabled, getRequestUrl, } from './util'; +const STRIPES = { + hasPerm: jest.fn().mockReturnValue(true), + hasInterface: jest.fn().mockReturnValue(true), + user: { + user: { + consortium: { + centralTenantId: 'centralTenantId' + } + } + } +}; + describe('accountsMatchStatus', () => { it('returns true if all accounts match', () => { const status = 'monkey'; @@ -366,6 +380,30 @@ describe('getContributors', () => { }); }); +describe('isConsortiumEnabled', () => { + it('should return false', () => { + const data = isConsortiumEnabled(); + expect(data).toBeFalsy(); + }); + + it('should return true', () => { + const data = isConsortiumEnabled(STRIPES); + expect(data).toBe(true); + }); +}); + +describe('getCentralTenantId ', () => { + it('should return undefined if consortium object is absent', () => { + const data = getCentralTenantId({ ...STRIPES, user: { user: { } } }); + expect(data).toBe(undefined); + }); + + it('should return centralTenantId if consortium object and id is present', () => { + const data = getCentralTenantId(STRIPES); + expect(data).toBe(STRIPES.user.user.consortium.centralTenantId); + }); +}); + describe('getRequestUrl', () => { it('should return url with user barcode', () => { const userBarcode = 'userBarcode'; diff --git a/src/routes/UserSearchContainer.js b/src/routes/UserSearchContainer.js index 6752e829a..dd7f5648f 100644 --- a/src/routes/UserSearchContainer.js +++ b/src/routes/UserSearchContainer.js @@ -4,27 +4,22 @@ import { get, template, } from 'lodash'; -import { stripesConnect } from '@folio/stripes/core'; +import { stripesConnect } from '@folio/stripes/core'; import { makeQueryFunction, StripesConnectedSource, buildUrl, } from '@folio/stripes/smart-components'; -import filterConfig from './filterConfig'; import { UserSearch } from '../views'; -import { - MAX_RECORDS, - USER_TYPES, -} from '../constants'; +import { MAX_RECORDS } from '../constants'; +import filterConfig from './filterConfig'; import { buildFilterConfig } from './utils'; const INITIAL_RESULT_COUNT = 30; const RESULT_COUNT_INCREMENT = 30; -export const NOT_SHADOW_USER_CQL = `((cql.allRecords=1 NOT type ="") or type<>"${USER_TYPES.SHADOW}")`; - const searchFields = [ 'username="%{query}*"', 'personal.firstName="%{query}*"', @@ -42,7 +37,7 @@ const compileQuery = template(`(${searchFields.join(' or ')})`, { interpolate: / export function buildQuery(queryParams, pathComponents, resourceData, logger, props) { const customFilterConfig = buildFilterConfig(queryParams.filters); - const mainQuery = makeQueryFunction( + return makeQueryFunction( 'cql.allRecords=1', // TODO: Refactor/remove this after work on FOLIO-2066 and RMB-385 is done (parsedQuery, _, localProps) => localProps.query.query.trim().replace('*', '').split(/\s+/) @@ -59,8 +54,6 @@ export function buildQuery(queryParams, pathComponents, resourceData, logger, pr [...filterConfig, ...customFilterConfig], 2, )(queryParams, pathComponents, resourceData, logger, props); - - return mainQuery && `${NOT_SHADOW_USER_CQL} and ${mainQuery}`; } class UserSearchContainer extends React.Component { diff --git a/src/routes/UserSearchContainer.test.js b/src/routes/UserSearchContainer.test.js index 5dfa0bed1..bfe40c9ee 100644 --- a/src/routes/UserSearchContainer.test.js +++ b/src/routes/UserSearchContainer.test.js @@ -1,7 +1,4 @@ -import { - buildQuery, - NOT_SHADOW_USER_CQL, -} from './UserSearchContainer'; +import { buildQuery } from './UserSearchContainer'; const queryParams = { filters: 'active.active', @@ -15,9 +12,20 @@ const resourceData = { const logger = { log: jest.fn(), }; +const mockHasInterface = jest.fn().mockReturnValue(false); +const props = { + stripes: { + hasInterface: mockHasInterface, + } +}; describe('buildQuery', () => { - it('should exclude shadow users when building CQL query', () => { - expect(buildQuery(queryParams, pathComponents, resourceData, logger)).toEqual(expect.stringContaining(NOT_SHADOW_USER_CQL)); + it('should return empty CQL query', () => { + expect(buildQuery({}, pathComponents, { query: {} }, logger, props)).toBeFalsy(); + }); + + it('should include username when building CQL query', () => { + mockHasInterface.mockReturnValue(true); + expect(buildQuery(queryParams, pathComponents, resourceData, logger, props)).toEqual(expect.stringContaining('username="Joe*"')); }); }); diff --git a/src/routes/filterConfig.js b/src/routes/filterConfig.js index 3b7b23f15..f178cf0d6 100644 --- a/src/routes/filterConfig.js +++ b/src/routes/filterConfig.js @@ -24,6 +24,12 @@ const filterConfig = [ values: [], operator: '=', }, + { + name: 'userType', + cql: 'type', + values: [], + operator: '=', + } ]; export default filterConfig; diff --git a/src/views/UserSearch/Filters.js b/src/views/UserSearch/Filters.js index 98807e825..e4e0a51d5 100644 --- a/src/views/UserSearch/Filters.js +++ b/src/views/UserSearch/Filters.js @@ -4,24 +4,24 @@ import { FormattedMessage, injectIntl, } from 'react-intl'; -import { - get, -} from 'lodash'; +import { get } from 'lodash'; import { Accordion, AccordionSet, FilterAccordionHeader, } from '@folio/stripes/components'; - +import { stripesConnect } from '@folio/stripes/core'; import { CheckboxFilter, MultiSelectionFilter, } from '@folio/stripes/smart-components'; -import { statusFilter } from '../../constants'; - import CustomFieldsFilters from '../../components/CustomFieldsFilters'; +import { isConsortiumEnabled } from '../../components/util'; +import { USER_TYPES, statusFilter } from '../../constants'; + +const ACCORDION_ID_PREFIX = 'users-filter-accordion'; class Filters extends React.Component { static propTypes = { @@ -34,6 +34,7 @@ class Filters extends React.Component { resultOffset: PropTypes.shape({ replace: PropTypes.func.isRequired, }), + stripes: PropTypes.object.isRequired, }; static defaultProps = { @@ -85,21 +86,44 @@ class Filters extends React.Component { onChangeHandlers: { clearGroup }, intl: { formatMessage }, resources, + stripes, } = this.props; const { active = [], pg = [], tags = [], departments = [], + userType, } = activeFilters; const departmentsAreNotEmpty = !!resources.departments?.records?.length; + const isConsortium = isConsortiumEnabled(stripes); + const { PATRON, SHADOW, STAFF, SYSTEM } = USER_TYPES; + const userTypeOptions = [ + { + value: PATRON, + label: formatMessage({ id: 'ui-users.information.userType.patron' }), + }, + { + value: STAFF, + label: formatMessage({ id: 'ui-users.information.userType.staff' }), + }, + { + value: SHADOW, + label: formatMessage({ id: 'ui-users.information.userType.shadow' }), + }, + { + value: SYSTEM, + label: formatMessage({ id: 'ui-users.information.userType.system' }), + } + ]; + return ( + { + isConsortium && ( + clearGroup('userType')} + > + + + ) + } { useCustomFields: jest.fn(() => [[customField]]), }; }); +jest.mock('../../components/util', () => ({ + isConsortiumEnabled: jest.fn(), +})); const stateMock = jest.fn(); const filterHandlers = { @@ -43,6 +47,9 @@ const initialProps = { resultOffset: { replace: jest.fn(), }, + stripes: { + hasInterface: jest.fn(), + }, }; describe('Filters', () => { @@ -77,4 +84,16 @@ describe('Filters', () => { renderFilters(initialProps); expect(screen.getByText('ui-users.departments')).toBeInTheDocument(); }); + + it('should display user-type filter for consortia tenants', () => { + isConsortiumEnabled.mockReturnValue(true); + renderFilters(initialProps); + expect(screen.getByText('ui-users.userType')).toBeInTheDocument(); + }); + + it('should hide user-types filter for non-consortia tenants', () => { + isConsortiumEnabled.mockReturnValue(false); + renderFilters(initialProps); + expect(screen.queryByText('ui-users.userType')).not.toBeInTheDocument(); + }); }); diff --git a/src/views/UserSearch/UserSearch.js b/src/views/UserSearch/UserSearch.js index 11114789f..0fd166341 100644 --- a/src/views/UserSearch/UserSearch.js +++ b/src/views/UserSearch/UserSearch.js @@ -39,14 +39,15 @@ import { import RefundsReportModal from '../../components/ReportModals/RefundsReportModal'; import CashDrawerReportModal from '../../components/ReportModals/CashDrawerReportModal'; import FinancialTransactionsReportModal from '../../components/ReportModals/FinancialTransactionsReportModal'; - import CsvReport from '../../components/data/reports'; import RefundsReport from '../../components/data/reports/RefundReport'; import CashDrawerReconciliationReportPDF from '../../components/data/reports/cashDrawerReconciliationReportPDF'; import CashDrawerReconciliationReportCSV from '../../components/data/reports/cashDrawerReconciliationReportCSV'; import FinancialTransactionsReport from '../../components/data/reports/FinancialTransactionsReport'; -import Filters from './Filters'; import LostItemsLink from '../../components/LostItemsLink'; +import { getCentralTenantId, isConsortiumEnabled } from '../../components/util'; +import { USER_TYPES } from '../../constants'; +import Filters from './Filters'; import css from './UserSearch.css'; @@ -651,6 +652,7 @@ class UserSearch extends React.Component { onNeedMoreData, resources, contentRef, + stripes, mutator: { resultOffset, cashDrawerReportSources }, stripes: { timezone }, okapi: { currentUser }, @@ -717,6 +719,12 @@ class UserSearch extends React.Component { email: user => get(user, ['personal', 'email']), }; + const { PATRON, SHADOW, STAFF, SYSTEM } = USER_TYPES; + const isCentralTenant = getCentralTenantId(stripes); + const isConsortium = isConsortiumEnabled(stripes); + const defaultSelectedUserTypes = isCentralTenant ? [STAFF, SHADOW, SYSTEM] : [STAFF, PATRON, SYSTEM]; + const initialFilters = isConsortium ? { userType: defaultSelectedUserTypes } : {}; + return (
@@ -726,6 +734,7 @@ class UserSearch extends React.Component { onComponentWillUnmount={onComponentWillUnmount} initialSearch={initialSearch} initialSearchState={{ query: '' }} + initialFilterState={initialFilters} > { ({ diff --git a/src/views/UserSearch/UserSearch.test.js b/src/views/UserSearch/UserSearch.test.js new file mode 100644 index 000000000..ecedd82ac --- /dev/null +++ b/src/views/UserSearch/UserSearch.test.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { screen } from '@folio/jest-config-stripes/testing-library/react'; + +import renderWithRouter from 'helpers/renderWithRouter'; +import '../../../test/jest/__mock__/matchMedia.mock'; + +import UserSearch from './UserSearch'; + +jest.unmock('@folio/stripes/components'); +jest.unmock('@folio/stripes/smart-components'); + +jest.mock('@folio/stripes/smart-components', () => { + // eslint-disable-next-line global-require + const customField = require('fixtures/multiSelectCustomField'); + return { + // eslint-disable-next-line global-require + ...jest.requireActual('@folio/stripes/smart-components'), + useCustomFields: jest.fn(() => [[customField]]), + }; +}); + +const defaultProps = { + mutator: {}, + resources: { + owners: { + records: [], + }, + }, + stripes: { + hasInterface: jest.fn(), + }, + location: {}, + history: {}, + match: {}, + intl: { + formatMessage: jest.fn(), + }, + okapi: { + currentUser: { + id: 'test', + }, + } +}; + +const renderComponent = (props) => renderWithRouter(); + +describe('UserSearch', () => { + it('should render component', () => { + renderComponent(); + expect(screen.getByText('ui-users.status')).toBeTruthy(); + }); +}); diff --git a/translations/ui-users/en.json b/translations/ui-users/en.json index 9104eb377..4edec5d76 100644 --- a/translations/ui-users/en.json +++ b/translations/ui-users/en.json @@ -402,6 +402,7 @@ "bulkActions.tooltip": "Multiple loans can be processed at the same time. Please select loans first.", "loanNotRenewed": "Loan not renewed", "status": "Status", + "userType": "User type", "selectColumns": "Select Columns", "accounts.title.feeFine": "Fees/fines", "accounts.header": "Fee/fine details - {userName} ({patronGroup})", From a3c57b3d5b34b02389b424713d40d7811657a256 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 20 Sep 2023 17:54:30 +0500 Subject: [PATCH 03/14] hide primary assigned affiliations --- .../AffiliationsManagerModal/AffiliationsManagerModal.js | 4 +++- src/hooks/useUserAffiliations/useUserAffiliations.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index 8cb9be4fc..b823e99b0 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -73,7 +73,9 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const isLoading = isConsortiumTenantsLoading || isUsersAffiliationsLoading; - const affiliationIds = useMemo(() => affiliations.map(({ tenantId }) => tenantId), [affiliations]); + const affiliationIds = useMemo(() => { + return affiliations.filter(({ isPrimary }) => !isPrimary).map(({ tenantId }) => tenantId); + }, [affiliations]); const handleOnSubmit = useCallback(async () => { const getAffiliationIds = (assigned) => ( diff --git a/src/hooks/useUserAffiliations/useUserAffiliations.js b/src/hooks/useUserAffiliations/useUserAffiliations.js index 91f43c69f..b7ead459b 100644 --- a/src/hooks/useUserAffiliations/useUserAffiliations.js +++ b/src/hooks/useUserAffiliations/useUserAffiliations.js @@ -85,7 +85,7 @@ const useUserAffiliations = ({ userId } = {}, options = {}) => { return ({ affiliations, - totalRecords: data.totalRecords, + totalRecords: data.totalRecords || 0, isFetching, isLoading, refetch, From 679b94b8db858a459e411fd8beaf0b33b570be4d Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 20 Sep 2023 18:20:15 +0500 Subject: [PATCH 04/14] hide primary and central affiliations --- .../AffiliationsManager/AffiliationsManager.test.js | 8 ++++---- .../AffiliationsManagerModal/AffiliationsManagerModal.js | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManager.test.js b/src/components/AffiliationsManager/AffiliationsManager.test.js index cea18580c..22e39d5c9 100644 --- a/src/components/AffiliationsManager/AffiliationsManager.test.js +++ b/src/components/AffiliationsManager/AffiliationsManager.test.js @@ -68,7 +68,7 @@ describe('AffiliationsManager', () => { describe('Filters', () => { it('should filter results by search query', async () => { - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length + 1); + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); await userEvent.type(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.search'), affiliations[0].tenantName); await userEvent.click(await screen.findByText('ui-users.search')); @@ -77,7 +77,7 @@ describe('AffiliationsManager', () => { }); it('should filter results by assignment status', async () => { - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length + 1); + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); const assignmentCheckboxes = await screen.findAllByLabelText('ui-users.affiliations.manager.modal.aria.assign'); @@ -97,7 +97,7 @@ describe('AffiliationsManager', () => { await userEvent.click(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.assignAll')); await userEvent.click(await screen.findByText('ui-users.affiliations.manager.filter.assignment.unassigned')); - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length + 1); + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); await userEvent.type(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.search'), 'Columbia'); await userEvent.click(await screen.findByText('ui-users.search')); @@ -106,7 +106,7 @@ describe('AffiliationsManager', () => { await userEvent.click(await screen.findByTestId('reset-all-affiliations-filters')); - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length + 1); + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); }); }); }); diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index b823e99b0..c402d4473 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -60,6 +60,8 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { isLoading: isConsortiumTenantsLoading, } = useConsortiumTenants(); + const visibleAffiliations = useMemo(() => affiliations.filter(({ isPrimary }) => !isPrimary), [affiliations]); + const { assignment, isAllAssigned, @@ -67,15 +69,15 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { toggleAll, totalAssigned, } = useAffiliationsAssignment({ - affiliations, + affiliations: visibleAffiliations, tenants, }); const isLoading = isConsortiumTenantsLoading || isUsersAffiliationsLoading; const affiliationIds = useMemo(() => { - return affiliations.filter(({ isPrimary }) => !isPrimary).map(({ tenantId }) => tenantId); - }, [affiliations]); + return visibleAffiliations.map(({ tenantId }) => tenantId); + }, [visibleAffiliations]); const handleOnSubmit = useCallback(async () => { const getAffiliationIds = (assigned) => ( From 4ef3aa9d978cf6b254262da7dba2ed671c2fc988 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 20 Sep 2023 19:03:57 +0500 Subject: [PATCH 05/14] tests: fix failing tests --- .../AffiliationsManager/AffiliationsManager.test.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManager.test.js b/src/components/AffiliationsManager/AffiliationsManager.test.js index 22e39d5c9..e4b285314 100644 --- a/src/components/AffiliationsManager/AffiliationsManager.test.js +++ b/src/components/AffiliationsManager/AffiliationsManager.test.js @@ -86,24 +86,18 @@ describe('AffiliationsManager', () => { await userEvent.click(assignmentCheckboxes[2]); await userEvent.click(await screen.findByText('ui-users.affiliations.manager.filter.assignment.assigned')); - expect(await screen.findAllByRole('row')).toHaveLength((affiliations.length - 3) + 1); + expect(await screen.findAllByRole('row')).toHaveLength((affiliations.length - 3)); await userEvent.click(await screen.findByLabelText(/Clear selected filters for/)); - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length + 1); + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); }); it('should reset search and filters when \'Reset all\' button was clicked', async () => { await userEvent.click(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.assignAll')); await userEvent.click(await screen.findByText('ui-users.affiliations.manager.filter.assignment.unassigned')); - - expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); - await userEvent.type(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.search'), 'Columbia'); await userEvent.click(await screen.findByText('ui-users.search')); - - expect(await screen.findAllByRole('row')).toHaveLength(2); - await userEvent.click(await screen.findByTestId('reset-all-affiliations-filters')); expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); From 7f91b680090d9ff2d441750915034fecf267e1d1 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Wed, 20 Sep 2023 19:39:25 +0500 Subject: [PATCH 06/14] tests: fix failing tests --- .../UserAffiliations/UserAffiliations.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js b/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js index 86d0bce93..d103602ba 100644 --- a/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js +++ b/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js @@ -106,7 +106,7 @@ describe('UserAffiliations', () => { name: 'ui-users.affiliations.manager.modal.aria.assign', checked: true, }); - expect(listOfAssignedTenants).toHaveLength(affiliations.length); + expect(listOfAssignedTenants).toHaveLength(affiliations.length - 1); const saveAndCloseButton = screen.getByText('ui-users.saveAndClose'); userEvent.click(saveAndCloseButton); await waitForElementToBeRemoved(() => screen.queryByText('ui-users.affiliations.manager.modal.title')); From f2e5ef8afc94fb28fee481ef87714a4b8b681adc Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Thu, 21 Sep 2023 18:50:20 +0500 Subject: [PATCH 07/14] display missing affiliations --- .../AffiliationsManagerModal.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index c402d4473..247845725 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -60,7 +60,7 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { isLoading: isConsortiumTenantsLoading, } = useConsortiumTenants(); - const visibleAffiliations = useMemo(() => affiliations.filter(({ isPrimary }) => !isPrimary), [affiliations]); + const primaryAffiliation = useMemo(() => affiliations.find(({ isPrimary }) => isPrimary), [affiliations]); const { assignment, @@ -69,15 +69,15 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { toggleAll, totalAssigned, } = useAffiliationsAssignment({ - affiliations: visibleAffiliations, + affiliations, tenants, }); const isLoading = isConsortiumTenantsLoading || isUsersAffiliationsLoading; const affiliationIds = useMemo(() => { - return visibleAffiliations.map(({ tenantId }) => tenantId); - }, [visibleAffiliations]); + return affiliations.map(({ tenantId }) => tenantId); + }, [affiliations]); const handleOnSubmit = useCallback(async () => { const getAffiliationIds = (assigned) => ( @@ -120,7 +120,7 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { filtersConfig .reduce((filtered, config) => config.filter(filtered, activeFilters, assignment), tenants) .filter(({ name, isCentral, id }) => { - if (isCentral || !affiliationIds.includes(id)) return false; + if (isCentral || primaryAffiliation.tenantId === id) return false; return (searchQuery ? name.toLowerCase().includes(searchQuery.toLowerCase()) : true); }), @@ -128,7 +128,15 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { sortDirection.name, ) ); - }, [affiliationIds, assignment, filters, sortDirection.name, sortOrder, sorters, tenants]); + }, [ + assignment, + filters, + primaryAffiliation.tenantId, + sortDirection.name, + sortOrder, + sorters, + tenants, + ]); return ( Date: Tue, 26 Sep 2023 13:09:47 +0500 Subject: [PATCH 08/14] hide central and primary affiliations --- .../AffiliationsManagerModal.js | 18 ++++++++++++------ .../useAffiliationsAssignment.js | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index 247845725..f9444a057 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -15,6 +15,7 @@ import { Modal, Paneset, } from '@folio/stripes/components'; +import { useStripes } from '@folio/stripes/core'; import { useConsortiumTenants, @@ -43,6 +44,8 @@ const INITIAL_FILTERS = {}; const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const [isFiltersVisible, toggleFilters] = useToggle(true); const [filters, dispatch] = useReducer(filtersReducer, INITIAL_FILTERS); + const stripes = useStripes(); + const currentUserTenants = stripes.user?.user?.tenants; const { sortOrder, @@ -61,6 +64,9 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { } = useConsortiumTenants(); const primaryAffiliation = useMemo(() => affiliations.find(({ isPrimary }) => isPrimary), [affiliations]); + const affiliationIds = useMemo(() => { + return currentUserTenants.map(({ id }) => id); + }, [currentUserTenants]); const { assignment, @@ -71,14 +77,11 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { } = useAffiliationsAssignment({ affiliations, tenants, + affiliationIds, }); const isLoading = isConsortiumTenantsLoading || isUsersAffiliationsLoading; - const affiliationIds = useMemo(() => { - return affiliations.map(({ tenantId }) => tenantId); - }, [affiliations]); - const handleOnSubmit = useCallback(async () => { const getAffiliationIds = (assigned) => ( Object @@ -120,7 +123,9 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { filtersConfig .reduce((filtered, config) => config.filter(filtered, activeFilters, assignment), tenants) .filter(({ name, isCentral, id }) => { - if (isCentral || primaryAffiliation.tenantId === id) return false; + const isNotValid = isCentral || primaryAffiliation?.tenantId === id || !affiliationIds.includes(id); + + if (isNotValid) return false; return (searchQuery ? name.toLowerCase().includes(searchQuery.toLowerCase()) : true); }), @@ -129,9 +134,10 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { ) ); }, [ + affiliationIds, assignment, filters, - primaryAffiliation.tenantId, + primaryAffiliation, sortDirection.name, sortOrder, sorters, diff --git a/src/components/AffiliationsManager/useAffiliationsAssignment.js b/src/components/AffiliationsManager/useAffiliationsAssignment.js index 535d544db..78766cd41 100644 --- a/src/components/AffiliationsManager/useAffiliationsAssignment.js +++ b/src/components/AffiliationsManager/useAffiliationsAssignment.js @@ -6,22 +6,22 @@ import { useState, } from 'react'; -const useAffiliationsAssignment = ({ affiliations, tenants }) => { +const useAffiliationsAssignment = ({ affiliations, tenants, affiliationIds = [] }) => { const [assignment, setAssignment] = useState({}); useEffect(() => { setAssignment(() => { const affiliationsMap = keyBy(affiliations, 'tenantId'); - return tenants.reduce((acc, { id, isCentral }) => { - if (!isCentral) { + const isValidAffiliation = !isCentral && affiliationIds.includes(id); + if (isValidAffiliation) { acc[id] = Boolean(affiliationsMap[id]); } return acc; }, {}); }); - }, [affiliations, tenants]); + }, [affiliationIds, affiliations, tenants]); const isAllAssigned = useMemo(() => ( Object.values(assignment).every(value => Boolean(value)) From 95e339a2398090943a026d909347620296964308 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 26 Sep 2023 17:10:28 +0500 Subject: [PATCH 09/14] tests: fix failing tests --- .../AffiliationsManager.test.js | 14 +++++++++++ .../AffiliationsManagerModal.js | 2 +- .../useAffiliationsAssignment.test.js | 25 ++++++++++++------- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManager.test.js b/src/components/AffiliationsManager/AffiliationsManager.test.js index e4b285314..c45a5be5e 100644 --- a/src/components/AffiliationsManager/AffiliationsManager.test.js +++ b/src/components/AffiliationsManager/AffiliationsManager.test.js @@ -1,9 +1,11 @@ import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; +import { useStripes } from '@folio/stripes/core'; import '__mock__/currencyData.mock'; import affiliations from 'fixtures/affiliations'; + import { useConsortiumTenants, useUserAffiliations, @@ -13,6 +15,11 @@ import AffiliationsManager from './AffiliationsManager'; jest.unmock('@folio/stripes/components'); jest.unmock('@folio/stripes/util'); +jest.mock('@folio/stripes/core', () => ({ + ...jest.requireActual('@folio/stripes/core'), + useStripes: jest.fn(), +})); + jest.mock('../../hooks', () => ({ ...jest.requireActual('../../hooks'), useConsortiumTenants: jest.fn(), @@ -24,6 +31,12 @@ const defaultProps = { userId: 'userId', }; +const tenants = affiliations.map(i => ({ + id: i.tenantId, + name: i.tenantName, + primary: i.isPrimary, +})); + const renderAffiliationsManager = (props = {}) => render( { useUserAffiliations .mockClear() .mockReturnValue({ isLoading: false, affiliations }); + useStripes.mockClear().mockReturnValue({ user: { user: { tenants } } }); }); describe('Modal', () => { diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index f9444a057..e5c61a19e 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -45,7 +45,7 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const [isFiltersVisible, toggleFilters] = useToggle(true); const [filters, dispatch] = useReducer(filtersReducer, INITIAL_FILTERS); const stripes = useStripes(); - const currentUserTenants = stripes.user?.user?.tenants; + const currentUserTenants = useMemo(() => stripes.user?.user?.tenants || [], [stripes]); const { sortOrder, diff --git a/src/components/AffiliationsManager/useAffiliationsAssignment.test.js b/src/components/AffiliationsManager/useAffiliationsAssignment.test.js index ce1723147..f16e1357f 100644 --- a/src/components/AffiliationsManager/useAffiliationsAssignment.test.js +++ b/src/components/AffiliationsManager/useAffiliationsAssignment.test.js @@ -5,12 +5,14 @@ import useAffiliationsAssignment from './useAffiliationsAssignment'; const userAffiliations = affiliations.slice(0, 3); const tenants = affiliations.map(({ tenantId, tenantName }) => ({ id: tenantId, name: tenantName })); +const affiliationIds = userAffiliations.map(({ tenantId }) => tenantId); describe('useAffiliationsAssignment', () => { it('should mark initial user\'s affiliations as assigned', async () => { const { result } = renderHook(() => useAffiliationsAssignment({ affiliations: userAffiliations, tenants, + affiliationIds, })); expect(result.current.totalAssigned).toEqual(3); @@ -18,7 +20,6 @@ describe('useAffiliationsAssignment', () => { [affiliations[0].tenantId]: true, [affiliations[1].tenantId]: true, [affiliations[2].tenantId]: true, - [affiliations[3].tenantId]: false, })); }); @@ -26,6 +27,7 @@ describe('useAffiliationsAssignment', () => { const { result } = renderHook(() => useAffiliationsAssignment({ affiliations: userAffiliations, tenants, + affiliationIds, })); act(() => result.current.toggle({ id: affiliations[0].tenantId })); @@ -45,20 +47,25 @@ describe('useAffiliationsAssignment', () => { const { result } = renderHook(() => useAffiliationsAssignment({ affiliations: userAffiliations, tenants, + affiliationIds, })); act(() => result.current.toggleAll()); - expect(result.current.isAllAssigned).toBeTruthy(); - expect(result.current.assignment).toEqual( - affiliations.reduce((acc, { tenantId }) => ({ ...acc, [tenantId]: true }), {}), - ); + expect(result.current.isAllAssigned).toBeFalsy(); + expect(result.current.assignment).toEqual({ + [affiliations[0].tenantId]: false, + [affiliations[1].tenantId]: false, + [affiliations[2].tenantId]: false, + }); act(() => result.current.toggleAll()); - expect(result.current.isAllAssigned).toBeFalsy(); - expect(result.current.assignment).toEqual( - affiliations.reduce((acc, { tenantId }) => ({ ...acc, [tenantId]: false }), {}), - ); + expect(result.current.isAllAssigned).toBeTruthy(); + expect(result.current.assignment).toEqual({ + [affiliations[0].tenantId]: true, + [affiliations[1].tenantId]: true, + [affiliations[2].tenantId]: true, + }); }); }); From 858dd40cd71b3cbf07fd1398cb0d8c7c185156cb Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 26 Sep 2023 18:30:16 +0500 Subject: [PATCH 10/14] tests: fix failing tests --- .../UserAffiliations/UserAffiliations.test.js | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js b/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js index d103602ba..95c0a5775 100644 --- a/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js +++ b/src/components/UserDetailSections/UserAffiliations/UserAffiliations.test.js @@ -1,14 +1,12 @@ -import { - screen, - waitForElementToBeRemoved, -} from '@folio/jest-config-stripes/testing-library/react'; -import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; import { QueryClient, QueryClientProvider, } from 'react-query'; -import renderWithRouter from 'helpers/renderWithRouter'; +import { screen } from '@folio/jest-config-stripes/testing-library/react'; +import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; +import { useStripes } from '@folio/stripes/core'; +import renderWithRouter from 'helpers/renderWithRouter'; import affiliations from 'fixtures/affiliations'; import { useConsortiumTenants, @@ -27,6 +25,10 @@ jest.mock('../../../hooks', () => ({ useUserAffiliations: jest.fn(), useUserAffiliationsMutation: jest.fn(), })); +jest.mock('@folio/stripes/core', () => ({ + ...jest.requireActual('@folio/stripes/core'), + useStripes: jest.fn(), +})); jest.mock('../../IfConsortiumPermission', () => jest.fn(({ children }) => children)); jest.mock('./util', () => ({ @@ -41,7 +43,11 @@ const defaultProps = { userName: 'mobius', }; -const tenants = affiliations.map(({ tenantId, tenantName }) => ({ id: tenantId, name: tenantName })); +const tenants = affiliations.map(({ tenantId, tenantName, primary }) => ({ + id: tenantId, + name: tenantName, + isPrimary: primary +})); const renderUserAffiliations = (props = {}) => renderWithRouter( @@ -58,6 +64,7 @@ describe('UserAffiliations', () => { beforeEach(() => { useConsortiumTenants.mockClear().mockReturnValue({ tenants }); useUserAffiliationsMutation.mockClear().mockReturnValue({ handleAssignment, isLoading: false }); + useStripes.mockClear().mockReturnValue({ user: { user: { tenants } } }); useUserAffiliations .mockClear() .mockReturnValue({ affiliations, totalRecords: affiliations.length, isLoading: false, handleAssignment: () => [{}], refetch: () => {} }); @@ -101,15 +108,15 @@ describe('UserAffiliations', () => { renderUserAffiliations(); const assignButton = screen.getByText('ui-users.affiliations.section.action.edit'); - userEvent.click(assignButton); + await userEvent.click(assignButton); const listOfAssignedTenants = await screen.findAllByRole('checkbox', { name: 'ui-users.affiliations.manager.modal.aria.assign', checked: true, }); expect(listOfAssignedTenants).toHaveLength(affiliations.length - 1); const saveAndCloseButton = screen.getByText('ui-users.saveAndClose'); - userEvent.click(saveAndCloseButton); - await waitForElementToBeRemoved(() => screen.queryByText('ui-users.affiliations.manager.modal.title')); + await userEvent.click(saveAndCloseButton); + expect(handleAssignment).toHaveBeenCalled(); expect(screen.queryByText('ui-users.saveAndClose')).toBeNull(); }); From 779d258b512ae8140155ff86e24717a13bc7efcf Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Fri, 29 Sep 2023 16:03:47 +0500 Subject: [PATCH 11/14] exclude primary affiliation --- .../AffiliationsManagerModal/AffiliationsManagerModal.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index e5c61a19e..8fbf9aa44 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -65,8 +65,12 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const primaryAffiliation = useMemo(() => affiliations.find(({ isPrimary }) => isPrimary), [affiliations]); const affiliationIds = useMemo(() => { - return currentUserTenants.map(({ id }) => id); - }, [currentUserTenants]); + const excludePrimaryAffiliation = ({ isPrimary, id }) => { + return !isPrimary && id !== primaryAffiliation?.tenantId; + }; + + return currentUserTenants.filter(excludePrimaryAffiliation).map(({ id }) => id); + }, [currentUserTenants, primaryAffiliation]); const { assignment, @@ -91,7 +95,6 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { ); const buildResult = (tenantIds) => tenantIds.map(tenantId => ({ tenantId, userId })); - const added = buildResult(difference(getAffiliationIds(true), affiliationIds)); const removed = buildResult(intersection(getAffiliationIds(false), affiliationIds)); From 9e0a45f8f69f83581857e1a3abab85bffd20a1bd Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Fri, 29 Sep 2023 18:06:15 +0500 Subject: [PATCH 12/14] fix: wrong comparison affiliation ids --- .../AffiliationsManagerModal.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index 8fbf9aa44..9c38b081b 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -87,6 +87,10 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const isLoading = isConsortiumTenantsLoading || isUsersAffiliationsLoading; const handleOnSubmit = useCallback(async () => { + const affiliationIdsToCompare = affiliations + .filter(({ isPrimary }) => !isPrimary) + .map(({ tenantId }) => tenantId); + const getAffiliationIds = (assigned) => ( Object .entries(assignment) @@ -95,12 +99,12 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { ); const buildResult = (tenantIds) => tenantIds.map(tenantId => ({ tenantId, userId })); - const added = buildResult(difference(getAffiliationIds(true), affiliationIds)); - const removed = buildResult(intersection(getAffiliationIds(false), affiliationIds)); + const added = buildResult(difference(getAffiliationIds(true), affiliationIdsToCompare)); + const removed = buildResult(intersection(getAffiliationIds(false), affiliationIdsToCompare)); await onSubmit({ added, removed }); onClose(); - }, [affiliationIds, assignment, onClose, onSubmit, userId]); + }, [affiliations, assignment, onClose, onSubmit, userId]); const modalFooter = ( Date: Fri, 29 Sep 2023 19:58:54 +0500 Subject: [PATCH 13/14] fix: not displaying affiliations --- .../AffiliationsManagerModal/AffiliationsManagerModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js index 9c38b081b..c29062bb9 100644 --- a/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js +++ b/src/components/AffiliationsManager/AffiliationsManagerModal/AffiliationsManagerModal.js @@ -65,8 +65,8 @@ const AffiliationManagerModal = ({ onClose, onSubmit, userId }) => { const primaryAffiliation = useMemo(() => affiliations.find(({ isPrimary }) => isPrimary), [affiliations]); const affiliationIds = useMemo(() => { - const excludePrimaryAffiliation = ({ isPrimary, id }) => { - return !isPrimary && id !== primaryAffiliation?.tenantId; + const excludePrimaryAffiliation = ({ id }) => { + return id !== primaryAffiliation?.tenantId; }; return currentUserTenants.filter(excludePrimaryAffiliation).map(({ id }) => id); From 58e98fe1f446552758eb66e1e5b5db796103ff73 Mon Sep 17 00:00:00 2001 From: Alisher Musurmonov Date: Tue, 3 Oct 2023 12:29:08 +0500 Subject: [PATCH 14/14] test: fix sematic testing issues --- .../AffiliationsManager/AffiliationsManager.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/AffiliationsManager/AffiliationsManager.test.js b/src/components/AffiliationsManager/AffiliationsManager.test.js index c45a5be5e..ef3115ff6 100644 --- a/src/components/AffiliationsManager/AffiliationsManager.test.js +++ b/src/components/AffiliationsManager/AffiliationsManager.test.js @@ -110,8 +110,14 @@ describe('AffiliationsManager', () => { it('should reset search and filters when \'Reset all\' button was clicked', async () => { await userEvent.click(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.assignAll')); await userEvent.click(await screen.findByText('ui-users.affiliations.manager.filter.assignment.unassigned')); + + expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length); + await userEvent.type(await screen.findByLabelText('ui-users.affiliations.manager.modal.aria.search'), 'Columbia'); await userEvent.click(await screen.findByText('ui-users.search')); + + expect(await screen.findAllByRole('row')).toHaveLength(2); + await userEvent.click(await screen.findByTestId('reset-all-affiliations-filters')); expect(await screen.findAllByRole('row')).toHaveLength(affiliations.length);