generated from openedx/frontend-template-application
-
Notifications
You must be signed in to change notification settings - Fork 0
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
1 parent
fd6acf8
commit bde4b17
Showing
13 changed files
with
291 additions
and
2 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
|
||
import Container from '@edx/paragon/dist/Container'; | ||
import { Pagination } from '@edx/paragon'; | ||
import ClassesTable from 'features/Classes/ClassesTable'; | ||
|
||
import { updateCurrentPage } from 'features/Classes/data/slice'; | ||
import { fetchClassesData } from 'features/Classes/data/thunks'; | ||
import { initialPage } from 'features/constants'; | ||
|
||
const ClassesPage = () => { | ||
const selectedInstitution = useSelector((state) => state.main.selectedInstitution); | ||
const stateClasses = useSelector((state) => state.classes); | ||
const dispatch = useDispatch(); | ||
const [currentPage, setCurrentPage] = useState(initialPage); | ||
|
||
useEffect(() => { | ||
if (Object.keys(selectedInstitution).length > 0) { | ||
dispatch(fetchClassesData(selectedInstitution.id, currentPage)); | ||
} | ||
}, [currentPage, selectedInstitution, dispatch]); // eslint-disable-line react-hooks/exhaustive-deps | ||
|
||
const handlePagination = (targetPage) => { | ||
setCurrentPage(targetPage); | ||
dispatch(updateCurrentPage(targetPage)); | ||
}; | ||
|
||
return ( | ||
<Container size="xl" className="px-4"> | ||
<h2 className="title-page">Classes</h2> | ||
<div className="page-content-container"> | ||
<ClassesTable | ||
data={stateClasses.table.data} | ||
count={stateClasses.table.count} | ||
/> | ||
{stateClasses.table.numPages > 1 && ( | ||
<Pagination | ||
paginationLabel="paginationNavigation" | ||
pageCount={stateClasses.table.numPages} | ||
currentPage={currentPage} | ||
onPageSelect={handlePagination} | ||
variant="reduced" | ||
className="mx-auto pagination-table" | ||
size="small" | ||
/> | ||
)} | ||
</div> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default ClassesPage; |
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,55 @@ | ||
import React from 'react'; | ||
import CoursesPage from 'features/Courses/CoursesPage'; | ||
import { waitFor } from '@testing-library/react'; | ||
import '@testing-library/jest-dom/extend-expect'; | ||
import { renderWithProviders } from 'test-utils'; | ||
|
||
jest.mock('@edx/frontend-platform/logging', () => ({ | ||
logError: jest.fn(), | ||
})); | ||
|
||
const mockStore = { | ||
courses: { | ||
table: { | ||
data: [ | ||
{ | ||
masterCourseName: 'Demo Course 1', | ||
numberOfClasses: 1, | ||
missingClassesForInstructor: null, | ||
numberOfStudents: 1, | ||
numberOfPendingStudents: 1, | ||
}, | ||
{ | ||
masterCourseName: 'Demo Course 2', | ||
numberOfClasses: 1, | ||
missingClassesForInstructor: 1, | ||
numberOfStudents: 16, | ||
numberOfPendingStudents: 0, | ||
}, | ||
], | ||
count: 2, | ||
num_pages: 1, | ||
current_page: 1, | ||
}, | ||
}, | ||
}; | ||
|
||
describe('CoursesPage', () => { | ||
it('renders courses data and pagination', async () => { | ||
const component = renderWithProviders( | ||
<CoursesPage />, | ||
{ preloadedState: mockStore }, | ||
); | ||
|
||
waitFor(() => { | ||
expect(component.container).toHaveTextContent('Demo Course 1'); | ||
expect(component.container).toHaveTextContent('Demo Course 2'); | ||
expect(component.container).toHaveTextContent('Ready'); | ||
expect(component.container).toHaveTextContent('Missing (1)'); | ||
expect(component.container).toHaveTextContent('Pending (1)'); | ||
expect(component.container).toHaveTextContent('Complete'); | ||
expect(component.container).toHaveTextContent('1/2'); | ||
expect(component.container).toHaveTextContent('16/16'); | ||
}); | ||
}); | ||
}); |
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 @@ | ||
/* eslint-disable react/prop-types, no-nested-ternary */ | ||
import React from 'react'; | ||
import { format } from 'date-fns'; | ||
|
||
const columns = [ | ||
{ | ||
Header: 'Course', | ||
accessor: 'masterCourseName', | ||
}, | ||
{ | ||
Header: 'Class', | ||
accessor: 'className', | ||
}, | ||
{ | ||
Header: 'Start Date', | ||
accessor: 'startDate', | ||
Cell: ({ row }) => (row.values.startDate ? format(row.values.startDate, 'MM/dd/yy') : ''), | ||
}, | ||
{ | ||
Header: 'End Date', | ||
accessor: 'endDate', | ||
Cell: ({ row }) => (row.values.endDate ? format(row.values.endDate, 'MM/dd/yy') : ''), | ||
}, | ||
{ | ||
Header: 'Students Enrolled', | ||
accessor: 'numberOfStudents', | ||
}, | ||
{ | ||
Header: 'Max', | ||
accessor: 'maxStudents', | ||
}, | ||
{ | ||
Header: 'Instructors', | ||
accessor: ({ instructors }) => ( | ||
<ul style={{ listStyleType: 'none', paddingLeft: 0 }}> | ||
{instructors.map(instructor => <li key={instructor}>{`${instructor}`}</li>)} | ||
</ul> | ||
), | ||
}, | ||
]; | ||
|
||
export { columns }; |
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,43 @@ | ||
import React, { useMemo } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { IntlProvider } from 'react-intl'; | ||
import { Row, Col } from '@edx/paragon'; | ||
import DataTable from '@edx/paragon/dist/DataTable'; | ||
|
||
import { columns } from 'features/Classes/ClassesTable/columns'; | ||
|
||
const ClassesTable = ({ data, count }) => { | ||
const COLUMNS = useMemo(() => columns, []); | ||
|
||
return ( | ||
<IntlProvider locale="en"> | ||
<Row className="justify-content-center my-4 my-3"> | ||
<Col xs={11} className="p-0"> | ||
<DataTable | ||
isSortable | ||
columns={COLUMNS} | ||
itemCount={count} | ||
data={data} | ||
> | ||
<DataTable.Table /> | ||
<DataTable.EmptyTable content="No classes found." /> | ||
<DataTable.TableFooter /> | ||
</DataTable> | ||
</Col> | ||
</Row> | ||
</IntlProvider> | ||
); | ||
}; | ||
|
||
ClassesTable.propTypes = { | ||
data: PropTypes.arrayOf(PropTypes.shape([])), | ||
count: PropTypes.number, | ||
}; | ||
|
||
ClassesTable.defaultProps = { | ||
data: [], | ||
count: 0, | ||
}; | ||
|
||
export default ClassesTable; |
Empty file.
Empty file.
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,2 @@ | ||
export { reducer } from 'features/Classes/data/slice'; | ||
export { fetchClassesData } from 'features/Classes/data/thunks'; |
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,51 @@ | ||
/* eslint-disable no-param-reassign */ | ||
import { createSlice } from '@reduxjs/toolkit'; | ||
import { RequestStatus } from 'features/constants'; | ||
|
||
const initialState = { | ||
table: { | ||
currentPage: 1, | ||
data: [], | ||
status: RequestStatus.LOADING, | ||
error: null, | ||
numPages: 0, | ||
count: 0, | ||
}, | ||
filters: {}, | ||
}; | ||
|
||
export const classesSlice = createSlice({ | ||
name: 'classes', | ||
initialState, | ||
reducers: { | ||
updateCurrentPage: (state, { payload }) => { | ||
state.table.currentPage = payload; | ||
}, | ||
fetchClassesDataRequest: (state) => { | ||
state.table.status = RequestStatus.LOADING; | ||
}, | ||
fetchClassesDataSuccess: (state, { payload }) => { | ||
const { results, count, numPages } = payload; | ||
state.table.status = RequestStatus.SUCCESS; | ||
state.table.data = results; | ||
state.table.numPages = numPages; | ||
state.table.count = count; | ||
}, | ||
fetchClassesDataFailed: (state) => { | ||
state.table.status = RequestStatus.ERROR; | ||
}, | ||
updateFilters: (state, { payload }) => { | ||
state.filters = payload; | ||
}, | ||
}, | ||
}); | ||
|
||
export const { | ||
updateCurrentPage, | ||
fetchClassesDataRequest, | ||
fetchClassesDataSuccess, | ||
fetchClassesDataFailed, | ||
updateFilters, | ||
} = classesSlice.actions; | ||
|
||
export const { reducer } = classesSlice; |
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,26 @@ | ||
import { logError } from '@edx/frontend-platform/logging'; | ||
import { camelCaseObject } from '@edx/frontend-platform'; | ||
import { | ||
fetchClassesDataRequest, | ||
fetchClassesDataSuccess, | ||
fetchClassesDataFailed, | ||
} from 'features/Classes/data/slice'; | ||
import { getClassesByInstitution } from 'features/Common/data/api'; | ||
|
||
function fetchClassesData(id, currentPage) { | ||
return async (dispatch) => { | ||
dispatch(fetchClassesDataRequest); | ||
|
||
try { | ||
const response = camelCaseObject(await getClassesByInstitution(id, '', true, '', currentPage)); | ||
dispatch(fetchClassesDataSuccess(response.data)); | ||
} catch (error) { | ||
dispatch(fetchClassesDataFailed()); | ||
logError(error); | ||
} | ||
}; | ||
} | ||
|
||
export { | ||
fetchClassesData, | ||
}; |
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
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