Skip to content

Commit 63a08c9

Browse files
committed
coord intereface changes so far
1 parent 822da46 commit 63a08c9

File tree

4 files changed

+209
-6
lines changed

4 files changed

+209
-6
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React, { useState } from "react";
2+
3+
import { useCoordDeleteSectionMutation } from "../../utils/queries/coord";
4+
// import Modal from "../Modal";
5+
import XIcon from "../../../static/frontend/img/x.svg";
6+
7+
// import "../../css/student_dropper.scss";
8+
9+
interface CoordSectionDeleteProps {
10+
sectionId: number;
11+
}
12+
13+
export default function CoordSectionDelete({ sectionId }: CoordSectionDeleteProps) {
14+
const [showDropPrompt, setShowDropPrompt] = useState(false);
15+
const [drop, setDrop] = useState(false);
16+
const [ban, setBan] = useState(false);
17+
const [blacklist, setBlacklist] = useState(false);
18+
19+
/**
20+
* Mutation to drop a student from the section. (Inspiration taken from dropping students within sections)
21+
*/
22+
const coordSectionDeleteMutation = useCoordDeleteSectionMutation(sectionId);
23+
24+
function handleClickDrop() {
25+
coordSectionDeleteMutation.mutate({ banned: ban, blacklisted: blacklist });
26+
setShowDropPrompt(false);
27+
}
28+
29+
const dropDiv = (
30+
<div>
31+
<h2 className="student-dropper-head-item">DROP Student</h2>
32+
<div className="student-dropper-checkbox-container">
33+
<input type="checkbox" id="drop" name="drop" onChange={e => setDrop(e.target.checked)} />
34+
<label className="student-dropper-checkbox-label" htmlFor="drop">
35+
I would like to DELETE the section: {sectionId}.
36+
</label>
37+
<br></br>
38+
</div>
39+
</div>
40+
);
41+
42+
return (
43+
// <span className={`student-dropper ${showDropPrompt ? "ban-prompt-visible" : ""}`}>
44+
// <XIcon
45+
// className="icon inline-plus-sign"
46+
// title="Drop student from section"
47+
// onClick={() => setShowDropPrompt(true)}
48+
// />
49+
// <div>dropDiv</div>
50+
<button className="danger-btn" onClick={handleClickDrop} disabled={!drop}>
51+
Submit
52+
</button>
53+
// </span>
54+
);
55+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useState } from "react";
2+
3+
import { useCoordDropStudentMutation } from "../../utils/queries/coord";
4+
// import Modal from "../Modal";
5+
import XIcon from "../../../static/frontend/img/x.svg";
6+
7+
// import "../../css/student_dropper.scss";
8+
9+
interface CoordStudentDropperProps {
10+
// data: [];
11+
id: number;
12+
sectionId: number;
13+
name: string;
14+
courseRestricted: boolean;
15+
}
16+
17+
// id, sectionId, name, courseRestricted old props
18+
export default function CoordStudentDropper({ id, sectionId, name, courseRestricted }: CoordStudentDropperProps) {
19+
// need to find a way to change the selectedData into the format which we want for this
20+
// either make it so we take in a list and then decode it wtihin this component or do something
21+
const [showDropPrompt, setShowDropPrompt] = useState(false);
22+
const [drop, setDrop] = useState(false);
23+
const [ban, setBan] = useState(false);
24+
const [blacklist, setBlacklist] = useState(false);
25+
26+
// make a bunch of lists which each of the indices corresponds to a student (if there are null/empty values just add null or undefined?)
27+
28+
/**
29+
* Mutation to drop a student from the section. (Inspiration taken from dropping students within sections)
30+
*/
31+
const coordStudentDropMutation = useCoordDropStudentMutation(id, sectionId); // might want to do like a forEach within this?
32+
33+
function handleClickDrop() {
34+
coordStudentDropMutation.mutate({ banned: ban, blacklisted: blacklist });
35+
setShowDropPrompt(false);
36+
}
37+
38+
// this is just copied over... please look over and change later?
39+
const dropDiv = (
40+
<div>
41+
<h2 className="student-dropper-head-item">DELETE Section</h2>
42+
<div className="student-dropper-checkbox-container">
43+
<input type="checkbox" id="drop" name="drop" onChange={e => setDrop(e.target.checked)} />
44+
<label className="student-dropper-checkbox-label" htmlFor="drop">
45+
I would like to DROP {name} from this section.
46+
</label>
47+
<br></br>
48+
</div>
49+
</div>
50+
);
51+
52+
return (
53+
// <span className={`student-dropper ${showDropPrompt ? "ban-prompt-visible" : ""}`}>
54+
// <XIcon
55+
// className="icon inline-plus-sign"
56+
// title="Drop student from section"
57+
// onClick={() => setShowDropPrompt(true)}
58+
// />
59+
// <div>dropDiv</div>
60+
<button className="danger-btn" onClick={handleClickDrop} disabled={!drop}>
61+
Submit
62+
</button>
63+
// </span>
64+
);
65+
}

csm_web/frontend/src/components/coord_interface/CoordTable.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { useLocation, useParams, useNavigate } from "react-router-dom";
33
import { Mentor, Student, getCoordData } from "../../utils/queries/coord";
44
import ActionButton from "./ActionButton";
55
import { CheckBox } from "./CheckBox";
6+
import CoordSectionDelete from "./CoordSectionDelete";
7+
import CoordStudentDropper from "./CoordStudentDropper";
68
import DropBox from "./DropBox";
79
import { SearchBar } from "./SearchBar";
810
import styles from "../../css/coord_interface.scss";
@@ -196,10 +198,10 @@ export default function CoordTable() {
196198
}
197199

198200
// Debugging Function for seeing selected data
199-
// function getSelectedData() {
200-
// console.log(selectedData);
201-
// return selectedData;
202-
// }
201+
function getSelectedData() {
202+
console.log(selectedData);
203+
return selectedData;
204+
}
203205

204206
return (
205207
<div className={styles}>
@@ -246,6 +248,8 @@ export default function CoordTable() {
246248
<div id="table-header">
247249
{isStudents ? <div className="title">Students List</div> : <div className="title">Mentors List</div>}
248250
<ActionButton copyEmail={copyEmail} reset={reset} />
251+
{/* <button onClick={getSelectedData}></button> Used for looking at how the selectedData looks like */}
252+
{/* {isStudents ? <CoordStudentDropper/> : <CoordSectionDelete></CoordSectionDelete>} */}
249253
</div>
250254

251255
<table>

csm_web/frontend/src/utils/queries/coord.tsx

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { fetchNormalized } from "../api";
2-
import { handlePermissionsError, PermissionError, ServerError } from "./helpers";
1+
import { useMutation, UseMutationResult, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query";
2+
import { fetchNormalized, fetchWithMethod, HTTP_METHODS } from "../api";
3+
import { handleError, handleRetry, handlePermissionsError, PermissionError, ServerError } from "./helpers";
34

45
export interface Student {
56
id: number;
@@ -34,3 +35,81 @@ export const getCoordData = async (courseId: number, isStudents: boolean) => {
3435
throw new ServerError(`Failed to fetch coord course ${courseId}`);
3536
}
3637
};
38+
39+
export interface StudentDropMutationBody {
40+
banned: boolean;
41+
blacklisted: boolean;
42+
}
43+
44+
/**
45+
* Hook to drop a student from their section though the coord interface.
46+
* Invalidates all queries associated with the section.
47+
* (insprition from /queries/sections.tsx)
48+
*/
49+
50+
export const useCoordDropStudentMutation = (
51+
studentId: number,
52+
sectionId: number
53+
): UseMutationResult<void, ServerError, StudentDropMutationBody> => {
54+
const queryClient = useQueryClient();
55+
const mutationResult = useMutation<void, Error, StudentDropMutationBody>(
56+
async (body: StudentDropMutationBody) => {
57+
if (isNaN(studentId) || isNaN(sectionId)) {
58+
throw new PermissionError("Invalid section id");
59+
}
60+
const response = await fetchWithMethod(`coord/${studentId}/drop_students`, HTTP_METHODS.PATCH, body); // changed this to match the path within urls.py
61+
if (response.ok) {
62+
return;
63+
} else {
64+
handlePermissionsError(response.status);
65+
throw new ServerError(`Failed to drop student ${studentId} from section ${sectionId}`);
66+
}
67+
},
68+
{
69+
onSuccess: () => {
70+
// invalidate all queries for the section
71+
queryClient.invalidateQueries(["sections", sectionId]);
72+
},
73+
retry: handleRetry
74+
}
75+
);
76+
77+
handleError(mutationResult);
78+
return mutationResult;
79+
};
80+
81+
/**
82+
* Hook to delete a section though the coord interface.
83+
* Invalidates all queries associated with the section.
84+
* (insprition from /queries/sections.tsx, but there wasn't a delete section sooo)
85+
*/
86+
87+
export const useCoordDeleteSectionMutation = (
88+
sectionId: number
89+
): UseMutationResult<void, ServerError, StudentDropMutationBody> => {
90+
const queryClient = useQueryClient();
91+
const mutationResult = useMutation<void, Error, StudentDropMutationBody>(
92+
async (body: StudentDropMutationBody) => {
93+
if (isNaN(sectionId)) {
94+
throw new PermissionError("Invalid section id");
95+
}
96+
const response = await fetchWithMethod(`coord/${sectionId}/section`, HTTP_METHODS.PATCH, body); // changed this to match the path within urls.py
97+
if (response.ok) {
98+
return;
99+
} else {
100+
handlePermissionsError(response.status);
101+
throw new ServerError(`Failed to delete section ${sectionId}`);
102+
}
103+
},
104+
{
105+
onSuccess: () => {
106+
// invalidate all queries for the section
107+
queryClient.invalidateQueries(["sections", sectionId]); // might still need this?
108+
},
109+
retry: handleRetry
110+
}
111+
);
112+
113+
handleError(mutationResult);
114+
return mutationResult;
115+
};

0 commit comments

Comments
 (0)