diff --git a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap index 829f004573..eb27cdc61a 100644 --- a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap +++ b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap @@ -2,35 +2,289 @@ exports[`EnrolledLearnersTable renders empty state correctly 1`] = `
- - - - -
- There are no results. +
+
+
+
+
+
+
+
+
+
+
+
+ + loading + +
+
+ + + + + + + + + +
+ + + Email + + + + + + + + + + + Account Created + + + + + + + + + + + Total Course Enrollment Count + + + + + + + +
+
+
+ +
diff --git a/src/components/EnrolledLearnersTable/data/hooks/useEnrolledLearners.js b/src/components/EnrolledLearnersTable/data/hooks/useEnrolledLearners.js new file mode 100644 index 0000000000..76e5bd0ef2 --- /dev/null +++ b/src/components/EnrolledLearnersTable/data/hooks/useEnrolledLearners.js @@ -0,0 +1,51 @@ +import { useCallback, useMemo, useState } from 'react'; +import { camelCaseObject } from '@edx/frontend-platform/utils'; +import debounce from 'lodash.debounce'; +import { logError } from '@edx/frontend-platform/logging'; +import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService'; + +const useEnrolledLearners = (enterpriseId) => { + const [isLoading, setIsLoading] = useState(true); + const [enrolledLearners, setEnrolledLearners] = useState( + { + itemCount: 0, + pageCount: 0, + results: [], + }, + ); + + const fetchEnrolledLearners = useCallback(async (args) => { + try { + setIsLoading(true); + const options = { + page: args.pageIndex + 1, + pageSize: args.pageSize, + }; + const response = await EnterpriseDataApiService.fetchEnrolledLearners(enterpriseId, options); + const data = camelCaseObject(response.data); + + setEnrolledLearners({ + itemCount: data.count, + pageCount: data.numPages ?? Math.floor(data.count / options.pageSize), + results: data.results, + }); + } catch (error) { + logError(error); + } finally { + setIsLoading(false); + } + }, [enterpriseId]); + + const debouncedFetchEnrolledLearners = useMemo( + () => debounce(fetchEnrolledLearners, 300), + [fetchEnrolledLearners], + ); + + return { + isLoading, + enrolledLearners, + fetchEnrolledLearners: debouncedFetchEnrolledLearners, + }; +}; + +export default useEnrolledLearners; diff --git a/src/components/EnrolledLearnersTable/index.jsx b/src/components/EnrolledLearnersTable/index.jsx index e4e40e9103..abfd786921 100644 --- a/src/components/EnrolledLearnersTable/index.jsx +++ b/src/components/EnrolledLearnersTable/index.jsx @@ -2,61 +2,89 @@ import React from 'react'; import { useIntl } from '@edx/frontend-platform/i18n'; -import TableContainer from '../../containers/TableContainer'; +import { DataTable } from '@openedx/paragon'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { i18nFormatTimestamp } from '../../utils'; -import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiService'; +import { DEFAULT_PAGE, PAGE_SIZE } from '../learner-credit-management/data'; +import useEnrolledLearners from './data/hooks/useEnrolledLearners'; -const EnrolledLearnersTable = () => { - const intl = useIntl(); +const UserEmail = ({ row }) => ( + {row.original.user_email} +); + +UserEmail.propTypes = { + row: PropTypes.shape({ + original: PropTypes.shape({ + user_email: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, +}; - const tableColumns = [ - { - label: intl.formatMessage({ - id: 'admin.portal.lpr.enrolled.learners.table.user_email.column.heading', - defaultMessage: 'Email', - description: 'Column heading for the user email column in the enrolled learners table', - }), - key: 'user_email', - columnSortable: true, - }, - { - label: intl.formatMessage({ - id: 'admin.portal.lpr.enrolled.learners.table.lms_user_created.column.heading', - defaultMessage: 'Account Created', - description: 'Column heading for the lms user created column in the enrolled learners table', - }), - key: 'lms_user_created', - columnSortable: true, - }, - { - label: intl.formatMessage({ - id: 'admin.portal.lpr.enrolled.learners.table.enrollment_count.column.heading', - defaultMessage: 'Total Course Enrollment Count', - description: 'Column heading for the course enrollment count column in the enrolled learners table', - }), - key: 'enrollment_count', - columnSortable: true, - }, - ]; - - const formatLearnerData = learners => learners.map(learner => ({ - ...learner, - user_email: {learner.user_email}, - lms_user_created: i18nFormatTimestamp({ - intl, timestamp: learner.lms_user_created, - }), - })); +const EnrolledLearnersTable = ({ enterpriseId }) => { + const intl = useIntl(); + const { + isLoading, + enrolledLearners: tableData, + fetchEnrolledLearners: fetchTableData, + } = useEnrolledLearners(enterpriseId); return ( - i18nFormatTimestamp({ intl, timestamp: row.values.lms_user_created }), + }, + { + Header: intl.formatMessage({ + id: 'admin.portal.lpr.enrolled.learners.table.enrollment_count.column.heading', + defaultMessage: 'Total Course Enrollment Count', + description: 'Column heading for the course enrollment count column in the enrolled learners table', + }), + accessor: 'enrollment_count', + }, + ]} + initialState={{ + pageSize: PAGE_SIZE, + pageIndex: DEFAULT_PAGE, + sortBy: [ + { id: 'lms_user_created', desc: true }, + ], + }} + fetchData={fetchTableData} + data={tableData.results} + itemCount={tableData.itemCount} + pageCount={tableData.pageCount} /> ); }; -export default EnrolledLearnersTable; +const mapStateToProps = state => ({ + enterpriseId: state.portalConfiguration.enterpriseId, +}); + +EnrolledLearnersTable.propTypes = { + enterpriseId: PropTypes.string.isRequired, +}; + +export default connect(mapStateToProps)(EnrolledLearnersTable);