Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions src/components/Admin/Admin.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -480,38 +480,6 @@ describe('<Admin />', () => {
csvFetchMethod: 'fetchCourseEnrollments',
csvFetchParams: [enterpriseId, {}, { csv: true }],
},
'registered-unenrolled-learners': {
csvFetchMethod: 'fetchUnenrolledRegisteredLearners',
csvFetchParams: [enterpriseId, {}, { csv: true }],
},
'enrolled-learners': {
csvFetchMethod: 'fetchEnrolledLearners',
csvFetchParams: [enterpriseId, {}, { csv: true }],
},
'enrolled-learners-inactive-courses': {
csvFetchMethod: 'fetchEnrolledLearnersForInactiveCourses',
csvFetchParams: [enterpriseId, {}, { csv: true }],
},
'learners-active-week': {
csvFetchMethod: 'fetchCourseEnrollments',
csvFetchParams: [enterpriseId, { learnerActivity: 'active_past_week' }, { csv: true }],
},
'learners-inactive-week': {
csvFetchMethod: 'fetchCourseEnrollments',
csvFetchParams: [enterpriseId, { learnerActivity: 'inactive_past_week' }, { csv: true }],
},
'learners-inactive-month': {
csvFetchMethod: 'fetchCourseEnrollments',
csvFetchParams: [enterpriseId, { learnerActivity: 'inactive_past_month' }, { csv: true }],
},
'completed-learners': {
csvFetchMethod: 'fetchCompletedLearners',
csvFetchParams: [enterpriseId, {}, { csv: true }],
},
'completed-learners-week': {
csvFetchMethod: 'fetchCourseEnrollments',
csvFetchParams: [enterpriseId, { passedDate: 'last_week' }, { csv: true }],
},
};

afterEach(() => {
Expand Down
50 changes: 50 additions & 0 deletions src/components/Admin/DownloadButtonWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import PropTypes from 'prop-types';
import DownloadCsvButton from '../../containers/DownloadCsvButton';
import { useTableData } from './TableDataContext';

const DownloadButtonWrapper = ({
tableMetadata,
actionSlug,
downloadButtonLabel,
isTableDataMissing,
}) => {
const { tablesWithData } = useTableData();

// Identify tables that rely on the useTableData hook and have been migrated to the new DataTable component.
const isTableUsingDataState = [
'learners-active-week',
'learners-inactive-week',
'learners-inactive-month',
'registered-unenrolled-learners',
'enrolled-learners-inactive-courses',
'enrolled-learners',
'completed-learners',
'completed-learners-week',
].includes(actionSlug);

return (
<DownloadCsvButton
id={tableMetadata.csvButtonId}
fetchMethod={tableMetadata.csvFetchMethod}
disabled={isTableUsingDataState ? !tablesWithData[actionSlug] : isTableDataMissing(actionSlug)}
buttonLabel={downloadButtonLabel}
/>
);
};

DownloadButtonWrapper.propTypes = {
tableMetadata: PropTypes.shape({
csvButtonId: PropTypes.string,
csvFetchMethod: PropTypes.func,
}).isRequired,
actionSlug: PropTypes.string,
downloadButtonLabel: PropTypes.string,
isTableDataMissing: PropTypes.func.isRequired,
};

DownloadButtonWrapper.defaultProps = {
actionSlug: undefined,
downloadButtonLabel: undefined,
};

export default DownloadButtonWrapper;
37 changes: 37 additions & 0 deletions src/components/Admin/TableDataContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
createContext, useContext, useState, useMemo,
} from 'react';
import PropTypes from 'prop-types';

const TableDataContext = createContext({
tablesWithData: {},
setTableHasData: () => {},
});

export const TableDataProvider = ({ children }) => {
const [tablesWithData, setTablesWithData] = useState({});

const setTableHasData = (tableId, hasData) => {
setTablesWithData(prev => ({
...prev,
[tableId]: hasData,
}));
};

const value = useMemo(() => ({
tablesWithData,
setTableHasData,
}), [tablesWithData]);

return (
<TableDataContext.Provider value={value}>
{children}
</TableDataContext.Provider>
);
};

TableDataProvider.propTypes = {
children: PropTypes.node.isRequired,
};

export const useTableData = () => useContext(TableDataContext);
107 changes: 55 additions & 52 deletions src/components/Admin/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import EnrolledLearnersForInactiveCoursesTable from '../EnrolledLearnersForInact
import CompletedLearnersTable from '../CompletedLearnersTable';
import PastWeekPassedLearnersTable from '../PastWeekPassedLearnersTable';
import LearnerActivityTable from '../LearnerActivityTable';
import DownloadCsvButton from '../../containers/DownloadCsvButton';
import AdminCards from '../../containers/AdminCards';
import AdminSearchForm from './AdminSearchForm';
import EnterpriseAppSkeleton from '../EnterpriseApp/EnterpriseAppSkeleton';
Expand All @@ -34,6 +33,8 @@ import AIAnalyticsSummary from './AIAnalyticsSummary';
import AIAnalyticsSummarySkeleton from './AIAnalyticsSummarySkeleton';
import BudgetExpiryAlertAndModal from '../BudgetExpiryAlertAndModal';
import ModuleActivityReport from './tabs/ModuleActivityReport';
import { TableDataProvider } from './TableDataContext';
import DownloadButtonWrapper from './DownloadButtonWrapper';

class Admin extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -108,7 +109,7 @@ class Admin extends React.Component {
defaultMessage: 'Registered Learners Not Yet Enrolled in a Course',
description: 'Report title for registered learners not yet enrolled in a course',
}),
component: <RegisteredLearnersTable />,
component: <RegisteredLearnersTable id="registered-unenrolled-learners" />,
csvFetchMethod: () => (
EnterpriseDataApiService.fetchUnenrolledRegisteredLearners(
enterpriseId,
Expand All @@ -124,7 +125,7 @@ class Admin extends React.Component {
defaultMessage: 'Number of Courses Enrolled by Learners',
description: 'Report title for number of courses enrolled by learners',
}),
component: <EnrolledLearnersTable />,
component: <EnrolledLearnersTable id="enrolled-learners" />,
csvFetchMethod: () => (
EnterpriseDataApiService.fetchEnrolledLearners(enterpriseId, {}, { csv: true })
),
Expand All @@ -141,7 +142,7 @@ class Admin extends React.Component {
defaultMessage: 'Learners who have completed all of their courses and/or courses have ended.',
description: 'Report description for learners not enrolled in an active course',
}),
component: <EnrolledLearnersForInactiveCoursesTable />,
component: <EnrolledLearnersForInactiveCoursesTable id="enrolled-learners-inactive-courses" />,
csvFetchMethod: () => (
EnterpriseDataApiService.fetchEnrolledLearnersForInactiveCourses(
enterpriseId,
Expand Down Expand Up @@ -220,7 +221,7 @@ class Admin extends React.Component {
defaultMessage: 'Number of Courses Completed by Learner',
description: 'Report title for number of courses completed by learners',
}),
component: <CompletedLearnersTable />,
component: <CompletedLearnersTable id="completed-learners" />,
csvFetchMethod: () => (
EnterpriseDataApiService.fetchCompletedLearners(enterpriseId, {}, { csv: true })
),
Expand All @@ -237,7 +238,7 @@ class Admin extends React.Component {
defaultMessage: 'Past Week',
description: 'Report title for number of courses completed by learners in past week',
}),
component: <PastWeekPassedLearnersTable />,
component: <PastWeekPassedLearnersTable id="completed-learners-week" />,
csvFetchMethod: () => (
EnterpriseDataApiService.fetchCourseEnrollments(
enterpriseId,
Expand All @@ -264,18 +265,18 @@ class Admin extends React.Component {
return tableData && tableData.data;
}

displaySearchBar() {
return !this.props.actionSlug;
}

isTableDataMissing(id) {
isTableDataMissing = (id) => {
const tableData = this.getTableData(id);
if (!tableData) {
return true;
}
const isTableLoading = tableData.loading;
const isTableEmpty = tableData.results && !tableData.results.length;
return isTableLoading || isTableEmpty;
};

displaySearchBar() {
return !this.props.actionSlug;
}

hasAnalyticsData() {
Expand Down Expand Up @@ -313,11 +314,11 @@ class Admin extends React.Component {
}

return (
<DownloadCsvButton
id={tableMetadata.csvButtonId}
fetchMethod={tableMetadata.csvFetchMethod}
disabled={this.isTableDataMissing(actionSlug)}
buttonLabel={downloadButtonLabel}
<DownloadButtonWrapper
tableMetadata={tableMetadata}
actionSlug={actionSlug}
downloadButtonLabel={downloadButtonLabel}
isTableDataMissing={this.isTableDataMissing}
/>
);
}
Expand Down Expand Up @@ -528,24 +529,25 @@ class Admin extends React.Component {
/>
)}
</div>
<Tabs
variant="tabs"
activeKey={activeTab}
onSelect={(tab) => {
this.setState({ activeTab: tab });
}}
>
<Tab
eventKey="learner-progress-report"
title={intl.formatMessage({
id: 'adminPortal.lpr.tab.learnerProgressReport.title',
defaultMessage: 'Learner Progress Report',
description: 'Title for the learner progress report tab in admin portal.',
})}
<TableDataProvider>
<Tabs
variant="tabs"
activeKey={activeTab}
onSelect={(tab) => {
this.setState({ activeTab: tab });
}}
>
<div className="row">
<div className="col">
{!error && !loading && !this.hasEmptyData() && (
<Tab
eventKey="learner-progress-report"
title={intl.formatMessage({
id: 'adminPortal.lpr.tab.learnerProgressReport.title',
defaultMessage: 'Learner Progress Report',
description: 'Title for the learner progress report tab in admin portal.',
})}
>
<div className="row">
<div className="col">
{!error && !loading && !this.hasEmptyData() && (
<>
<div className="row pb-3 mt-2">
<div className="col-12 col-md-12 col-xl-12">
Expand All @@ -563,27 +565,28 @@ class Admin extends React.Component {
/>
)}
</>
)}
{csvErrorMessage && this.renderCsvErrorMessage(csvErrorMessage)}
<div className="mt-3 mb-5">
{enterpriseId && tableMetadata.component}
)}
{csvErrorMessage && this.renderCsvErrorMessage(csvErrorMessage)}
<div className="mt-3 mb-5">
{enterpriseId && tableMetadata.component}
</div>
</div>
</div>
</div>
</Tab>
<Tab
eventKey="module-activity"
title={intl.formatMessage({
id: 'adminPortal.lpr.tab.moduleActivity.title',
defaultMessage: 'Module Activity (Executive Education)',
description: 'Title for the module activity tab in admin portal.',
})}
>
<div className="mt-3">
<ModuleActivityReport enterpriseId={enterpriseId} />
</div>
</Tab>
</Tabs>
</Tab>
<Tab
eventKey="module-activity"
title={intl.formatMessage({
id: 'adminPortal.lpr.tab.moduleActivity.title',
defaultMessage: 'Module Activity (Executive Education)',
description: 'Title for the module activity tab in admin portal.',
})}
>
<div className="mt-3">
<ModuleActivityReport enterpriseId={enterpriseId} />
</div>
</Tab>
</Tabs>
</TableDataProvider>
</div>
</div>
</>
Expand Down
Loading