Skip to content

Commit 6d2369c

Browse files
Student Info Page: Can delete students from a course. Can block students from purchases (#60)
* Individual student route; block students from purchasing * Delete student button with confirmation
1 parent 4d9bc41 commit 6d2369c

File tree

7 files changed

+452
-4
lines changed

7 files changed

+452
-4
lines changed

graphql.schema.json

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3697,6 +3697,120 @@
36973697
"isDeprecated": false,
36983698
"deprecationReason": null
36993699
},
3700+
{
3701+
"name": "removeStudent",
3702+
"description": null,
3703+
"args": [
3704+
{
3705+
"name": "course",
3706+
"description": null,
3707+
"type": {
3708+
"kind": "NON_NULL",
3709+
"name": null,
3710+
"ofType": {
3711+
"kind": "SCALAR",
3712+
"name": "String",
3713+
"ofType": null
3714+
}
3715+
},
3716+
"defaultValue": null,
3717+
"isDeprecated": false,
3718+
"deprecationReason": null
3719+
},
3720+
{
3721+
"name": "student",
3722+
"description": null,
3723+
"type": {
3724+
"kind": "NON_NULL",
3725+
"name": null,
3726+
"ofType": {
3727+
"kind": "SCALAR",
3728+
"name": "String",
3729+
"ofType": null
3730+
}
3731+
},
3732+
"defaultValue": null,
3733+
"isDeprecated": false,
3734+
"deprecationReason": null
3735+
}
3736+
],
3737+
"type": {
3738+
"kind": "NON_NULL",
3739+
"name": null,
3740+
"ofType": {
3741+
"kind": "SCALAR",
3742+
"name": "String",
3743+
"ofType": null
3744+
}
3745+
},
3746+
"isDeprecated": false,
3747+
"deprecationReason": null
3748+
},
3749+
{
3750+
"name": "blockStudentPurchases",
3751+
"description": null,
3752+
"args": [
3753+
{
3754+
"name": "course",
3755+
"description": null,
3756+
"type": {
3757+
"kind": "NON_NULL",
3758+
"name": null,
3759+
"ofType": {
3760+
"kind": "SCALAR",
3761+
"name": "String",
3762+
"ofType": null
3763+
}
3764+
},
3765+
"defaultValue": null,
3766+
"isDeprecated": false,
3767+
"deprecationReason": null
3768+
},
3769+
{
3770+
"name": "student",
3771+
"description": null,
3772+
"type": {
3773+
"kind": "NON_NULL",
3774+
"name": null,
3775+
"ofType": {
3776+
"kind": "SCALAR",
3777+
"name": "String",
3778+
"ofType": null
3779+
}
3780+
},
3781+
"defaultValue": null,
3782+
"isDeprecated": false,
3783+
"deprecationReason": null
3784+
},
3785+
{
3786+
"name": "blocked",
3787+
"description": null,
3788+
"type": {
3789+
"kind": "NON_NULL",
3790+
"name": null,
3791+
"ofType": {
3792+
"kind": "SCALAR",
3793+
"name": "Boolean",
3794+
"ofType": null
3795+
}
3796+
},
3797+
"defaultValue": null,
3798+
"isDeprecated": false,
3799+
"deprecationReason": null
3800+
}
3801+
],
3802+
"type": {
3803+
"kind": "NON_NULL",
3804+
"name": null,
3805+
"ofType": {
3806+
"kind": "OBJECT",
3807+
"name": "Student",
3808+
"ofType": null
3809+
}
3810+
},
3811+
"isDeprecated": false,
3812+
"deprecationReason": null
3813+
},
37003814
{
37013815
"name": "refundPurchase",
37023816
"description": null,
@@ -7625,6 +7739,22 @@
76257739
},
76267740
"isDeprecated": false,
76277741
"deprecationReason": null
7742+
},
7743+
{
7744+
"name": "purchaseBlocked",
7745+
"description": null,
7746+
"args": [],
7747+
"type": {
7748+
"kind": "NON_NULL",
7749+
"name": null,
7750+
"ofType": {
7751+
"kind": "SCALAR",
7752+
"name": "Boolean",
7753+
"ofType": null
7754+
}
7755+
},
7756+
"isDeprecated": false,
7757+
"deprecationReason": null
76287758
}
76297759
],
76307760
"inputFields": null,

src/Components/Content/Content.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MarketHomeInstructor from '../MarketHome/MarketHome';
55
import { CourseInfoFieldsFragment, Role, useUserQuery } from '../../__generated__/types';
66
import { Settings } from '../Settings/Settings';
77
import { MarketHomeStudent } from '../MarketHome/Student/MarketHomeStudent';
8+
import { StudentInfoPage } from '../MarketHome/StudentInfoPage';
89

910
type Props = {
1011
courses: CourseInfoFieldsFragment[];
@@ -26,6 +27,9 @@ export default function Content({ courses, refetchCourses }: Props) {
2627
<Route path="/courseHome/:classId/:className">
2728
{getUser.role === Role.Instructor ? <MarketHomeInstructor /> : <MarketHomeStudent />}
2829
</Route>
30+
<Route path="/student/:classId/:studentId">
31+
{getUser.role === Role.Instructor ? <StudentInfoPage /> : <>Forbidden</>}
32+
</Route>
2933
<Route path="/settings">
3034
<Settings user={getUser} />
3135
</Route>

src/Components/MarketHome/MarketHome.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const useStyles = makeStyles((theme: Theme) =>
3030
function MarketHome() {
3131
const classes = useStyles();
3232

33-
const [value, setValue] = useState('2');
33+
const [value, setValue] = useState('3');
3434

3535
const handleChange = (event: React.ChangeEvent<Record<string, unknown>>, newValue: string) => {
3636
setValue(newValue);
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* eslint-disable react/jsx-curly-brace-presence */
2+
import { Button, Checkbox } from '@material-ui/core';
3+
import { useEffect, useState } from 'react';
4+
import { useHistory, useParams } from 'react-router-dom';
5+
import Dialog from '@material-ui/core/Dialog';
6+
import DialogActions from '@material-ui/core/DialogActions';
7+
import DialogContent from '@material-ui/core/DialogContent';
8+
import DialogContentText from '@material-ui/core/DialogContentText';
9+
import DialogTitle from '@material-ui/core/DialogTitle';
10+
import {
11+
useBlockPurchasesMutation,
12+
useDeleteStudentMutation,
13+
useStudentQuery,
14+
} from '../../__generated__/types';
15+
16+
export function StudentInfoPage() {
17+
const { studentId, classId } = useParams<Record<string, string>>();
18+
const [checked, setChecked] = useState(true);
19+
const [open, setOpen] = useState(false);
20+
const history = useHistory();
21+
22+
const [blockPurchases] = useBlockPurchasesMutation({});
23+
24+
const handleClickOpen = () => {
25+
setOpen(true);
26+
};
27+
28+
const handleClose = () => {
29+
setOpen(false);
30+
};
31+
32+
const onDeleteCompleted = () => {
33+
console.log('student deleted!');
34+
handleClose();
35+
history.goBack();
36+
};
37+
const [deleteStudent] = useDeleteStudentMutation({ onCompleted: onDeleteCompleted });
38+
39+
const handleDeleteStudent = () => {
40+
deleteStudent({
41+
variables: {
42+
studentId,
43+
courseId: classId,
44+
},
45+
}).catch((e) => console.log(e));
46+
};
47+
48+
const handleChange = () => {
49+
blockPurchases({
50+
variables: {
51+
studentId,
52+
courseId: classId,
53+
blocked: !checked,
54+
},
55+
}).catch((e) => console.log(e));
56+
setChecked(!checked);
57+
};
58+
const { loading, error, data } = useStudentQuery({
59+
variables: {
60+
studentId,
61+
courseId: classId,
62+
},
63+
fetchPolicy: 'network-only',
64+
});
65+
66+
useEffect(() => {
67+
if (!data) {
68+
return;
69+
}
70+
setChecked(data.student.purchaseBlocked);
71+
}, [data]);
72+
73+
if (loading) return <div>Loading...</div>;
74+
if (error) {
75+
console.log(error);
76+
return <>{error}</>;
77+
}
78+
if (!data) {
79+
return <>Data could not be retrieved</>;
80+
}
81+
82+
const { student } = data;
83+
return (
84+
<div>
85+
<h3>
86+
{student.firstName} {student.lastName}
87+
</h3>
88+
<p>User Id: {student.studentId}</p>
89+
<p>Points: {student.points}</p>
90+
<p>Total Points Earned: {student.totalPointsAwarded}</p>
91+
<p>Total Points Spent: {student.totalPointsSpent}</p>
92+
<div>
93+
Purchases Blocked
94+
<Checkbox
95+
checked={checked}
96+
onChange={handleChange}
97+
inputProps={{ 'aria-label': 'primary checkbox' }}
98+
/>
99+
</div>
100+
<Button
101+
style={{
102+
width: 100,
103+
marginTop: 20,
104+
backgroundColor: '#DC143C',
105+
color: 'white',
106+
}}
107+
onClick={handleClickOpen}
108+
data-testid="create-btn"
109+
>
110+
Delete Student
111+
</Button>
112+
<Dialog
113+
open={open}
114+
onClose={handleClose}
115+
aria-labelledby="alert-dialog-title"
116+
aria-describedby="alert-dialog-description"
117+
>
118+
<DialogTitle id="alert-dialog-title">{'Delete student?'}</DialogTitle>
119+
<DialogContent>
120+
<DialogContentText id="alert-dialog-description">
121+
All purchases, receipts, point data, and activity notifications for this student
122+
will be removed. This action can not be undone.
123+
</DialogContentText>
124+
</DialogContent>
125+
<DialogActions>
126+
<Button onClick={handleClose} color="primary">
127+
Cancel
128+
</Button>
129+
<Button onClick={handleDeleteStudent} color="primary" autoFocus>
130+
Delete Student
131+
</Button>
132+
</DialogActions>
133+
</Dialog>
134+
</div>
135+
);
136+
}

src/Components/MarketHome/StudentsTab.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { CircularProgress } from '@material-ui/core';
33

44
import { useEffect, useMemo, useState } from 'react';
5-
import { useParams } from 'react-router-dom';
5+
import { useHistory, useParams } from 'react-router-dom';
66
import { StudentInfoFragment, useStudentsQuery } from '../../__generated__/types';
77
import TableComponent from '../TableComponent/TableComponent';
88
import { AddStudentDialog } from './AddStudentDialog';
@@ -14,6 +14,8 @@ type StudentRow = {
1414
points: number;
1515
earned: number;
1616
spent: number;
17+
id: string;
18+
purchaseBlocked: string;
1719
};
1820
};
1921

@@ -23,6 +25,7 @@ const sortStudents = (a: StudentInfoFragment, b: StudentInfoFragment) => {
2325
export function StudentsTab() {
2426
const { classId } = useParams<Record<string, string>>();
2527
const [students, setStudents] = useState<StudentInfoFragment[]>([]);
28+
const history = useHistory();
2629

2730
const editStudents = (updated: StudentInfoFragment[]) => {
2831
const items = [...students];
@@ -42,6 +45,7 @@ export function StudentsTab() {
4245
variables: {
4346
courseId: classId,
4447
},
48+
fetchPolicy: 'network-only',
4549
});
4650

4751
useEffect(() => {
@@ -52,7 +56,7 @@ export function StudentsTab() {
5256
}, [data]);
5357

5458
const rowClicked = (studentRow: StudentRow) => {
55-
console.log(`${studentRow.row.name} Clicked`);
59+
history.push(`/student/${classId}/${studentRow.row.id}`);
5660
};
5761

5862
const columns = useMemo(
@@ -73,6 +77,10 @@ export function StudentsTab() {
7377
Header: 'Spent',
7478
accessor: 'row.spent',
7579
},
80+
{
81+
Header: 'Purchases Blocked',
82+
accessor: 'row.purchaseBlocked',
83+
},
7684
],
7785
[]
7886
);
@@ -96,6 +104,8 @@ export function StudentsTab() {
96104
points: student.points,
97105
spent: student.totalPointsSpent,
98106
earned: student.totalPointsAwarded,
107+
id: student.studentId,
108+
purchaseBlocked: student.purchaseBlocked ? 'X' : '',
99109
},
100110
};
101111
});

0 commit comments

Comments
 (0)