From 7e436ccc8cbf8e2cf5680faac892d198da1ec017 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:45:11 -0400 Subject: [PATCH 01/12] added filetype to jsx and added notes for implementing mass add/drop logic --- .../pages/forms/courses/courseUpdatePage.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 5d3787d..fd40ea0 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -86,10 +86,15 @@ const CourseUpdatePage = ({ }) => { }) } + // const handleFileUpload = () => { + // if file uploaded parse file to grab all student emails + // note: accepted filetype (enforced by frontend) is csv + // for each email parsed from file, call handle add or drop student as needed + // } + const handleAddStudent = () => { // TODO: get user id by getting email and calling /users --> search through /users --> - // RequestService.put(`/api/courses/${courseId}/users-courses/${id}:`, - + // RequestService.post(`/api/courses/${courseId}/users-courses/${id}:`, } const handleDropStudent = () => { @@ -132,7 +137,9 @@ const CourseUpdatePage = ({ }) => {

Add/Drop Students

- + + {/* csv should be a good standard filetype */} +
From 64b6c462fcd14ca123d73b30877ddd0ad6c963df Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:07:12 -0400 Subject: [PATCH 02/12] removed isAuthorized('admin') from user.router.ts --- devU-api/src/entities/user/user.router.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devU-api/src/entities/user/user.router.ts b/devU-api/src/entities/user/user.router.ts index 67ab230..c0dbcfe 100644 --- a/devU-api/src/entities/user/user.router.ts +++ b/devU-api/src/entities/user/user.router.ts @@ -19,7 +19,8 @@ const Router = express.Router() * '200': * description: OK */ -Router.get('/', isAuthorized('admin'), UserController.get) +Router.get('/', /*isAuthorized('admin'),*/ UserController.get) +// Router.get('/', isAuthorized('admin'), UserController.get) /** * @swagger From 4d6ce119fde13577e862f1d7a0ad6015d31f1b29 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:18:28 -0400 Subject: [PATCH 03/12] wrote logic to handle adding/dropping single student --- .../pages/forms/courses/courseUpdatePage.tsx | 106 +++++++++++++++--- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index fd40ea0..b2702b0 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -4,14 +4,12 @@ import { useHistory, useParams } from 'react-router-dom' import PageWrapper from 'components/shared/layouts/pageWrapper' import RequestService from 'services/request.service' -// import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { ExpressValidationError } from 'devu-shared-modules' import { useActionless } from 'redux/hooks' import TextField from 'components/shared/inputs/textField' -// import Button from '@mui/material/Button' import { SET_ALERT } from 'redux/types/active.types' import { applyMessageToErrorFields, @@ -20,10 +18,21 @@ import { import formStyles from './coursesFormPage.scss' + type UrlParams = { courseId: string } +type User = { + id?: number + externalId: string // School's unique identifier (the thing that links to the schools auth) + email: string + createdAt?: string + updatedAt?: string + preferredName?: string +} + + const CourseUpdatePage = ({ }) => { const [setAlert] = useActionless(SET_ALERT) const history = useHistory() @@ -34,6 +43,7 @@ const CourseUpdatePage = ({ }) => { }) const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) + const [studentEmail, setStudentEmail] = useState("") const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -52,12 +62,21 @@ const CourseUpdatePage = ({ }) => { }); } }, []); - const handleChange = (value: String, e: React.ChangeEvent) => { + + const handleChange = (value: string, e: React.ChangeEvent) => { const key = e.target.id const newInvalidFields = removeClassFromField(invalidFields, key) setInvalidFields(newInvalidFields) - setFormData(prevState => ({ ...prevState, [key]: value })) + // setFormData(prevState => ({ ...prevState, [key]: value })) + + // Update form data based on input field + if (key === 'studentEmail') { + setStudentEmail(value) + } else { + setFormData(prevState => ({ ...prevState, [key]: value })) + } } + const handleStartDateChange = (event: React.ChangeEvent) => { setStartDate(event.target.value) } const handleEndDateChange = (event: React.ChangeEvent) => { setEndDate(event.target.value) } @@ -87,19 +106,74 @@ const CourseUpdatePage = ({ }) => { } // const handleFileUpload = () => { - // if file uploaded parse file to grab all student emails - // note: accepted filetype (enforced by frontend) is csv - // for each email parsed from file, call handle add or drop student as needed + // if file uploaded parse file to grab all student emails + // note: accepted filetype (enforced by frontend) is csv + // for each email parsed from file, call handle add or drop student as needed // } + const getUserId = (email: string): number | null => { + RequestService.get("/api/users/") + .then((res) => { + const user: User = res.data.find((user: User) => user.email === email); + if (user) { + return user.id + } + }) + .catch((error) => { + console.error("Error fetching users:", error); + }); + return null + } + + const addSingleStudent = (email: string) => { + const userID = getUserId(email) + if (!userID) { return } + + const userCourseData = { + userId: userID, + courseId: courseId, + role: 'student', + dropped: false + } + + RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData) + .catch((error: Error) => { + const message = error.message + setAlert({ autoDelete: false, type: 'error', message }) + }).catch((error: Error) => { + const message = error.message + setAlert({ autoDelete: false, type: 'error', message }) + }).finally(() => { + setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) + }) + + } + const handleAddStudent = () => { - // TODO: get user id by getting email and calling /users --> search through /users --> - // RequestService.post(`/api/courses/${courseId}/users-courses/${id}:`, + // TODO: if file inputted then parse csv file to get emails and for each email addSingleStudent + // TODO: if no file inputted then addSingleStudent with email + addSingleStudent(studentEmail) } const handleDropStudent = () => { - - + // TODO: if file inputted then parse csv file to get emails and for each email dropSingleStudent + // TODO: if no file inputted then dropSingleStudent with email + dropSingleStudent(studentEmail) + } + + const dropSingleStudent = (email: string) => { + const userID = getUserId(email) + if (!userID) { return } + RequestService.delete(`/api/courses/${courseId}/users-courses/${userID}`) + .catch((error: Error) => { + const message = error.message + setAlert({ autoDelete: false, type: 'error', message }) + }).catch((error: Error) => { + const message = error.message + setAlert({ autoDelete: false, type: 'error', message }) + }).finally(() => { + setAlert({ autoDelete: true, type: 'success', message: `${email} dropped from course` }) + }) } @@ -122,11 +196,11 @@ const CourseUpdatePage = ({ }) => {
- +
- +
@@ -135,12 +209,12 @@ const CourseUpdatePage = ({ }) => {

Add/Drop Students

- + {/* csv should be a good standard filetype */} -
+
From 72f9d1dbff123feb9306d2e3a5124113bc18bd1a Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:25:51 -0400 Subject: [PATCH 04/12] added handle add/drop --- .../pages/forms/courses/courseUpdatePage.tsx | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index b2702b0..d158708 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -24,7 +24,7 @@ type UrlParams = { } type User = { - id?: number + id: number externalId: string // School's unique identifier (the thing that links to the schools auth) email: string createdAt?: string @@ -44,6 +44,7 @@ const CourseUpdatePage = ({ }) => { const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) const [studentEmail, setStudentEmail] = useState("") + const [studentID, setStudentID] = useState(0) const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -111,41 +112,68 @@ const CourseUpdatePage = ({ }) => { // for each email parsed from file, call handle add or drop student as needed // } - const getUserId = (email: string): number | null => { - RequestService.get("/api/users/") - .then((res) => { - const user: User = res.data.find((user: User) => user.email === email); - if (user) { - return user.id - } - }) - .catch((error) => { - console.error("Error fetching users:", error); - }); - return null + const getUserId = async (email: string): Promise => { + try { + const res: User[] = await RequestService.get("/api/users/"); + const user: User | undefined = res.find((user: User) => user.email === email); + + if (user) { + console.log("User found"); + console.log(user); + console.log(user.id); + setStudentID(user.id); + return true; + } else { + console.log("User not found"); + return false; + } + } catch (error) { + console.error("Error fetching users:", error); + return false; + } } - const addSingleStudent = (email: string) => { - const userID = getUserId(email) - if (!userID) { return } + const addSingleStudent = async (email: string) => { + const idFound = await getUserId(email) + + if (!idFound) { + console.log("userID not found") + setAlert({ autoDelete: false, type: 'error', message: "userID not found"}) + return + } const userCourseData = { - userId: userID, + userId: 1, courseId: courseId, role: 'student', dropped: false } - RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData) - .catch((error: Error) => { - const message = error.message - setAlert({ autoDelete: false, type: 'error', message }) - }).catch((error: Error) => { - const message = error.message - setAlert({ autoDelete: false, type: 'error', message }) - }).finally(() => { - setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) - }) + console.log("STUDENT ID BEFORE USER COURSE CALL: ", studentID) + + // RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData) + // .then(()=>{ + // setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) + // console.log("added") + // }) + // .catch((error: Error) => { + // const message = error.message + // setAlert({ autoDelete: false, type: 'error', message }) + // console.log(message) + // }).catch((error: Error) => { + // const message = error.message + // setAlert({ autoDelete: false, type: 'error', message }) + // console.log(message) + // }) + try { + await RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData); + setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }); + console.log("Added"); + } catch (error: any) { // Use any if the error type isn't strictly defined + const message = error.message || "An unknown error occurred"; + setAlert({ autoDelete: false, type: 'error', message }); + console.log(message); + } } From 0966fee51024389fb76999fcb1d015630316b944 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:49:25 -0400 Subject: [PATCH 05/12] got request to user-courses working --- .../pages/forms/courses/courseUpdatePage.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index d158708..7123acc 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -23,6 +23,7 @@ type UrlParams = { courseId: string } +// copied from devU-shared>src>types>user.types.ts and edited from id? to id type User = { id: number externalId: string // School's unique identifier (the thing that links to the schools auth) @@ -143,7 +144,7 @@ const CourseUpdatePage = ({ }) => { } const userCourseData = { - userId: 1, + userId: studentID, courseId: courseId, role: 'student', dropped: false @@ -151,22 +152,8 @@ const CourseUpdatePage = ({ }) => { console.log("STUDENT ID BEFORE USER COURSE CALL: ", studentID) - // RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData) - // .then(()=>{ - // setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) - // console.log("added") - // }) - // .catch((error: Error) => { - // const message = error.message - // setAlert({ autoDelete: false, type: 'error', message }) - // console.log(message) - // }).catch((error: Error) => { - // const message = error.message - // setAlert({ autoDelete: false, type: 'error', message }) - // console.log(message) - // }) try { - await RequestService.post(`/api/courses/${courseId}/users-courses`, userCourseData); + await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData); setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }); console.log("Added"); } catch (error: any) { // Use any if the error type isn't strictly defined From 70fee1679973f1e88b06cb0acbbc16159c421d2a Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:06:50 -0400 Subject: [PATCH 06/12] getUserID correctly returns userID, still not setting state properly --- .../pages/forms/courses/courseUpdatePage.tsx | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 7123acc..6c70c8f 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -23,7 +23,10 @@ type UrlParams = { courseId: string } -// copied from devU-shared>src>types>user.types.ts and edited from id? to id +/* +copied from devU-shared>src>types>user.types.ts and edited from id? to id +to ensure number type instead of number|undefined +*/ type User = { id: number externalId: string // School's unique identifier (the thing that links to the schools auth) @@ -46,6 +49,7 @@ const CourseUpdatePage = ({ }) => { const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) const [studentEmail, setStudentEmail] = useState("") const [studentID, setStudentID] = useState(0) + const [strvnum, setstr] = useState("") const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -69,7 +73,6 @@ const CourseUpdatePage = ({ }) => { const key = e.target.id const newInvalidFields = removeClassFromField(invalidFields, key) setInvalidFields(newInvalidFields) - // setFormData(prevState => ({ ...prevState, [key]: value })) // Update form data based on input field if (key === 'studentEmail') { @@ -113,36 +116,51 @@ const CourseUpdatePage = ({ }) => { // for each email parsed from file, call handle add or drop student as needed // } - const getUserId = async (email: string): Promise => { + const getUserId = async (email: string) => { + // default value 0 because userIDs start from 1 try { const res: User[] = await RequestService.get("/api/users/"); const user: User | undefined = res.find((user: User) => user.email === email); if (user) { console.log("User found"); - console.log(user); - console.log(user.id); + console.log("getUserId User: ", user); + console.log("getUserId user.id", user.id); + setstr("heehoo"); setStudentID(user.id); - return true; + return user.id; } else { console.log("User not found"); - return false; + return 0; } } catch (error) { console.error("Error fetching users:", error); - return false; + return 0; } } + useEffect(() => { + if (!studentID) { + console.log("userID not found"); + // setAlert({ autoDelete: false, type: 'error', message: "userID not found" }); + } else { + console.log("studentID after calling getUserID: ", studentID); + } + }, [studentID]); // Only run this effect when studentID changes + const addSingleStudent = async (email: string) => { - const idFound = await getUserId(email) + const id = await getUserId(email) + console.log("studendID after calling getUserID: ", studentID) + console.log("id after calling getUserID: ", id) - if (!idFound) { + if (!id) { console.log("userID not found") setAlert({ autoDelete: false, type: 'error', message: "userID not found"}) - return + return } + setStudentID(id); + const userCourseData = { userId: studentID, courseId: courseId, @@ -151,15 +169,17 @@ const CourseUpdatePage = ({ }) => { } console.log("STUDENT ID BEFORE USER COURSE CALL: ", studentID) + console.log("strvnum should be heehoo: ", strvnum) + // NOTE: strvnum isnt updating --> not updating state in the middle of function try { - await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData); - setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }); - console.log("Added"); + await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData) + setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) + console.log("Added") } catch (error: any) { // Use any if the error type isn't strictly defined const message = error.message || "An unknown error occurred"; - setAlert({ autoDelete: false, type: 'error', message }); - console.log(message); + setAlert({ autoDelete: false, type: 'error', message }) + console.log(message) } } From 257af3425491f7aaea42ddf658989b8f6b0a9308 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:29:30 -0400 Subject: [PATCH 07/12] user course request works for adding students, correct id value passed in --- .../pages/forms/courses/courseUpdatePage.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 6c70c8f..7b8d6c4 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -48,8 +48,7 @@ const CourseUpdatePage = ({ }) => { const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) const [studentEmail, setStudentEmail] = useState("") - const [studentID, setStudentID] = useState(0) - const [strvnum, setstr] = useState("") + const [studentID, setStudentID] = useState() const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -126,8 +125,7 @@ const CourseUpdatePage = ({ }) => { console.log("User found"); console.log("getUserId User: ", user); console.log("getUserId user.id", user.id); - setstr("heehoo"); - setStudentID(user.id); + // setStudentID(user.id.toString()); return user.id; } else { console.log("User not found"); @@ -162,15 +160,13 @@ const CourseUpdatePage = ({ }) => { setStudentID(id); const userCourseData = { - userId: studentID, + userId: id, courseId: courseId, role: 'student', dropped: false } - console.log("STUDENT ID BEFORE USER COURSE CALL: ", studentID) - console.log("strvnum should be heehoo: ", strvnum) - // NOTE: strvnum isnt updating --> not updating state in the middle of function + console.log("CALLING WITH USER ID: ", userCourseData.userId) try { await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData) @@ -181,7 +177,6 @@ const CourseUpdatePage = ({ }) => { setAlert({ autoDelete: false, type: 'error', message }) console.log(message) } - } const handleAddStudent = () => { From f2e594cdb36697a435371f51120281dd4dc2a8e1 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Fri, 25 Oct 2024 23:30:43 -0400 Subject: [PATCH 08/12] delete deletes a student: added _deleteUser function in userCourse.controller.ts and userCourse.router.ts that uses id param --- .../userCourse/userCourse.controller.ts | 21 ++++++++- .../entities/userCourse/userCourse.router.ts | 24 +++++++++- .../pages/forms/courses/courseUpdatePage.tsx | 46 ++++++------------- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/devU-api/src/entities/userCourse/userCourse.controller.ts b/devU-api/src/entities/userCourse/userCourse.controller.ts index 4c98af8..e40a848 100644 --- a/devU-api/src/entities/userCourse/userCourse.controller.ts +++ b/devU-api/src/entities/userCourse/userCourse.controller.ts @@ -115,6 +115,7 @@ export async function checkEnroll(req: Request, res: Response, next: NextFunctio export async function _delete(req: Request, res: Response, next: NextFunction) { try { const id = parseInt(req.params.courseId) + console.log("DELETE PARAMS: ", req.params) const currentUser = req.currentUser?.userId if (!currentUser) return res.status(401).json({ message: 'Unauthorized' }) @@ -128,4 +129,22 @@ export async function _delete(req: Request, res: Response, next: NextFunction) { } } -export default { get, getByCourse, getAll, detail, detailByUser, post, put, _delete, checkEnroll } +export async function _deleteUser(req: Request, res: Response, next: NextFunction) { + try { + const courseID = parseInt(req.params.courseId) + console.log("DELETE PARAMS2: ", req.params) + // const currentUser = req.currentUser?.userId + const userID = parseInt(req.params.id) + if (!userID) return res.status(401).json({ message: 'Unauthorized' }) + + const results = await UserCourseService._delete(courseID, userID) + + if (!results.affected) return res.status(404).json(NotFound) + + res.status(204).send() + } catch (err) { + next(err) + } +} + +export default { get, getByCourse, getAll, detail, detailByUser, post, put, _delete, _deleteUser, checkEnroll } diff --git a/devU-api/src/entities/userCourse/userCourse.router.ts b/devU-api/src/entities/userCourse/userCourse.router.ts index c781c49..4692914 100644 --- a/devU-api/src/entities/userCourse/userCourse.router.ts +++ b/devU-api/src/entities/userCourse/userCourse.router.ts @@ -128,9 +128,9 @@ Router.put('/:id', isAuthorized('userCourseEditAll'), asInt(), validator, UserCo /** * @swagger - * /course/:courseId/user-courses/{id}: + * /course/:courseId/user-courses: * delete: - * summary: Delete a user-course association + * summary: Delete a user-course association for current user * tags: * - UserCourses * responses: @@ -145,4 +145,24 @@ Router.put('/:id', isAuthorized('userCourseEditAll'), asInt(), validator, UserCo */ Router.delete('/', UserCourseController._delete) // TODO: eventually add authorization to this. For now, everyone can remove anyone + +/** + * @swagger + * /course/:courseId/user-courses/{id}: + * delete: + * summary: Delete a user-course association given a specific user + * tags: + * - UserCourses + * responses: + * '200': + * description: OK + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: integer + */ +Router.delete('/:id', UserCourseController._deleteUser) +// TODO: eventually add authorization to this. For now, everyone can remove anyone export default Router diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 7b8d6c4..5a87389 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -48,7 +48,7 @@ const CourseUpdatePage = ({ }) => { const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) const [studentEmail, setStudentEmail] = useState("") - const [studentID, setStudentID] = useState() + // const [studentID, setStudentID] = useState() const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -125,7 +125,6 @@ const CourseUpdatePage = ({ }) => { console.log("User found"); console.log("getUserId User: ", user); console.log("getUserId user.id", user.id); - // setStudentID(user.id.toString()); return user.id; } else { console.log("User not found"); @@ -137,28 +136,14 @@ const CourseUpdatePage = ({ }) => { } } - useEffect(() => { - if (!studentID) { - console.log("userID not found"); - // setAlert({ autoDelete: false, type: 'error', message: "userID not found" }); - } else { - console.log("studentID after calling getUserID: ", studentID); - } - }, [studentID]); // Only run this effect when studentID changes - const addSingleStudent = async (email: string) => { const id = await getUserId(email) - console.log("studendID after calling getUserID: ", studentID) - console.log("id after calling getUserID: ", id) if (!id) { - console.log("userID not found") setAlert({ autoDelete: false, type: 'error', message: "userID not found"}) return } - setStudentID(id); - const userCourseData = { userId: id, courseId: courseId, @@ -166,14 +151,12 @@ const CourseUpdatePage = ({ }) => { dropped: false } - console.log("CALLING WITH USER ID: ", userCourseData.userId) - try { await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData) setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) console.log("Added") } catch (error: any) { // Use any if the error type isn't strictly defined - const message = error.message || "An unknown error occurred"; + const message = error.message || "An unknown error occurred" setAlert({ autoDelete: false, type: 'error', message }) console.log(message) } @@ -191,21 +174,20 @@ const CourseUpdatePage = ({ }) => { dropSingleStudent(studentEmail) } - const dropSingleStudent = (email: string) => { - const userID = getUserId(email) + const dropSingleStudent = async (email: string) => { + const userID = await getUserId(email) if (!userID) { return } - RequestService.delete(`/api/courses/${courseId}/users-courses/${userID}`) - .catch((error: Error) => { - const message = error.message - setAlert({ autoDelete: false, type: 'error', message }) - }).catch((error: Error) => { - const message = error.message - setAlert({ autoDelete: false, type: 'error', message }) - }).finally(() => { - setAlert({ autoDelete: true, type: 'success', message: `${email} dropped from course` }) - }) - } + try { + await RequestService.delete(`/api/course/${courseId}/user-courses/${userID}`) + setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) + console.log("dropped") + } catch (error: any) { // Use any if the error type isn't strictly defined + const message = error.message || "An unknown error occurred" + setAlert({ autoDelete: false, type: 'error', message }) + console.log(message) + } + } return ( From 0c02779ba1d3cfdee7d6c4148a1444791c968184 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Fri, 25 Oct 2024 23:32:21 -0400 Subject: [PATCH 09/12] fixed error message for dropSingleStudent --- .../src/components/pages/forms/courses/courseUpdatePage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 5a87389..7433b7b 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -154,7 +154,6 @@ const CourseUpdatePage = ({ }) => { try { await RequestService.post(`/api/course/${courseId}/user-courses`, userCourseData) setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) - console.log("Added") } catch (error: any) { // Use any if the error type isn't strictly defined const message = error.message || "An unknown error occurred" setAlert({ autoDelete: false, type: 'error', message }) @@ -180,12 +179,10 @@ const CourseUpdatePage = ({ }) => { try { await RequestService.delete(`/api/course/${courseId}/user-courses/${userID}`) - setAlert({ autoDelete: true, type: 'success', message: `${email} added to course` }) - console.log("dropped") + setAlert({ autoDelete: true, type: 'success', message: `${email} dropped from course` }) } catch (error: any) { // Use any if the error type isn't strictly defined const message = error.message || "An unknown error occurred" setAlert({ autoDelete: false, type: 'error', message }) - console.log(message) } } From 6f77249ffb93c14761713d530c0c64acfd9df11f Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Sat, 26 Oct 2024 01:25:13 -0400 Subject: [PATCH 10/12] added functionality for adding several users through csv file --- .../pages/forms/courses/courseUpdatePage.tsx | 116 +++++++++++++----- 1 file changed, 87 insertions(+), 29 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 7433b7b..5c1caee 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -48,7 +48,7 @@ const CourseUpdatePage = ({ }) => { const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) const [studentEmail, setStudentEmail] = useState("") - // const [studentID, setStudentID] = useState() + const [emails, setEmails] = useState([]) const [invalidFields, setInvalidFields] = useState(new Map()) const { courseId } = useParams() as UrlParams @@ -67,12 +67,12 @@ const CourseUpdatePage = ({ }) => { }); } }, []); - + const handleChange = (value: string, e: React.ChangeEvent) => { const key = e.target.id const newInvalidFields = removeClassFromField(invalidFields, key) setInvalidFields(newInvalidFields) - + // Update form data based on input field if (key === 'studentEmail') { setStudentEmail(value) @@ -109,22 +109,64 @@ const CourseUpdatePage = ({ }) => { }) } - // const handleFileUpload = () => { - // if file uploaded parse file to grab all student emails - // note: accepted filetype (enforced by frontend) is csv - // for each email parsed from file, call handle add or drop student as needed - // } + // update value of file and update parsed values if file uploaded + const handleFileChange = (event: React.ChangeEvent) => { + const uploadedFile = event.target.files?.[0] || null; + if (uploadedFile) { + console.log("file uploaded") + handleFileUpload(uploadedFile); + } + }; + + // set array of parsed emails from csv file + const handleFileUpload = (uploadedFile: File) => { + const reader = new FileReader(); + reader.onload = (e) => { + const text = e.target?.result as string; + const parsedEmails = parseCSV(text); + setEmails(parsedEmails); // Store the parsed emails in state + }; + reader.readAsText(uploadedFile); // Read the file + }; + + // return array of emails + const parseCSV = (text: string): string[] => { + const lines = text.split('\n'); + const emails: string[] = []; + const headers = lines[0].toLowerCase().split(','); + + // Find the index of the email-related fields + const emailIndex = headers.findIndex(header => + ['email', 'e-mail', 'email address', 'e-mail address'].includes(header.trim()) + ); + + if (emailIndex === -1) { + console.error("Email field not found in CSV file"); + setAlert({ autoDelete: false, type: 'error', message: "Email field not found in CSV file" }) + return []; + } + + // Extract emails + for (let i = 1; i < lines.length; i++) { + const fields = lines[i].split(','); + const email = fields[emailIndex].trim(); + if (email) { + emails.push(email); + } + } + + console.log("Parsed emails: ", emails); + return emails; + }; const getUserId = async (email: string) => { - // default value 0 because userIDs start from 1 + // default return value 0 because userIDs start from 1 try { const res: User[] = await RequestService.get("/api/users/"); const user: User | undefined = res.find((user: User) => user.email === email); - + if (user) { console.log("User found"); - console.log("getUserId User: ", user); - console.log("getUserId user.id", user.id); return user.id; } else { console.log("User not found"); @@ -138,9 +180,9 @@ const CourseUpdatePage = ({ }) => { const addSingleStudent = async (email: string) => { const id = await getUserId(email) - - if (!id) { - setAlert({ autoDelete: false, type: 'error', message: "userID not found"}) + + if (!id) { + setAlert({ autoDelete: false, type: 'error', message: "userID not found" }) return } @@ -157,22 +199,9 @@ const CourseUpdatePage = ({ }) => { } catch (error: any) { // Use any if the error type isn't strictly defined const message = error.message || "An unknown error occurred" setAlert({ autoDelete: false, type: 'error', message }) - console.log(message) } } - const handleAddStudent = () => { - // TODO: if file inputted then parse csv file to get emails and for each email addSingleStudent - // TODO: if no file inputted then addSingleStudent with email - addSingleStudent(studentEmail) - } - - const handleDropStudent = () => { - // TODO: if file inputted then parse csv file to get emails and for each email dropSingleStudent - // TODO: if no file inputted then dropSingleStudent with email - dropSingleStudent(studentEmail) - } - const dropSingleStudent = async (email: string) => { const userID = await getUserId(email) if (!userID) { return } @@ -186,6 +215,35 @@ const CourseUpdatePage = ({ }) => { } } + const handleAddStudent = () => { + console.log("emails: ", emails); + if (emails.length<1) { + // if no file inputted then addSingleStudent with email + console.log("adding single user") + addSingleStudent(studentEmail) + } else { + // if file inputted then for each email parsed from csv addSingleStudent + console.log("adding multiple users") + emails.forEach(email => { + addSingleStudent(email) + }) + } + } + + const handleDropStudent = () => { + if (emails.length<1) { + // if no file inputted then dropSingleStudent with email + console.log("dropping single user") + dropSingleStudent(studentEmail) + } else { + // if file inputted then for each email parsed from csv dropSingleStudent + console.log("dropping multiple users") + emails.forEach(email => { + dropSingleStudent(email) + }) + } + } + return (

Update Course Form

@@ -222,7 +280,7 @@ const CourseUpdatePage = ({ }) => { placeholder='e.g. hartloff@buffalo.edu' invalidated={!!invalidFields.get("studentEmail")} helpText={invalidFields.get("studentEmail")} /> {/* csv should be a good standard filetype */} - +
From dbac57142902ab1f4bca996307803a776c59820f Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Sat, 26 Oct 2024 01:37:58 -0400 Subject: [PATCH 11/12] adding and dropping multiple users tested, works. adding and dropping single users tested, works. --- .../src/components/pages/forms/courses/courseUpdatePage.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 5c1caee..71e5f12 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -113,7 +113,6 @@ const CourseUpdatePage = ({ }) => { const handleFileChange = (event: React.ChangeEvent) => { const uploadedFile = event.target.files?.[0] || null; if (uploadedFile) { - console.log("file uploaded") handleFileUpload(uploadedFile); } }; @@ -166,7 +165,6 @@ const CourseUpdatePage = ({ }) => { const user: User | undefined = res.find((user: User) => user.email === email); if (user) { - console.log("User found"); return user.id; } else { console.log("User not found"); @@ -181,7 +179,7 @@ const CourseUpdatePage = ({ }) => { const addSingleStudent = async (email: string) => { const id = await getUserId(email) - if (!id) { + if (id == 0) { setAlert({ autoDelete: false, type: 'error', message: "userID not found" }) return } From 7f5a738a8b23e8566ac9ab35e63af7673521ed22 Mon Sep 17 00:00:00 2001 From: NeemZ16 <110858265+NeemZ16@users.noreply.github.com> Date: Sat, 26 Oct 2024 02:12:17 -0400 Subject: [PATCH 12/12] fixed date input subtracting a day --- .../components/pages/forms/courses/courseUpdatePage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx index 71e5f12..e76454c 100644 --- a/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx +++ b/devU-client/src/components/pages/forms/courses/courseUpdatePage.tsx @@ -45,8 +45,8 @@ const CourseUpdatePage = ({ }) => { number: '', semester: '', }) - const [startDate, setStartDate] = useState(new Date().toISOString().split("T")[0]) - const [endDate, setEndDate] = useState(new Date().toISOString().split("T")[0]) + const [startDate, setStartDate] = useState(new Date().toISOString()) + const [endDate, setEndDate] = useState(new Date().toISOString()) const [studentEmail, setStudentEmail] = useState("") const [emails, setEmails] = useState([]) const [invalidFields, setInvalidFields] = useState(new Map()) @@ -89,8 +89,8 @@ const CourseUpdatePage = ({ }) => { name: formData.name, number: formData.number, semester: formData.semester, - startDate: startDate, - endDate: endDate, + startDate: startDate + "T16:02:41.849Z", + endDate: endDate + "T16:02:41.849Z", } RequestService.put(`/api/courses/${courseId}`, finalFormData)