diff --git a/.env.development b/.env.development
index f18f7ad9..4dd7f8c2 100644
--- a/.env.development
+++ b/.env.development
@@ -20,3 +20,4 @@ ACCOUNT_PROFILE_URL='http://localhost:1995/u'
MFE_CONFIG_API_URL='http://localhost:18000/api/mfe_config/v1'
INSTITUTION_PORTAL_PATH=''
COURSE_OPERATIONS_API_V2_BASE_URL='http://localhost:18000/pearson_course_operation/api/v2'
+COURSE_OPERATIONS_API_METRICS_BASE_URL='http://localhost:18000/pearson_course_operation/api/metrics'
diff --git a/src/features/Students/StudentsMetrics/index.jsx b/src/features/Students/StudentsMetrics/index.jsx
index 7556da3c..011f9c88 100644
--- a/src/features/Students/StudentsMetrics/index.jsx
+++ b/src/features/Students/StudentsMetrics/index.jsx
@@ -2,17 +2,23 @@ import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Card, CardGrid, ToggleButton } from '@edx/paragon';
import { ToggleButtonGroup } from 'react-paragon-topaz';
-import { fetchMetricsData } from 'features/Students/data/thunks';
+import { fetchClassesMetricsData, fetchStudentsMetricsData } from 'features/Students/data/thunks';
+import { daysWeek } from 'features/constants';
import 'features/Students/StudentsMetrics/index.scss';
const StudentsMetrics = () => {
const dispatch = useDispatch();
- const stateMetrics = useSelector((state) => state.students.metrics.data);
+ const institution = useSelector((state) => state.main.selectedInstitution);
+ const studentsMetrics = useSelector((state) => state.students.studentsMetrics.data);
+ const classesMetrics = useSelector((state) => state.students.classesMetrics.data);
useEffect(() => {
- dispatch(fetchMetricsData());
- }, [dispatch]);
+ if (Object.keys(institution).length > 0) {
+ dispatch(fetchStudentsMetricsData(institution.id, daysWeek));
+ dispatch(fetchClassesMetricsData(institution.id, daysWeek));
+ }
+ }, [institution, dispatch]);
return (
@@ -20,10 +26,10 @@ const StudentsMetrics = () => {
This week
-
+ {/* Temporarily disabled */}
Next week
-
+ {/* Temporarily disabled */}
Next month
@@ -39,7 +45,9 @@ const StudentsMetrics = () => {
title="New students registered"
/>
- {stateMetrics.newStudentsRegistered}
+
+ {studentsMetrics.numberOfEnrollments ? studentsMetrics.numberOfEnrollments : '-'}
+
@@ -47,7 +55,9 @@ const StudentsMetrics = () => {
title="Classes scheduled"
/>
- {stateMetrics.classesScheduled}
+
+ {classesMetrics.numberOfClassesCreated ? classesMetrics.numberOfClassesCreated : '-'}
+
diff --git a/src/features/Students/StudentsPage/_test_/index.test.jsx b/src/features/Students/StudentsPage/_test_/index.test.jsx
index 59a090a4..06cbfc8f 100644
--- a/src/features/Students/StudentsPage/_test_/index.test.jsx
+++ b/src/features/Students/StudentsPage/_test_/index.test.jsx
@@ -45,8 +45,19 @@ const mockStore = {
num_pages: 1,
current_page: 1,
},
- metrics: {
- data: [],
+ classesMetrics: {
+ data: [
+ {
+ numberOfClassesCreated: 10,
+ },
+ ],
+ },
+ studentsMetrics: {
+ data: [
+ {
+ numberOfEnrollments: 2,
+ },
+ ],
},
classes: {
data: [],
@@ -75,6 +86,8 @@ describe('StudentsPage', () => {
expect(component.container).toHaveTextContent('Instructor 2');
expect(component.container).toHaveTextContent('active');
expect(component.container).toHaveTextContent('pending');
+ expect(component.container).toHaveTextContent('10');
+ expect(component.container).toHaveTextContent('2');
});
});
});
diff --git a/src/features/Students/data/_test_/redux.test.js b/src/features/Students/data/_test_/redux.test.js
index 9f232a40..50347425 100644
--- a/src/features/Students/data/_test_/redux.test.js
+++ b/src/features/Students/data/_test_/redux.test.js
@@ -5,7 +5,8 @@ import {
fetchStudentsData,
fetchCoursesData,
fetchClassesData,
- fetchMetricsData,
+ fetchClassesMetricsData,
+ fetchStudentsMetricsData,
} from 'features/Students/data/thunks';
import { updateCurrentPage, updateFilters } from 'features/Students/data/slice';
import { executeThunk } from 'test-utils';
@@ -170,24 +171,78 @@ describe('Students redux tests', () => {
.toEqual('error');
});
- test('successful fetch metrics data', async () => {
- const mockResponse = {
- classesScheduled: '71%',
- newStudentsRegistered: '367',
- };
+ test('successful fetch classes metrics data', async () => {
+ const classesMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`;
+ const mockResponse = { numberOfClassesCreated: 71 };
+ axiosMock.onGet(classesMetricsApiUrl)
+ .reply(200, mockResponse);
- expect(store.getState().students.metrics.status)
+ expect(store.getState().students.classesMetrics.status)
.toEqual('loading');
- await executeThunk(fetchMetricsData(), store.dispatch, store.getState);
+ await executeThunk(fetchClassesMetricsData(1, 2), store.dispatch, store.getState);
- expect(store.getState().students.metrics.data)
+ expect(store.getState().students.classesMetrics.data)
.toEqual(mockResponse);
- expect(store.getState().students.metrics.status)
+ expect(store.getState().students.classesMetrics.status)
.toEqual('success');
});
+ test('failed fetch classes metrics data', async () => {
+ const classesMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`;
+
+ axiosMock.onGet(classesMetricsApiUrl)
+ .reply(500);
+
+ expect(store.getState().students.classesMetrics.status)
+ .toEqual('loading');
+
+ await executeThunk(fetchClassesMetricsData(1, 2), store.dispatch, store.getState);
+
+ expect(store.getState().students.classesMetrics.data)
+ .toEqual([]);
+
+ expect(store.getState().students.classesMetrics.status)
+ .toEqual('error');
+ });
+
+ test('successful fetch students metrics data', async () => {
+ const studentsMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`;
+ const mockResponse = { numberOfEnrollments: 20 };
+ axiosMock.onGet(studentsMetricsApiUrl)
+ .reply(200, mockResponse);
+
+ expect(store.getState().students.studentsMetrics.status)
+ .toEqual('loading');
+
+ await executeThunk(fetchStudentsMetricsData(1, 2), store.dispatch, store.getState);
+
+ expect(store.getState().students.studentsMetrics.data)
+ .toEqual(mockResponse);
+
+ expect(store.getState().students.studentsMetrics.status)
+ .toEqual('success');
+ });
+
+ test('failed fetch students metrics data', async () => {
+ const studentsMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`;
+
+ axiosMock.onGet(studentsMetricsApiUrl)
+ .reply(500);
+
+ expect(store.getState().students.studentsMetrics.status)
+ .toEqual('loading');
+
+ await executeThunk(fetchStudentsMetricsData(1, 2), store.dispatch, store.getState);
+
+ expect(store.getState().students.studentsMetrics.data)
+ .toEqual([]);
+
+ expect(store.getState().students.studentsMetrics.status)
+ .toEqual('error');
+ });
+
test('update current page', () => {
const newPage = 2;
const intialState = store.getState().students.table;
diff --git a/src/features/Students/data/api.js b/src/features/Students/data/api.js
index 1d277935..a425a1cc 100644
--- a/src/features/Students/data/api.js
+++ b/src/features/Students/data/api.js
@@ -24,18 +24,33 @@ function handleEnrollments(data, courseId) {
);
}
-function getMetricsStudents() {
- const metricsData = {
- data: {
- new_students_registered: '367',
- classes_scheduled: '71%',
- },
+function getStudentsMetrics(institutionId, days) {
+ const params = {
+ institution_id: institutionId,
+ days,
};
- return metricsData;
+
+ return getAuthenticatedHttpClient().get(
+ `${getConfig().COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`,
+ { params },
+ );
+}
+
+function getClassesMetrics(institutionId, days) {
+ const params = {
+ institution_id: institutionId,
+ days,
+ };
+
+ return getAuthenticatedHttpClient().get(
+ `${getConfig().COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`,
+ { params },
+ );
}
export {
getStudentbyInstitutionAdmin,
handleEnrollments,
- getMetricsStudents,
+ getStudentsMetrics,
+ getClassesMetrics,
};
diff --git a/src/features/Students/data/slice.js b/src/features/Students/data/slice.js
index 4234404d..973134be 100644
--- a/src/features/Students/data/slice.js
+++ b/src/features/Students/data/slice.js
@@ -17,7 +17,10 @@ const initialState = {
classes: {
...initialStateService,
},
- metrics: {
+ classesMetrics: {
+ ...initialStateService,
+ },
+ studentsMetrics: {
...initialStateService,
},
filters: {},
@@ -66,15 +69,25 @@ export const studentsSlice = createSlice({
fetchClassesDataFailed: (state) => {
state.classes.status = RequestStatus.ERROR;
},
- fetchMetricsDataRequest: (state) => {
- state.metrics.status = RequestStatus.LOADING;
+ fetchClassesMetricsDataRequest: (state) => {
+ state.classesMetrics.status = RequestStatus.LOADING;
+ },
+ fetchClassesMetricsDataSuccess: (state, { payload }) => {
+ state.classesMetrics.status = RequestStatus.SUCCESS;
+ state.classesMetrics.data = payload;
+ },
+ fetchClassesMetricsDataFailed: (state) => {
+ state.classesMetrics.status = RequestStatus.ERROR;
+ },
+ fetchStudentsMetricsDataRequest: (state) => {
+ state.studentsMetrics.status = RequestStatus.LOADING;
},
- fetchMetricsDataSuccess: (state, { payload }) => {
- state.metrics.status = RequestStatus.SUCCESS;
- state.metrics.data = payload;
+ fetchStudentsMetricsDataSuccess: (state, { payload }) => {
+ state.studentsMetrics.status = RequestStatus.SUCCESS;
+ state.studentsMetrics.data = payload;
},
- fetchMetricsDataFailed: (state) => {
- state.metrics.status = RequestStatus.ERROR;
+ fetchStudentsMetricsDataFailed: (state) => {
+ state.studentsMetrics.status = RequestStatus.ERROR;
},
},
});
@@ -91,9 +104,12 @@ export const {
fetchClassesDataRequest,
fetchClassesDataSuccess,
fetchClassesDataFailed,
- fetchMetricsDataRequest,
- fetchMetricsDataSuccess,
- fetchMetricsDataFailed,
+ fetchClassesMetricsDataRequest,
+ fetchClassesMetricsDataSuccess,
+ fetchClassesMetricsDataFailed,
+ fetchStudentsMetricsDataRequest,
+ fetchStudentsMetricsDataSuccess,
+ fetchStudentsMetricsDataFailed,
} = studentsSlice.actions;
export const { reducer } = studentsSlice;
diff --git a/src/features/Students/data/thunks.js b/src/features/Students/data/thunks.js
index db8510cd..9507901a 100644
--- a/src/features/Students/data/thunks.js
+++ b/src/features/Students/data/thunks.js
@@ -10,11 +10,18 @@ import {
fetchClassesDataRequest,
fetchClassesDataSuccess,
fetchClassesDataFailed,
- fetchMetricsDataRequest,
- fetchMetricsDataSuccess,
- fetchMetricsDataFailed,
+ fetchClassesMetricsDataRequest,
+ fetchClassesMetricsDataSuccess,
+ fetchClassesMetricsDataFailed,
+ fetchStudentsMetricsDataRequest,
+ fetchStudentsMetricsDataSuccess,
+ fetchStudentsMetricsDataFailed,
} from 'features/Students/data/slice';
-import { getStudentbyInstitutionAdmin, getMetricsStudents } from 'features/Students/data/api';
+import {
+ getClassesMetrics,
+ getStudentsMetrics,
+ getStudentbyInstitutionAdmin,
+} from 'features/Students/data/api';
import { getCoursesByInstitution, getClassesByInstitution } from 'features/Common/data/api';
function fetchStudentsData(id, currentPage, filtersData) {
@@ -59,15 +66,29 @@ function fetchClassesData(id, courseName) {
};
}
-function fetchMetricsData() {
+function fetchClassesMetricsData(institutionId, days) {
+ return async (dispatch) => {
+ dispatch(fetchClassesMetricsDataRequest());
+
+ try {
+ const response = camelCaseObject(await getClassesMetrics(institutionId, days));
+ dispatch(fetchClassesMetricsDataSuccess(response.data));
+ } catch (error) {
+ dispatch(fetchClassesMetricsDataFailed());
+ logError(error);
+ }
+ };
+}
+
+function fetchStudentsMetricsData(institutionId, days) {
return async (dispatch) => {
- dispatch(fetchMetricsDataRequest());
+ dispatch(fetchStudentsMetricsDataRequest());
try {
- const response = camelCaseObject(await getMetricsStudents());
- dispatch(fetchMetricsDataSuccess(response.data));
+ const response = camelCaseObject(await getStudentsMetrics(institutionId, days));
+ dispatch(fetchStudentsMetricsDataSuccess(response.data));
} catch (error) {
- dispatch(fetchMetricsDataFailed());
+ dispatch(fetchStudentsMetricsDataFailed());
logError(error);
}
};
@@ -77,5 +98,6 @@ export {
fetchStudentsData,
fetchCoursesData,
fetchClassesData,
- fetchMetricsData,
+ fetchClassesMetricsData,
+ fetchStudentsMetricsData,
};