Skip to content

Commit

Permalink
feat: adds sorting and search functionality (#1379)
Browse files Browse the repository at this point in the history
* feat: adds sorting and search functionality
  • Loading branch information
katrinan029 authored Jan 15, 2025
1 parent 482781d commit 8666c65
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 92 deletions.
34 changes: 20 additions & 14 deletions src/components/PeopleManagement/EnterpriseCustomerUserDatatable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import {
TextFilter,
CheckboxControl,
} from '@openedx/paragon';
import { useEnterpriseLearnersTableData } from './data/hooks/useEnterpriseLearnersTableData';
import { GROUP_MEMBERS_TABLE_DEFAULT_PAGE, GROUP_MEMBERS_TABLE_PAGE_SIZE } from './constants';
import MemberDetailsCell from './MemberDetailsCell';
import AddMembersBulkAction from './GroupDetailPage/AddMembersBulkAction';
import RemoveMembersBulkAction from './RemoveMembersBulkAction';
import MemberJoinedDateCell from './MemberJoinedDateCell';
import { useEnterpriseMembersTableData } from './data/hooks';

export const BaseSelectWithContext = ({ row, enterpriseGroupLearners }) => {
const {
indeterminate,
checked,
...toggleRowSelectedProps
} = row.getToggleRowSelectedProps();
const isAddedMember = enterpriseGroupLearners.find(learner => learner.enterpriseCustomerUserId === Number(row.id));
const isAddedMember = enterpriseGroupLearners.find(learner => learner.lmsUserId === Number(row.id));
return (
<div>
<CheckboxControl
Expand All @@ -32,9 +32,8 @@ export const BaseSelectWithContext = ({ row, enterpriseGroupLearners }) => {
</div>
);
};
const FilterStatus = (rest) => <DataTable.FilterStatus showFilteredFields={false} {...rest} />;

// TO-DO: add search functionality on member details once the learner endpoint is updated
// to support search
const EnterpriseCustomerUserDatatable = ({
enterpriseId,
learnerEmails,
Expand All @@ -44,9 +43,9 @@ const EnterpriseCustomerUserDatatable = ({
}) => {
const {
isLoading,
enterpriseCustomerUserTableData,
fetchEnterpriseLearnersData,
} = useEnterpriseLearnersTableData(enterpriseId, enterpriseGroupLearners);
enterpriseMembersTableData,
fetchEnterpriseMembersTableData,
} = useEnterpriseMembersTableData({ enterpriseId });

return (
<DataTable
Expand All @@ -65,34 +64,41 @@ const EnterpriseCustomerUserDatatable = ({
columns={[
{
Header: 'Member details',
accessor: 'user.email',
accessor: 'name',
Cell: MemberDetailsCell,
},
{
Header: 'Joined organization',
accessor: 'created',
accessor: 'joinedOrg',
Cell: MemberJoinedDateCell,
disableFilters: true,
},
]}
initialState={{
pageIndex: GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
pageSize: GROUP_MEMBERS_TABLE_PAGE_SIZE,
sortBy: [
{ id: 'name', desc: true },
],
filters: [],
}}
data={enterpriseCustomerUserTableData.results}
data={enterpriseMembersTableData.results}
defaultColumnValues={{ Filter: TextFilter }}
fetchData={fetchEnterpriseLearnersData}
FilterStatusComponent={FilterStatus}
fetchData={fetchEnterpriseMembersTableData}
isFilterable
isLoading={isLoading}
isPaginated
isSelectable
itemCount={enterpriseCustomerUserTableData.itemCount}
itemCount={enterpriseMembersTableData.itemCount}
manualFilters
manualPagination
isSortable
manualSortBy
initialTableOptions={{
getRowId: row => row.id.toString(),
getRowId: row => row.enterpriseCustomerUser.userId.toString(),
}}
pageCount={enterpriseCustomerUserTableData.pageCount}
pageCount={enterpriseMembersTableData.pageCount}
manualSelectColumn={
{
id: 'selection',
Expand Down
8 changes: 4 additions & 4 deletions src/components/PeopleManagement/MemberDetailsCell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { Stack } from '@openedx/paragon';
const MemberDetailsCell = ({ row }) => (
<Stack gap={1}>
<div className="font-weight-bold">
{row.original?.user?.username}
{row.original?.enterpriseCustomerUser?.name}
</div>
<div>
{row.original?.user?.email}
{row.original?.enterpriseCustomerUser?.email}
</div>
</Stack>
);

MemberDetailsCell.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
user: PropTypes.shape({
enterpriseCustomerUser: PropTypes.shape({
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
}).isRequired,
Expand Down
7 changes: 4 additions & 3 deletions src/components/PeopleManagement/MemberJoinedDateCell.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import PropTypes from 'prop-types';
import { formatTimestamp } from '../../utils';

const MemberJoinedDateCell = ({ row }) => (
<div>
{formatTimestamp({ timestamp: row.original.created, format: 'MMM DD, YYYY' })}
{row.original.enterpriseCustomerUser.joinedOrg}
</div>
);

MemberJoinedDateCell.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
created: PropTypes.string.isRequired,
enterpriseCustomerUser: PropTypes.shape({
joinedOrg: PropTypes.string.isRequired,
}),
}).isRequired,
}).isRequired,
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/PeopleManagement/PeopleManagementTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const PeopleManagementTable = ({ enterpriseId }) => {
pageSize: 10,
pageIndex: 0,
sortBy: [
{ id: 'enterpriseCustomerUser.name', desc: true },
{ id: 'name', desc: true },
],
filters: [],
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
import { camelCaseObject } from '@edx/frontend-platform/utils';
import { logError } from '@edx/frontend-platform/logging';
import debounce from 'lodash.debounce';
import snakeCase from 'lodash/snakeCase';

import LmsApiService from '../../../../data/services/LmsApiService';

Expand Down Expand Up @@ -34,7 +35,13 @@ const useEnterpriseMembersTableData = ({ enterpriseId }) => {
options.user_query = value;
}
});

if (args?.sortBy.length > 0) {
const sortByValue = args.sortBy[0].id;
options.sort_by = snakeCase(sortByValue);
if (!args.sortBy[0].desc) {
options.is_reversed = !args.sortBy[0].desc;
}
}
options.page = args.pageIndex + 1;
const response = await LmsApiService.fetchEnterpriseCustomerMembers(enterpriseId, options);
const data = camelCaseObject(response.data);
Expand Down
61 changes: 24 additions & 37 deletions src/components/PeopleManagement/tests/AddMembersModal.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ import LmsApiService from '../../../data/services/LmsApiService';
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from '../../learner-credit-management/cards/data';
import AddMembersModal from '../AddMembersModal/AddMembersModal';
import {
useEnterpriseLearnersTableData,
useGetAllEnterpriseLearnerEmails,
} from '../data/hooks/useEnterpriseLearnersTableData';
import { useEnterpriseLearners } from '../../learner-credit-management/data';
import { useAllEnterpriseGroupLearners } from '../data/hooks';
import { useAllEnterpriseGroupLearners, useEnterpriseMembersTableData } from '../data/hooks';

jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
Expand All @@ -27,12 +26,12 @@ jest.mock('@tanstack/react-query', () => ({
jest.mock('../../../data/services/LmsApiService');
jest.mock('../data/hooks/useEnterpriseLearnersTableData', () => ({
...jest.requireActual('../data/hooks/useEnterpriseLearnersTableData'),
useEnterpriseLearnersTableData: jest.fn(),
useGetAllEnterpriseLearnerEmails: jest.fn(),
}));
jest.mock('../data/hooks', () => ({
...jest.requireActual('../data/hooks'),
useAllEnterpriseGroupLearners: jest.fn(),
useEnterpriseMembersTableData: jest.fn(),
}));
jest.mock('../../learner-credit-management/data', () => ({
...jest.requireActual('../../learner-credit-management/data'),
Expand Down Expand Up @@ -70,47 +69,35 @@ const mockTabledata = {
pageCount: 1,
results: [
{
id: 1,
user: {
id: 1,
username: 'testuser-1',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 1,
name: 'Test User 1',
email: 'testuser-1@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 5, 2021',
},
},
{
id: 2,
user: {
id: 2,
username: 'testuser-2',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 2,
name: 'Test User 2',
email: 'testuser-2@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 2, 2022',
},
},
{
id: 3,
user: {
id: 3,
username: 'testuser-3',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 3,
name: 'Test User 3',
email: 'testuser-3@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 3, 2023',
},
},
{
id: 4,
user: {
id: 4,
username: 'testuser-4',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 4,
name: 'Test User 4',
email: 'testuser-4@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 4, 2024',
},
},
],
Expand All @@ -132,10 +119,10 @@ const AddMembersModalWrapper = ({

describe('<AddMembersModal />', () => {
beforeEach(() => {
useEnterpriseLearnersTableData.mockReturnValue({
useEnterpriseMembersTableData.mockReturnValue({
isLoading: false,
enterpriseCustomerUserTableData: mockTabledata,
fetchEnterpriseLearnersData: jest.fn(),
enterpriseMembersTableData: mockTabledata,
fetchEnterpriseMembersTableData: jest.fn(),
});
useGetAllEnterpriseLearnerEmails.mockReturnValue({
isLoading: false,
Expand Down Expand Up @@ -175,11 +162,11 @@ describe('<AddMembersModal />', () => {
// renders datatable
expect(screen.getByText('Member details')).toBeInTheDocument();
expect(screen.getByText('Joined organization')).toBeInTheDocument();
expect(screen.getByText('testuser-1')).toBeInTheDocument();
expect(screen.getByText('Test User 1')).toBeInTheDocument();
expect(screen.getByText('testuser-1@2u.com')).toBeInTheDocument();
expect(screen.getByText('testuser-2')).toBeInTheDocument();
expect(screen.getByText('Test User 2')).toBeInTheDocument();
expect(screen.getByText('testuser-2@2u.com')).toBeInTheDocument();
expect(screen.getByText('testuser-3')).toBeInTheDocument();
expect(screen.getByText('Test User 3')).toBeInTheDocument();
expect(screen.getByText('testuser-3@2u.com')).toBeInTheDocument();
});
it('adds members to a group', async () => {
Expand Down
59 changes: 31 additions & 28 deletions src/components/PeopleManagement/tests/CreateGroupModal.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import LmsApiService from '../../../data/services/LmsApiService';
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from '../../learner-credit-management/cards/data';
import CreateGroupModal from '../CreateGroupModal';
import {
useEnterpriseLearnersTableData,
useGetAllEnterpriseLearnerEmails,
} from '../data/hooks/useEnterpriseLearnersTableData';
import { useEnterpriseLearners } from '../../learner-credit-management/data';
import { useEnterpriseMembersTableData } from '../data/hooks';

jest.mock('../data/hooks', () => ({
...jest.requireActual('../data/hooks'),
useEnterpriseMembersTableData: jest.fn(),
}));
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
useQueryClient: jest.fn(),
Expand Down Expand Up @@ -62,36 +66,35 @@ const mockTabledata = {
pageCount: 1,
results: [
{
id: 1,
user: {
id: 1,
username: 'testuser-1',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 1,
name: 'Test User 1',
email: 'testuser-1@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 5, 2021',
},
},
{
id: 2,
user: {
id: 2,
username: 'testuser-2',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 2,
name: 'Test User 2',
email: 'testuser-2@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 2, 2022',
},
},
{
id: 3,
user: {
id: 3,
username: 'testuser-3',
firstName: '',
lastName: '',
enterpriseCustomerUser: {
userId: 3,
name: 'Test User 3',
email: 'testuser-3@2u.com',
dateJoined: '2023-05-09T16:18:22Z',
joinedOrg: 'July 3, 2023',
},
},
{
enterpriseCustomerUser: {
userId: 4,
name: 'Test User 4',
email: 'testuser-4@2u.com',
joinedOrg: 'July 4, 2024',
},
},
],
Expand All @@ -113,10 +116,10 @@ const CreateGroupModalWrapper = ({

describe('<CreateGroupModal />', () => {
beforeEach(() => {
useEnterpriseLearnersTableData.mockReturnValue({
useEnterpriseMembersTableData.mockReturnValue({
isLoading: false,
enterpriseCustomerUserTableData: mockTabledata,
fetchEnterpriseLearnersData: jest.fn(),
enterpriseMembersTableData: mockTabledata,
fetchEnterpriseMembersTableData: jest.fn(),
});
useGetAllEnterpriseLearnerEmails.mockReturnValue({
isLoading: false,
Expand All @@ -141,11 +144,11 @@ describe('<CreateGroupModal />', () => {
// renders datatable
expect(screen.getByText('Member details')).toBeInTheDocument();
expect(screen.getByText('Joined organization')).toBeInTheDocument();
expect(screen.getByText('testuser-1')).toBeInTheDocument();
expect(screen.getByText('Test User 1')).toBeInTheDocument();
expect(screen.getByText('testuser-1@2u.com')).toBeInTheDocument();
expect(screen.getByText('testuser-2')).toBeInTheDocument();
expect(screen.getByText('Test User 2')).toBeInTheDocument();
expect(screen.getByText('testuser-2@2u.com')).toBeInTheDocument();
expect(screen.getByText('testuser-3')).toBeInTheDocument();
expect(screen.getByText('Test User 3')).toBeInTheDocument();
expect(screen.getByText('testuser-3@2u.com')).toBeInTheDocument();
});
it('creates groups and assigns learners', async () => {
Expand Down
Loading

0 comments on commit 8666c65

Please sign in to comment.