Skip to content

Commit ca27af2

Browse files
authored
[feature] added TaskSubmissionSummaryView (#44)
* added TaskListView * complete TaskSubmissionSummaryView
1 parent 26152e4 commit ca27af2

File tree

10 files changed

+461
-22
lines changed

10 files changed

+461
-22
lines changed

graphql.schema.json

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4757,6 +4757,55 @@
47574757
"isDeprecated": false,
47584758
"deprecationReason": null
47594759
},
4760+
{
4761+
"name": "taskSubmissionSummary",
4762+
"description": null,
4763+
"args": [
4764+
{
4765+
"name": "course",
4766+
"description": null,
4767+
"type": {
4768+
"kind": "NON_NULL",
4769+
"name": null,
4770+
"ofType": {
4771+
"kind": "SCALAR",
4772+
"name": "String",
4773+
"ofType": null
4774+
}
4775+
},
4776+
"defaultValue": null,
4777+
"isDeprecated": false,
4778+
"deprecationReason": null
4779+
},
4780+
{
4781+
"name": "taskId",
4782+
"description": null,
4783+
"type": {
4784+
"kind": "NON_NULL",
4785+
"name": null,
4786+
"ofType": {
4787+
"kind": "SCALAR",
4788+
"name": "String",
4789+
"ofType": null
4790+
}
4791+
},
4792+
"defaultValue": null,
4793+
"isDeprecated": false,
4794+
"deprecationReason": null
4795+
}
4796+
],
4797+
"type": {
4798+
"kind": "NON_NULL",
4799+
"name": null,
4800+
"ofType": {
4801+
"kind": "OBJECT",
4802+
"name": "TaskSubmissionSummary",
4803+
"ofType": null
4804+
}
4805+
},
4806+
"isDeprecated": false,
4807+
"deprecationReason": null
4808+
},
47604809
{
47614810
"name": "quizblock",
47624811
"description": null,
@@ -6591,6 +6640,105 @@
65916640
"enumValues": null,
65926641
"possibleTypes": null
65936642
},
6643+
{
6644+
"kind": "OBJECT",
6645+
"name": "StudentTaskSubmissionResult",
6646+
"description": null,
6647+
"fields": [
6648+
{
6649+
"name": "studentName",
6650+
"description": null,
6651+
"args": [],
6652+
"type": {
6653+
"kind": "NON_NULL",
6654+
"name": null,
6655+
"ofType": {
6656+
"kind": "SCALAR",
6657+
"name": "String",
6658+
"ofType": null
6659+
}
6660+
},
6661+
"isDeprecated": false,
6662+
"deprecationReason": null
6663+
},
6664+
{
6665+
"name": "studentId",
6666+
"description": null,
6667+
"args": [],
6668+
"type": {
6669+
"kind": "NON_NULL",
6670+
"name": null,
6671+
"ofType": {
6672+
"kind": "SCALAR",
6673+
"name": "String",
6674+
"ofType": null
6675+
}
6676+
},
6677+
"isDeprecated": false,
6678+
"deprecationReason": null
6679+
},
6680+
{
6681+
"name": "pointsAwarded",
6682+
"description": "Todo\nThe pointsAwarded is calculated on fly when a single task submission is queried. Here we are querying a list of task submission, the pointsAwarded would not be accurate since we are not recalcuated it.",
6683+
"args": [],
6684+
"type": {
6685+
"kind": "SCALAR",
6686+
"name": "Int",
6687+
"ofType": null
6688+
},
6689+
"isDeprecated": false,
6690+
"deprecationReason": null
6691+
},
6692+
{
6693+
"name": "graded",
6694+
"description": null,
6695+
"args": [],
6696+
"type": {
6697+
"kind": "NON_NULL",
6698+
"name": null,
6699+
"ofType": {
6700+
"kind": "SCALAR",
6701+
"name": "Boolean",
6702+
"ofType": null
6703+
}
6704+
},
6705+
"isDeprecated": false,
6706+
"deprecationReason": null
6707+
},
6708+
{
6709+
"name": "teacherComment",
6710+
"description": null,
6711+
"args": [],
6712+
"type": {
6713+
"kind": "SCALAR",
6714+
"name": "String",
6715+
"ofType": null
6716+
},
6717+
"isDeprecated": false,
6718+
"deprecationReason": null
6719+
},
6720+
{
6721+
"name": "submitted",
6722+
"description": null,
6723+
"args": [],
6724+
"type": {
6725+
"kind": "NON_NULL",
6726+
"name": null,
6727+
"ofType": {
6728+
"kind": "SCALAR",
6729+
"name": "Boolean",
6730+
"ofType": null
6731+
}
6732+
},
6733+
"isDeprecated": false,
6734+
"deprecationReason": null
6735+
}
6736+
],
6737+
"inputFields": null,
6738+
"interfaces": [],
6739+
"enumValues": null,
6740+
"possibleTypes": null
6741+
},
65946742
{
65956743
"kind": "OBJECT",
65966744
"name": "SubGoal",
@@ -8325,6 +8473,57 @@
83258473
"enumValues": null,
83268474
"possibleTypes": null
83278475
},
8476+
{
8477+
"kind": "OBJECT",
8478+
"name": "TaskSubmissionSummary",
8479+
"description": null,
8480+
"fields": [
8481+
{
8482+
"name": "task",
8483+
"description": null,
8484+
"args": [],
8485+
"type": {
8486+
"kind": "NON_NULL",
8487+
"name": null,
8488+
"ofType": {
8489+
"kind": "OBJECT",
8490+
"name": "Task",
8491+
"ofType": null
8492+
}
8493+
},
8494+
"isDeprecated": false,
8495+
"deprecationReason": null
8496+
},
8497+
{
8498+
"name": "results",
8499+
"description": null,
8500+
"args": [],
8501+
"type": {
8502+
"kind": "NON_NULL",
8503+
"name": null,
8504+
"ofType": {
8505+
"kind": "LIST",
8506+
"name": null,
8507+
"ofType": {
8508+
"kind": "NON_NULL",
8509+
"name": null,
8510+
"ofType": {
8511+
"kind": "OBJECT",
8512+
"name": "StudentTaskSubmissionResult",
8513+
"ofType": null
8514+
}
8515+
}
8516+
}
8517+
},
8518+
"isDeprecated": false,
8519+
"deprecationReason": null
8520+
}
8521+
],
8522+
"inputFields": null,
8523+
"interfaces": [],
8524+
"enumValues": null,
8525+
"possibleTypes": null
8526+
},
83288527
{
83298528
"kind": "OBJECT",
83308529
"name": "TextBlock",

src/Components/Content/Content.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import SingleMissionOverview from '../SingleStudentOverview/SingleMissionOvervie
1212

1313
import TaskView from '../../Screens/TaskView/TaskView';
1414
import TaskListView from '../TaskListView';
15+
import TaskSubmissionSummaryView from '../TaskSubmissionSummaryView';
1516
import { ClassMastery } from '../../Screens/ClassMastery';
1617
import './Content.css';
1718

@@ -49,7 +50,10 @@ export default function Content() {
4950
<Route path="/taskList">
5051
<TaskListView />
5152
</Route>
52-
<Route path="/viewTask/:taskId">
53+
<Route path="/taskSubmissionSummary/:taskId">
54+
<TaskSubmissionSummaryView />
55+
</Route>
56+
<Route path="/viewTask/:taskId/:username">
5357
<TaskView />
5458
</Route>
5559
<Route path="/classMastery">

src/Components/TaskListView/TaskListTable.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,19 @@ import { TaskListTaskFieldsFragment } from '../../__generated__/types';
33

44
import './TaskListTable.css';
55

6-
type HeadProps = {
7-
headers: Array<string>;
8-
};
9-
106
type RowProps = {
117
data: Array<TaskListTaskFieldsFragment>;
128
};
139

14-
type TaskTableProps = HeadProps & RowProps;
10+
type TaskTableProps = RowProps;
1511

16-
const TaskListTableHead = ({ headers }: HeadProps): JSX.Element => {
12+
const TaskListTableHead = (): JSX.Element => {
1713
return (
1814
<thead>
1915
<tr>
20-
{headers.map((header) => (
21-
<th>{header}</th>
22-
))}
16+
<th>Task Name</th>
17+
<th>Instructions</th>
18+
<th>Submissions</th>
2319
</tr>
2420
</thead>
2521
);
@@ -34,7 +30,7 @@ const TaskListTableBody = ({ data }: RowProps): JSX.Element => {
3430
<td className="cell-view">
3531
<Link
3632
to={{
37-
pathname: `/viewTask/${task.id}`,
33+
pathname: `/taskSubmissionSummary/${task.id}`,
3834
}}
3935
>
4036
view
@@ -47,10 +43,10 @@ const TaskListTableBody = ({ data }: RowProps): JSX.Element => {
4743
return <tbody>{rows}</tbody>;
4844
};
4945

50-
const TaskListTable = ({ headers: columns, data }: TaskTableProps): JSX.Element => {
46+
const TaskListTable = ({ data }: TaskTableProps): JSX.Element => {
5147
return (
5248
<table>
53-
<TaskListTableHead headers={columns} />
49+
<TaskListTableHead />
5450
<TaskListTableBody data={data} />
5551
</table>
5652
);

src/Components/TaskListView/TaskListView.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,20 @@ function TaskList() {
88
},
99
});
1010

11-
const tasks = data?.tasksByCourse || [];
12-
const headers = ['Task Name', 'Instructions', 'Submissions'];
11+
let tasks = data?.tasksByCourse || [];
12+
13+
// sort by task name
14+
tasks = [...tasks].sort((a, b) => {
15+
if (a.name === b.name) {
16+
return 0;
17+
}
18+
19+
return a.name < b.name ? -1 : 1;
20+
});
1321

1422
return (
1523
<div className="base-table">
16-
<TaskListTable headers={headers} data={tasks} />
24+
<TaskListTable data={tasks} />
1725
</div>
1826
);
1927
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { useHistory } from 'react-router-dom';
2+
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
3+
import CancelIcon from '@material-ui/icons/Cancel';
4+
import { SummaryStudentResultFieldsFragment } from '../../__generated__/types';
5+
6+
type RowProps = {
7+
taskId: string;
8+
results: Array<SummaryStudentResultFieldsFragment>;
9+
};
10+
11+
type TaskTableProps = RowProps;
12+
13+
const TaskSubmissionSummaryTableHead = (): JSX.Element => {
14+
return (
15+
<thead>
16+
<tr>
17+
<td>Student</td>
18+
<td>Submitted</td>
19+
<td>Graded</td>
20+
<td>Points Awarded</td>
21+
</tr>
22+
</thead>
23+
);
24+
};
25+
26+
const TaskSubmissionSummaryTableBody = ({ taskId, results }: RowProps): JSX.Element => {
27+
const history = useHistory();
28+
29+
const rows = results.map((result) => {
30+
const Icon = result.submitted ? CheckCircleIcon : CancelIcon;
31+
const color = result.submitted ? 'primary' : 'disabled';
32+
33+
return (
34+
<tr
35+
key={result.studentId}
36+
className="hoverRow"
37+
onClick={() => {
38+
if (!result.submitted) {
39+
return;
40+
}
41+
history.push(`/viewTask/${taskId}/${result.studentId}`);
42+
}}
43+
>
44+
<td>{result.studentName}</td>
45+
<td>
46+
<Icon color={color} />
47+
</td>
48+
<td>{result.graded ? 'Graded' : 'Not Graded'}</td>
49+
<td>{result.pointsAwarded}</td>
50+
</tr>
51+
);
52+
});
53+
54+
return <tbody>{rows}</tbody>;
55+
};
56+
57+
const TaskSubmissionSummaryTable = ({ taskId, results }: TaskTableProps): JSX.Element => {
58+
return (
59+
<table>
60+
<TaskSubmissionSummaryTableHead />
61+
<TaskSubmissionSummaryTableBody taskId={taskId} results={results} />
62+
</table>
63+
);
64+
};
65+
66+
export default TaskSubmissionSummaryTable;

0 commit comments

Comments
 (0)