-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
205 additions
and
138 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,92 +1,53 @@ | ||
import { Avatar, Card, Col, Row } from '@openedx/paragon'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { | ||
Avatar, Card, Col, Row, | ||
} from '@openedx/paragon'; | ||
|
||
const OrgMemberCard = ({ original }) => { | ||
const { name, email, joinedOrg, enrollments } = original; | ||
const { enterpriseCustomerUser, enrollments } = original; | ||
const { name, joinedOrg, email } = enterpriseCustomerUser; | ||
|
||
return ( | ||
<Card orientation="horizontal"> | ||
<Card.Body> | ||
<Card.Section className="pb-1"> | ||
<Row className="d-flex flex-row"> | ||
<Col xs={2}> | ||
<Avatar size="lg" /> | ||
</Col> | ||
<Col> | ||
<Row> | ||
<h3 className="pt-2">{name}</h3> | ||
</Row> | ||
<Row> | ||
<p>{email}</p> | ||
</Row> | ||
</Col> | ||
<Col> | ||
<h5 className="pt-2 text-uppercase">Joined org</h5> | ||
{joinedOrg} | ||
</Col> | ||
<Col> | ||
<h5 className="pt-2 text-uppercase">Enrollments </h5> | ||
{enrollments} | ||
</Col> | ||
</Row> | ||
<Row className="d-flex flex-row"> | ||
<Col xs={2}> | ||
<Avatar size="lg" /> | ||
</Col> | ||
<Col> | ||
<Row> | ||
<h3 className="pt-2">{name}</h3> | ||
</Row> | ||
<Row> | ||
<p>{email}</p> | ||
</Row> | ||
</Col> | ||
<Col> | ||
<h5 className="pt-2 text-uppercase">Joined org</h5> | ||
{joinedOrg} | ||
</Col> | ||
<Col> | ||
<h5 className="pt-2 text-uppercase">Enrollments</h5> | ||
{enrollments} | ||
</Col> | ||
</Row> | ||
</Card.Section> | ||
</Card.Body> | ||
</Card> | ||
// <Card | ||
// className="mb-4 org-member-card" | ||
// orientation="horizontal" | ||
// > | ||
// <Card.Body> | ||
// <Card.Section> | ||
|
||
|
||
|
||
// <Col> | ||
// <h3> | ||
// {name} | ||
// {/* <FormattedMessage | ||
// id="executive.education.external.course.enrollment.page.registration.summarycard.title" | ||
// defaultMessage="Registration summary:" | ||
// description="Title for the registration summary card on the executive education course enrollment page" | ||
// /> */} | ||
// </h3> | ||
// <br /> | ||
// <p className="small font-weight-light text-gray-500 font-italic"> | ||
// {/* <FormattedMessage | ||
// id="executive.education.external.course.enrollment.page.course" | ||
// defaultMessage="This course is covered by the Learner Credit provided by your organization." | ||
// description="Message about the course being covered by the Learner Credit" | ||
// /> */} | ||
// {email} | ||
// </p> | ||
// </Col> | ||
// <Col xs={12} lg={{ span: 5, offset: 2 }}> | ||
// <div className="registration-details rounded-lg border p-3"> | ||
// <Row> | ||
// <Col xs={12} lg={{ span: 6, offset: 0 }} className="small font-weight-light text-gray-500 justify-content-start"> | ||
// {/* <FormattedMessage | ||
// id="executive.education.external.course.enrollment.page.registration.total.message" | ||
// defaultMessage="Registration total:" | ||
// description="Total registration cost for the executive education course" | ||
// /> */} | ||
// {joinedOrg} | ||
// </Col> | ||
// <Col xs={12} lg={{ span: 6, offset: 0 }} className="justify-content-end"> | ||
// <div className="d-flex justify-content-end small font-weight-light text-gray-500 mr-2.5"> | ||
// {/* <FormattedMessage | ||
// id="executive.education.external.course.enrollment.page.registration.tax.included" | ||
// defaultMessage="Tax included" | ||
// description="Message about tax being included in the registration cost" | ||
// /> */} | ||
// {enrollments} | ||
// </div> | ||
// </Col> | ||
// </Row> | ||
// </div> | ||
// </Col> | ||
// </Card.Section> | ||
// </Card.Body> | ||
// </Card> | ||
); | ||
}; | ||
|
||
export default OrgMemberCard; | ||
OrgMemberCard.propTypes = { | ||
original: PropTypes.shape({ | ||
enterpriseCustomerUser: PropTypes.shape({ | ||
email: PropTypes.string.isRequired, | ||
name: PropTypes.string.isRequired, | ||
joinedOrg: PropTypes.string.isRequired, | ||
}), | ||
enrollments: PropTypes.number.isRequired, | ||
}), | ||
}; | ||
|
||
export default OrgMemberCard; |
103 changes: 46 additions & 57 deletions
103
src/components/PeopleManagement/PeopleManagementTable.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,68 @@ | ||
import { CardView, Container, DataTable, TextFilter } from "@openedx/paragon"; | ||
import { useIntl } from "@edx/frontend-platform/i18n"; | ||
import React from 'react'; | ||
import { CardView, DataTable } from '@openedx/paragon'; | ||
import { connect } from 'react-redux'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import OrgMemberCard from "./OrgMemberCard"; | ||
import TableTextFilter from '../learner-credit-management/TableTextFilter'; | ||
import CustomDataTableEmptyState from '../learner-credit-management/CustomDataTableEmptyState'; | ||
import OrgMemberCard from './OrgMemberCard'; | ||
import useEnterpriseMembersTableData from './data/hooks/useEnterpriseMembersTableData'; | ||
|
||
const PeopleManagementTable = () => { | ||
const pageSize = 10; | ||
const intl = useIntl(); | ||
// const tableColumns = [ | ||
// { | ||
// Header: "Name", | ||
// accessor: "name", | ||
// }, | ||
// { | ||
// Header: "Email", | ||
// accessor: "email", | ||
// }, | ||
// { | ||
// Header: "Joined org", | ||
// accessor: "joinedOrg", | ||
// }, | ||
// { | ||
// Header: "Enrollments", | ||
// accessor: "enrollments", | ||
// }, | ||
// ]; | ||
const FilterStatus = (rest) => <DataTable.FilterStatus showFilteredFields={false} {...rest} />; | ||
|
||
const PeopleManagementTable = ({ enterpriseId }) => { | ||
const { | ||
isLoading: isTableLoading, | ||
enterpriseMembersTableData, | ||
fetchEnterpriseMembersTableData, | ||
} = useEnterpriseMembersTableData({ enterpriseId }); | ||
|
||
const tableColumns = [{ Header: 'Name', accessor: 'name' }]; | ||
|
||
return ( | ||
<DataTable | ||
className="pt-4 mt-4" | ||
isFilterable | ||
isSortable | ||
defaultColumnValues={{ Filter: TextFilter }} | ||
// isLoading={isFetching} | ||
manualSortBy | ||
isPaginated | ||
manualPagination | ||
isFilterable | ||
manualFilters | ||
isLoading={isTableLoading} | ||
defaultColumnValues={{ Filter: TableTextFilter }} | ||
FilterStatusComponent={FilterStatus} | ||
numBreakoutFilters={2} | ||
columns={tableColumns} | ||
initialState={{ | ||
pageSize, | ||
pageSize: 10, | ||
pageIndex: 0, | ||
sortBy: [ | ||
{ id: 'enterpriseCustomerUser.name', desc: true }, | ||
], | ||
filters: [], | ||
}} | ||
// itemCount={paginatedData.itemCount} | ||
// pageCount={paginatedData.pageCount} | ||
// fetchData={fetchData} | ||
// data={paginatedData.data} | ||
data={[ | ||
{ | ||
name: "April Ludgate", | ||
email: "[email protected]", | ||
joinedOrg: "Jan 21, 2021", | ||
enrollments: 3, | ||
}, | ||
{ | ||
name: "Ben Wyatt", | ||
email: "[email protected]", | ||
joinedOrg: "Oct 31, 2022", | ||
enrollments: 1, | ||
}, | ||
]} | ||
columns={tableColumns}> | ||
fetchData={fetchEnterpriseMembersTableData} | ||
data={enterpriseMembersTableData.results} | ||
itemCount={enterpriseMembersTableData.itemCount} | ||
pageCount={enterpriseMembersTableData.pageCount} | ||
EmptyTableComponent={CustomDataTableEmptyState} | ||
> | ||
<DataTable.TableControlBar /> | ||
<CardView | ||
className="d-block" | ||
CardComponent={OrgMemberCard} | ||
columnSizes={{ xs: 12 }} | ||
/> | ||
{/* <DataTable.Table /> */} | ||
{/* <DataTable.EmptyTable | ||
content={intl.formatMessage({ | ||
id: 'peopleManagement.dataTable.empty', | ||
defaultMessage: 'No results found.', | ||
description: 'Message displayed when the table has no data.', | ||
})} | ||
/> */} | ||
<DataTable.TableFooter /> | ||
</DataTable> | ||
); | ||
}; | ||
|
||
export default PeopleManagementTable; | ||
PeopleManagementTable.propTypes = { | ||
enterpriseId: PropTypes.string.isRequired, | ||
}; | ||
|
||
const mapStateToProps = state => ({ | ||
enterpriseId: state.portalConfiguration.enterpriseId, | ||
}); | ||
|
||
export default connect(mapStateToProps)(PeopleManagementTable); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
src/components/PeopleManagement/data/hooks/useEnterpriseMembersTableData.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { | ||
useCallback, useMemo, useState, | ||
} from 'react'; | ||
import { camelCaseObject } from '@edx/frontend-platform/utils'; | ||
import { logError } from '@edx/frontend-platform/logging'; | ||
import debounce from 'lodash.debounce'; | ||
|
||
import LmsApiService from '../../../../data/services/LmsApiService'; | ||
|
||
const useEnterpriseMembersTableData = ({ enterpriseId }) => { | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [enterpriseMembersTableData, setEnterpriseMembersTableData] = useState({ | ||
itemCount: 0, | ||
pageCount: 0, | ||
results: [], | ||
}); | ||
const fetchEnterpriseMembersData = useCallback((args) => { | ||
const fetch = async () => { | ||
try { | ||
setIsLoading(true); | ||
const options = {}; | ||
args.filters.forEach((filter) => { | ||
const { id, value } = filter; | ||
if (id === 'name') { | ||
options.user_query = value; | ||
} | ||
}); | ||
|
||
options.page = args.pageIndex + 1; | ||
const response = await LmsApiService.fetchEnterpriseCustomerMembers(enterpriseId, options); | ||
const data = camelCaseObject(response.data); | ||
setEnterpriseMembersTableData({ | ||
itemCount: data.count, | ||
pageCount: data.numPages ?? Math.floor(data.count / options.pageSize), | ||
results: data.results, | ||
}); | ||
} catch (error) { | ||
logError(error); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
fetch(); | ||
}, [enterpriseId]); | ||
|
||
const debouncedFetchEnterpriseMembersData = useMemo( | ||
() => debounce(fetchEnterpriseMembersData, 300), | ||
[fetchEnterpriseMembersData], | ||
); | ||
|
||
return { | ||
isLoading, | ||
enterpriseMembersTableData, | ||
fetchEnterpriseMembersTableData: debouncedFetchEnterpriseMembersData, | ||
}; | ||
}; | ||
|
||
export default useEnterpriseMembersTableData; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/components/PeopleManagement/tests/useEnterpriseMembersTableData.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { renderHook } from '@testing-library/react-hooks'; | ||
import { camelCaseObject } from '@edx/frontend-platform/utils'; | ||
import LmsApiService from '../../../data/services/LmsApiService'; | ||
|
||
import useEnterpriseMembersTableData from '../data/hooks/useEnterpriseMembersTableData'; | ||
|
||
describe('useEnterpriseMembersTableData', () => { | ||
it('should fetch and return members of an enterprise', async () => { | ||
const mockEnterpriseUUID = 'uuid-bb'; | ||
const mockData = { | ||
count: 1, | ||
current_page: 1, | ||
next: null, | ||
num_pages: 1, | ||
previous: null, | ||
results: [{ | ||
enterprise_customer_user: { | ||
email: '[email protected]', | ||
joinedOrg: 'Sep 15, 2021', | ||
name: 'Jeez Louise', | ||
}, | ||
enrollments: 11, | ||
}], | ||
}; | ||
const mockEnterpriseMembers = jest.spyOn(LmsApiService, 'fetchEnterpriseCustomerMembers'); | ||
mockEnterpriseMembers.mockResolvedValue({ data: mockData }); | ||
|
||
const { result, waitForNextUpdate } = renderHook( | ||
() => useEnterpriseMembersTableData({ enterpriseId: mockEnterpriseUUID }), | ||
); | ||
result.current.fetchEnterpriseMembersTableData({ | ||
pageIndex: 0, | ||
pageSize: 10, | ||
filters: [], | ||
sortBy: [], | ||
}); | ||
await waitForNextUpdate(); | ||
expect(LmsApiService.fetchEnterpriseCustomerMembers).toHaveBeenCalledWith(mockEnterpriseUUID, { page: 1 }); | ||
expect(result.current.isLoading).toEqual(false); | ||
expect(result.current.enterpriseMembersTableData.results).toEqual(camelCaseObject(mockData.results)); | ||
}); | ||
}); |
Oops, something went wrong.