Skip to content

Commit

Permalink
UIPFU-77 - Add 'User assignment status' filter group
Browse files Browse the repository at this point in the history
  • Loading branch information
Terala-Priyanka committed Jan 10, 2024
1 parent 1e3f510 commit 0024421
Show file tree
Hide file tree
Showing 14 changed files with 836 additions and 26 deletions.
25 changes: 24 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
{
"parser": "@babel/eslint-parser",
"extends": "@folio/eslint-config-stripes"
"extends": "@folio/eslint-config-stripes",
"overrides": [
{
"files": [ "src/**/tests/*", "*.test.js", "test/**/*" ],
"rules": {
"react/prop-types": "off",
"import/prefer-default-export": "off"
}
}
],
"env": {
"jest": true
},
"settings": {
"import/resolver": {
"alias": {
"map": [
["__mock__", "./test/jest/__mock__"],
["fixtures", "./test/jest/fixtures"],
["helpers", "./test/jest/helpers"]
]
}
}
}
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
artifacts
artifacts
junit.xml
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## 7.1.0 IN PROGRESS

* Add "User assignment status" filter in find-user-plugin. Refs UIPFU-77.

## [7.0.0](https://github.com/folio-org/ui-plugin-find-user/tree/v7.0.0) (2023-10-12)
[Full Changelog](https://github.com/folio-org/ui-plugin-find-user/compare/v6.3.0...v7.0.0)

Expand Down
10 changes: 10 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const path = require('path');
const config = require('@folio/jest-config-stripes');

module.exports = {
...config,
setupFiles: [
...config.setupFiles,
path.join(__dirname, './test/jest/setupFiles.js'),
],
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"lint": "eslint .",
"build-mod-descriptor": "stripes mod descriptor --full --strict | jq '.[]' > module-descriptor.json ",
"formatjs-compile": "formatjs compile-folder --ast --format simple ./translations/ui-plugin-find-user ./translations/ui-plugin-find-user/compiled",
"test": "stripes test karma"
"test": "stripes test karma",
"test:jest": "yarn run jest"
},
"okapiInterfaces": {
"users": "16.0",
Expand All @@ -37,6 +38,7 @@
"@bigtest/interactor": "^0.9.1",
"@bigtest/mocha": "^0.5.2",
"@folio/eslint-config-stripes": "^7.0.0",
"@folio/jest-config-stripes": "^2.0.0",
"@folio/stripes": "^9.0.0",
"@folio/stripes-cli": "^3.0.0",
"@folio/stripes-core": "^10.0.0",
Expand Down
45 changes: 26 additions & 19 deletions src/UserSearchContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
StripesConnectedSource,
} from '@folio/stripes/smart-components';

import filterConfig from './filterConfig';
import filterConfig, { filterConfigWithUserAssignedStatus } from './filterConfig';
import { updateResourceData } from './utils';
import { UAS } from './constants';

const INITIAL_RESULT_COUNT = 30;
const RESULT_COUNT_INCREMENT = 30;
Expand All @@ -32,6 +34,28 @@ const compileQuery = template(
{ interpolate: /%{([\s\S]+?)}/g }
);

export function buildQuery(queryParams, pathComponents, resourceData, logger, props) {
const filters = props.initialSelectedUsers ? filterConfigWithUserAssignedStatus : filterConfig;
const updatedResourceData = props.initialSelectedUsers && resourceData?.query?.filters?.substring(`${UAS}`) ? updateResourceData(resourceData) : resourceData;

return makeQueryFunction(
'cql.allRecords=1',
(parsedQuery, _, localProps) => localProps.query.query.trim().split(/\s+/).map(query => compileQuery({ query })).join(' and '),
{
// the keys in this object must match those passed to
// SearchAndSort's columnMapping prop
'active': 'active',
'name': 'personal.lastName personal.firstName',
'patronGroup': 'patronGroup.group',
'username': 'username',
'barcode': 'barcode',
'email': 'personal.email',
},
filters,
2,
)(queryParams, pathComponents, updatedResourceData, logger, props);
}

class UserSearchContainer extends React.Component {
static manifest = Object.freeze({
initializedFilterConfig: { initialValue: false },
Expand All @@ -46,24 +70,7 @@ class UserSearchContainer extends React.Component {
perRequest: 100,
path: 'users',
GET: {
params: {
query: makeQueryFunction(
'cql.allRecords=1',
(parsedQuery, props, localProps) => localProps.query.query.trim().split(/\s+/).map(query => compileQuery({ query })).join(' and '),
{
// the keys in this object must match those passed to
// SearchAndSort's columnMapping prop
'active': 'active',
'name': 'personal.lastName personal.firstName',
'patronGroup': 'patronGroup.group',
'username': 'username',
'barcode': 'barcode',
'email': 'personal.email',
},
filterConfig,
2,
),
},
params: { query: buildQuery },
staticFallback: { params: {} },
},
},
Expand Down
1 change: 1 addition & 0 deletions src/UserSearchModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class UserSearchModal extends Component {
{...this.props}
onComponentWillUnmount={onCloseModal}
tenantId={tenantId || stripes.okapi.tenant}
initialSelectedUsers={initialSelectedUsers}
>
{(viewProps) => <UserSearchView
{...viewProps}
Expand Down
28 changes: 25 additions & 3 deletions src/UserSearchView.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import {
SearchAndSortSearchButton as FilterPaneToggle,
} from '@folio/stripes/smart-components';

import filterConfig from './filterConfig';
import { UAS } from './constants';
import { getUsersBasedOnAssignmentStatus } from './utils';
import filterConfig, { filterConfigWithUserAssignedStatus } from './filterConfig';
import Filters from './Filters';

import css from './UserSearch.css';
Expand Down Expand Up @@ -173,6 +175,13 @@ class UserSearchView extends React.Component {

isSelected = ({ item }) => Boolean(this.state.checkedMap[item.id]);

getFilterConfig = () => {
if (this.props.initialSelectedUsers) {
return filterConfigWithUserAssignedStatus;
}
return filterConfig;
}

render() {
const {
onSelectRow,
Expand Down Expand Up @@ -286,6 +295,19 @@ class UserSearchView extends React.Component {
return true;
};

const getContentData = () => {
const activeFilterState = activeFilters?.state;
const isUasFilterGroupActive = {}.hasOwnProperty.call(activeFilterState, UAS);
const uasFilterValue = activeFilterState.uas;

if (isUasFilterGroupActive && uasFilterValue.length === 1) {
const filteredUsers = getUsersBasedOnAssignmentStatus(activeFilterState, uasFilterValue, initialSelectedUsers, users);
return filteredUsers;
}
// when both 'Assigned' and 'Unassigned' filters are applied or both are not applied
return users;
};

return (
<IntlConsumer>
{intl => (
Expand Down Expand Up @@ -339,7 +361,7 @@ class UserSearchView extends React.Component {
<Filters
onChangeHandlers={getFilterHandlers()}
activeFilters={activeFilters}
config={filterConfig}
config={this.getFilterConfig()}
resultOffset={resultOffset}
/>
</form>
Expand All @@ -355,7 +377,7 @@ class UserSearchView extends React.Component {
<MultiColumnList
visibleColumns={builtVisibleColumns}
isSelected={this.isSelected}
contentData={users}
contentData={getContentData()}
totalCount={count}
id="list-plugin-find-user"
columnMapping={{
Expand Down
8 changes: 8 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable import/prefer-default-export */
export const UAS = 'uas';
export const ASSIGNED_FILTER_KEY = 'uas.Assigned';
export const UNASSIGNED_FILTER_KEY = 'uas.Unassigned';
export const ASSIGNED = 'Assigned';
export const UNASSIGNED = 'Unassigned';
export const ACTIVE = 'active';
export const INACTIVE = 'inactive';
21 changes: 21 additions & 0 deletions src/filterConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,25 @@ const filterConfig = [
},
];

export const filterConfigWithUserAssignedStatus = [
...filterConfig,
{
label: <FormattedMessage id="ui-plugin-find-user.userAssignmentStatus" />,
name: 'uas',
cql: 'uas',
values: [
{
name: 'Assigned',
cql: 'true',
displayName: <FormattedMessage id="ui-plugin-find-user.assigned" />,
},
{
name: 'Unassigned',
cql: 'false',
displayName: <FormattedMessage id="ui-plugin-find-user.unassigned" />,
},
],
},
];

export default filterConfig;
87 changes: 87 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import cloneDeep from 'lodash/cloneDeep';
import {
ASSIGNED_FILTER_KEY,
UNASSIGNED_FILTER_KEY,
ASSIGNED,
UNASSIGNED,
UAS,
ACTIVE,
INACTIVE
} from './constants';

// eslint-disable-next-line import/prefer-default-export
export const updateResourceData = (rData) => {
const filterString = rData?.query?.filters;
const newRData = cloneDeep(rData);
if (filterString === `${UNASSIGNED_FILTER_KEY}` || filterString === `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}` || filterString === `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`) {
/*
* When Unassigned filter is selected on 'User assigbment Status' filter group, with no other filter from other groups,
* fetch all the user records. The filter string is adjusted to include both active and inactive status filters. This will result in (cql.allRecords=1)
*
* The same applies when both Assigned and Unassigned are selected in any sequential order.
*/
const alteredfilters = 'active.active,active.inactive';
newRData.query.filters = alteredfilters;
} else if (filterString.includes(`${UNASSIGNED_FILTER_KEY}`)) {
/*
* When UnAssigned filter is selected incombination with any other filters(in other filter groups),
* filter astring for Unassigned is removed and th erest of the filter string is propagated to makeQueryFunction.
*/
const alteredfilters = newRData.query.filters.split(',').filter(str => !str.startsWith(`${UAS}`)).join(',');
newRData.query.filters = alteredfilters;
} else if (filterString.includes(`${ASSIGNED_FILTER_KEY}`)) {
/*
* When Assigned filter is selected on 'User assigbment Status' filter group, in any combination of filters in other filter groups,
* cql formation is not needed.
* hence remove aus filter before propagating it further to makeQueryFunction
*/
const alteredfilters = '';
newRData.query.filters = alteredfilters;
}
return newRData;
};

// eslint-disable-next-line consistent-return
export const getUsersBasedOnAssignmentStatus = (activeFilterState, uasFilterValue, initialSelectedUsers, users) => {
const condForOneOfTheFilters = (u) => activeFilterState?.active?.includes(u.active ? `${ACTIVE}` : `${INACTIVE}`) || activeFilterState?.pg?.includes(u.patronGroup);
const condForBothTheFilters = (u) => activeFilterState?.active?.includes(u.active ? `${ACTIVE}` : `${INACTIVE}`) && activeFilterState?.pg?.includes(u.patronGroup);
if (uasFilterValue[0] === `${ASSIGNED}`) {
// when ONLY "Assigned" filter is selected
const assignedUsers = Object.values(initialSelectedUsers);
if (Object.keys(activeFilterState).length === 1) {
return assignedUsers;
}
// several filters are selected
// filter users based on the filter group values in place

// when "Assigned" from "User Assignment Status" filter group along with some other filter in one of the other filter groups
if (Object.keys(activeFilterState).length === 2) {
const filteredAssignedUsers = assignedUsers.filter(u => condForOneOfTheFilters(u));
return filteredAssignedUsers;
}

// when filters from all the filter groups are selected
const filteredAssignedUsers = assignedUsers.filter(u => condForBothTheFilters(u));
return filteredAssignedUsers;
}
if (uasFilterValue[0] === `${UNASSIGNED}`) {
// when ONLY "Unassigned" filter is selected
const assignedUserIds = Object.keys(initialSelectedUsers);
if (Object.keys(activeFilterState).length === 1) {
const filteredUsers = users.filter(u => !assignedUserIds.includes(u.id));
return filteredUsers;
}
// several filters are selected
// filter users based on the filter group values in place

// when "UnAssigned" from "User Assignment Status" filter group along with some other filter in one of the other filter groups
if (Object.keys(activeFilterState).length === 2) {
const filteredAssignedUsers = users.filter(u => !assignedUserIds.includes(u.id) && (condForOneOfTheFilters(u)));
return filteredAssignedUsers;
}

// when filters from all the filter groups are selected
const filteredAssignedUsers = users.filter(u => !assignedUserIds.includes(u.id) && (condForBothTheFilters(u)));
return filteredAssignedUsers;
}
};
Loading

0 comments on commit 0024421

Please sign in to comment.