Skip to content

Commit f8ba357

Browse files
rrtheonlyoneremo5000
authored andcommitted
Set-up grading table to allow for batch grading (#342)
* Added Pagination and checkbox to show all submissions * Fix formatting * Updated API calls to get submissions for group/all * Refactored code to allow for a clean API call to fetch groups * Updated mock backend to simulate group filtering behaviour * Updating formatting * Updated Documentation explaning group param
1 parent 6a827cc commit f8ba357

File tree

6 files changed

+68
-15
lines changed

6 files changed

+68
-15
lines changed

src/actions/session.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ export const fetchGrading = (submissionId: number) => ({
3030
payload: submissionId
3131
})
3232

33-
export const fetchGradingOverviews = () => ({
34-
type: actionTypes.FETCH_GRADING_OVERVIEWS
33+
/**
34+
* @param filterToGroup - param when set to true, only shows submissions under the group
35+
* of the grader
36+
*/
37+
export const fetchGradingOverviews = (filterToGroup = true) => ({
38+
type: actionTypes.FETCH_GRADING_OVERVIEWS,
39+
payload: filterToGroup
3540
})
3641

3742
export const login = () => ({

src/components/academy/grading/index.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Colors, InputGroup, NonIdealState, Spinner } from '@blueprintjs/core'
1+
import { Colors, FormGroup, InputGroup, NonIdealState, Spinner } from '@blueprintjs/core'
22
import { IconNames } from '@blueprintjs/icons'
33
import { ColDef, GridApi, GridReadyEvent } from 'ag-grid'
44
import { AgGridReact } from 'ag-grid-react'
@@ -24,6 +24,7 @@ import { OwnProps as GradingWorkspaceProps } from './GradingWorkspace'
2424
type State = {
2525
columnDefs: ColDef[]
2626
filterValue: string
27+
groupFilterEnabled: boolean
2728
}
2829

2930
type GradingNavLinkProps = {
@@ -41,7 +42,7 @@ export interface IGradingWorkspaceParams {
4142
}
4243

4344
export interface IDispatchProps {
44-
handleFetchGradingOverviews: () => void
45+
handleFetchGradingOverviews: (filterToGroup?: boolean) => void
4546
}
4647

4748
export interface IStateProps {
@@ -104,7 +105,9 @@ class Grading extends React.Component<IGradingProps, State> {
104105
{ headerName: 'Max XP', field: 'maxXp', hide: true }
105106
],
106107

107-
filterValue: ''
108+
filterValue: '',
109+
110+
groupFilterEnabled: false
108111
}
109112
}
110113

@@ -138,17 +141,30 @@ class Grading extends React.Component<IGradingProps, State> {
138141
const grid = (
139142
<div className="GradingContainer">
140143
<div>
141-
<div className="col-md-6 col-md-offset-3">
144+
<FormGroup label="Filter:" labelFor="text-input" inline={true}>
142145
<InputGroup
146+
id="filterBar"
143147
large={false}
144148
leftIcon="filter"
145-
placeholder="Filter..."
149+
placeholder="Enter any text(e.g. mission)"
146150
value={this.state.filterValue}
147151
onChange={this.handleFilterChange}
148152
/>
153+
</FormGroup>
154+
155+
<div className="checkboxPanel">
156+
<label>Show All Submissions:</label>
157+
&nbsp;&nbsp;
158+
<input
159+
name="showAllSubmissions"
160+
type="checkbox"
161+
checked={this.state.groupFilterEnabled}
162+
onChange={this.handleGroupsFilter}
163+
/>
149164
</div>
150165
</div>
151166

167+
<hr />
152168
<br />
153169

154170
<div className="Grading">
@@ -161,6 +177,8 @@ class Grading extends React.Component<IGradingProps, State> {
161177
columnDefs={this.state.columnDefs}
162178
onGridReady={this.onGridReady}
163179
rowData={data}
180+
pagination={true}
181+
paginationPageSize={50}
164182
/>
165183
</div>
166184
<div className="ag-grid-export-button">
@@ -187,6 +205,12 @@ class Grading extends React.Component<IGradingProps, State> {
187205
}
188206
}
189207

208+
private handleGroupsFilter = (event: React.ChangeEvent<HTMLInputElement>) => {
209+
const checkStatus = event.target.checked
210+
this.setState({ groupFilterEnabled: checkStatus })
211+
this.props.handleFetchGradingOverviews(!checkStatus)
212+
}
213+
190214
private onGridReady = (params: GridReadyEvent) => {
191215
this.gridApi = params.api
192216
this.gridApi.sizeColumnsToFit()

src/mocks/backend.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ export function* mockBackendSaga(): SagaIterator {
4242
yield put(actions.updateAssessment({ ...assessment }))
4343
})
4444

45-
yield takeEvery(actionTypes.FETCH_GRADING_OVERVIEWS, function*() {
45+
yield takeEvery(actionTypes.FETCH_GRADING_OVERVIEWS, function*(action) {
4646
const accessToken = yield select((state: IState) => state.session.accessToken)
47-
const gradingOverviews = yield call(() => mockFetchGradingOverview(accessToken))
47+
const filterToGroup = (action as actionTypes.IAction).payload
48+
const gradingOverviews = yield call(() => mockFetchGradingOverview(accessToken, filterToGroup))
4849
if (gradingOverviews !== null) {
4950
yield put(actions.updateGradingOverviews([...gradingOverviews]))
5051
}

src/mocks/gradingAPI.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ export const mockGradingOverviews: GradingOverview[] = [
5858
* A null value is returned for invalid token or role.
5959
*
6060
* @param accessToken a valid access token for the cadet backend.
61+
* @param group a boolean if true, only fetches submissions from the grader's group
6162
*/
62-
export const mockFetchGradingOverview = (accessToken: string): GradingOverview[] | null => {
63+
export const mockFetchGradingOverview = (
64+
accessToken: string,
65+
group: boolean
66+
): GradingOverview[] | null => {
6367
// mocks backend role fetching
6468
const permittedRoles: Role[] = [Roles.admin, Roles.trainer]
6569
const role: Role | null = mockFetchRole(accessToken)
6670
if (role === null || !permittedRoles.includes(role)) {
6771
return null
6872
} else {
69-
return mockGradingOverviews
73+
return group ? [mockGradingOverviews[0]] : mockGradingOverviews
7074
}
7175
}
7276

src/sagas/backend.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,15 @@ function* backendSaga(): SagaIterator {
173173
}
174174
})
175175

176-
yield takeEvery(actionTypes.FETCH_GRADING_OVERVIEWS, function*() {
176+
yield takeEvery(actionTypes.FETCH_GRADING_OVERVIEWS, function*(action) {
177177
const tokens = yield select((state: IState) => ({
178178
accessToken: state.session.accessToken,
179179
refreshToken: state.session.refreshToken
180180
}))
181-
const gradingOverviews = yield call(getGradingOverviews, tokens)
181+
182+
const filterToGroup = (action as actionTypes.IAction).payload
183+
184+
const gradingOverviews = yield call(getGradingOverviews, tokens, filterToGroup)
182185
if (gradingOverviews) {
183186
yield put(actions.updateGradingOverviews(gradingOverviews))
184187
}
@@ -404,10 +407,14 @@ async function postAssessment(id: number, tokens: Tokens): Promise<Response | nu
404407

405408
/*
406409
* GET /grading
410+
* @params group - a boolean if true gets the submissions from the grader's group
407411
* @returns {Array} GradingOverview[]
408412
*/
409-
async function getGradingOverviews(tokens: Tokens): Promise<GradingOverview[] | null> {
410-
const response = await request('grading', 'GET', {
413+
async function getGradingOverviews(
414+
tokens: Tokens,
415+
group: boolean
416+
): Promise<GradingOverview[] | null> {
417+
const response = await request(`grading?group=${group}`, 'GET', {
411418
accessToken: tokens.accessToken,
412419
refreshToken: tokens.refreshToken,
413420
shouldRefresh: true

src/styles/_academy.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,15 @@
213213
justify-content: center;
214214
text-align: center;
215215
}
216+
217+
#filterBar {
218+
width: 400px;
219+
}
220+
221+
.ag-paging-panel {
222+
padding: 20px;
223+
}
224+
225+
.checkboxPanel {
226+
text-align: left;
227+
}

0 commit comments

Comments
 (0)