From 15c58c643f474dabab0d336c16b5ca50464f9b4c Mon Sep 17 00:00:00 2001 From: soubhi Date: Tue, 23 Apr 2024 08:19:51 -0400 Subject: [PATCH] Adding Student Tasks Box --- src/App.tsx | 11 +- .../StudentTasks/StudentTasks.module.css | 52 +++++ src/pages/StudentTasks/StudentTasks.tsx | 184 +++++++++++------- .../StudentTasks/StudentTasksBox.module.css | 103 ++++++++++ src/pages/StudentTasks/StudentTasksBox.tsx | 81 ++++++++ src/pages/StudentTasks/testData.json | 111 +++++++++++ 6 files changed, 472 insertions(+), 70 deletions(-) create mode 100644 src/pages/StudentTasks/StudentTasksBox.module.css create mode 100644 src/pages/StudentTasks/StudentTasksBox.tsx create mode 100644 src/pages/StudentTasks/testData.json diff --git a/src/App.tsx b/src/App.tsx index 0e757ed..4ba3b32 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,9 +26,8 @@ import Home from "pages/Home"; import Questionnaire from "pages/EditQuestionnaire/Questionnaire"; import Courses from "pages/Courses/Course"; import CourseEditor from "pages/Courses/CourseEditor"; -import StudentTasks from "pages/StudentTasks/StudentTasks"; -import StudentTaskDetail from "pages/StudentTasks/StudentTaskDetail"; import { loadCourseInstructorDataAndInstitutions } from "pages/Courses/CourseUtil"; +import StudentTasks from "./pages/StudentTasks/StudentTasks"; import TA from "pages/TA/TA"; import TAEditor from "pages/TA/TAEditor"; import { loadTAs } from "pages/TA/TAUtil"; @@ -44,8 +43,10 @@ function App() { { path: "login", element: }, { path: "logout", element: } /> }, { path: "edit-questionnaire", element: } /> }, - { path: "student_tasks", element: } leastPrivilegeRole={ROLE.STUDENT} /> }, - { path: "student_task_detail/:id", element: } leastPrivilegeRole={ROLE.STUDENT} /> }, + { + path: "student_tasks", + element: } leastPrivilegeRole={ROLE.STUDENT} />, + }, { path: "assignments", element: } leastPrivilegeRole={ROLE.TA} />, @@ -210,4 +211,4 @@ function App() { return ; } -export default App; +export default App; \ No newline at end of file diff --git a/src/pages/StudentTasks/StudentTasks.module.css b/src/pages/StudentTasks/StudentTasks.module.css index 99895b5..801a271 100644 --- a/src/pages/StudentTasks/StudentTasks.module.css +++ b/src/pages/StudentTasks/StudentTasks.module.css @@ -117,3 +117,55 @@ table td a:hover, table td a:focus { color: #337ab7; /* Change color to #337ab7 on hover */ text-decoration: underline; /* Underline the link on hover */ } + +/* StudentTasks.module.css */ +.studentTasksLayout { + display: flex; + flex-direction: row; /* aligns children (StudentTasksBox and the tasks list) in a row */ + gap: 20px; /* adds space between the children */ +} + +.studentTasksList { + flex-grow: 1; /* allows the tasks list to take up the remaining space */ +} + + +/* StudentTasks.module.css */ + +.pageLayout { + display: flex; + margin: 16px; +} + +.sidebar { + width: 250px; /* Width of the sidebar */ + margin-right: 20px; /* Spacing between sidebar and main content */ +} + +.mainContent { + flex-grow: 1; + overflow: hidden; /* In case the content is too wide */ +} + +.header { + margin-bottom: 20px; /* Space below the header */ +} + +.tasksTable { + width: 100%; /* Full width of the main content area */ + /* Add more styling for your table */ +} + +/* Add additional styling as needed */ + +.assignments-page { + font-family: 'Arial', sans-serif; +} + +.assignments-title { + color: #333; + text-align: left; + padding: 20px; + font-size: 24px; +} + diff --git a/src/pages/StudentTasks/StudentTasks.tsx b/src/pages/StudentTasks/StudentTasks.tsx index 83f2c55..422c9b0 100644 --- a/src/pages/StudentTasks/StudentTasks.tsx +++ b/src/pages/StudentTasks/StudentTasks.tsx @@ -1,6 +1,13 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { useNavigate, Link } from 'react-router-dom'; +import { RootState } from '../../store/store'; +import useAPI from 'hooks/useAPI'; import styles from './StudentTasks.module.css'; -import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; +import StudentTasksBox from './StudentTasksBox'; +import testData from './testData.json'; + + // Define the types for a single task and the associated course type Task = { @@ -17,80 +24,127 @@ type Task = { type Props = {}; -const StudentTasks: React.FC = () => { - const [tasks, setTasks] = useState([ - { - id: 1, - assignment: 'Program 1', - course: 'CSC/ECE 517, Spring 2024', - topic: '-', - currentStage: 'Finished', - reviewGrade: 'N/A', - badges: '', - stageDeadline: '2024-01-31 00:00:00 -0500', - publishingRights: false, - }, - { - id: 2, - assignment: 'Program 2', - course: 'CSC/ECE 517, Spring 2024', - topic: '-', - currentStage: 'Finished', - reviewGrade: 'N/A', - badges: '', - stageDeadline: '2024-01-31 00:00:00 -0500', - publishingRights: false, - }, - // ... add additional tasks as needed - ]); + +const StudentTasks: React.FC = () => { + const { error, isLoading, data: assignmentResponse, sendRequest: fetchAssignments } = useAPI(); + const { data: coursesResponse, sendRequest: fetchCourses } = useAPI(); + const auth = useSelector((state: RootState) => state.authentication); + const dispatch = useDispatch(); + const navigate = useNavigate(); + + const [tasks, setTasks] = useState([]); + const exampleDuties = testData.duties; + const taskRevisions = testData.revisions; + const studentsTeamedWith = testData.studentsTeamedWith; + + // Fetch assignments and courses data on component mount + useEffect(() => { + const fetchData = async () => { + try { + await Promise.all([ + fetchAssignments({ url: '/assignments' }), + fetchCourses({ url: '/courses' }) + ]); + } catch (error) { + console.error("Error fetching tasks data:", error); + } + }; + fetchData(); + }, [fetchAssignments, fetchCourses]); + + // Merge assignments and courses data + useEffect(() => { + if (assignmentResponse && coursesResponse) { + const mergedTasks = assignmentResponse.data.map((assignment: any) => { + const course = coursesResponse.data.find((c: any) => c.id === assignment.course_id); + return { + id: assignment.id, + assignment: assignment.name, + course: course ? course.name : 'Unknown', + topic: assignment.topic || '-', + currentStage: assignment.currentStage || 'Pending', + reviewGrade: assignment.reviewGrade || 'N/A', + badges: assignment.badges || '', + stageDeadline: assignment.stageDeadline || 'No deadline', + publishingRights: assignment.publishingRights || false + }; + }); + setTasks(mergedTasks); + } + }, [assignmentResponse, coursesResponse]); + + // Error handling for API requests + useEffect(() => { + if (error) { + dispatch({ type: 'SHOW_ALERT', payload: { message: error, variant: 'danger' }}); + } + }, [error, dispatch]); // Function to toggle publishing rights - const togglePublishingRights = (id: number) => { + const togglePublishingRights = useCallback((id: number) => { setTasks(prevTasks => prevTasks.map(task => task.id === id ? {...task, publishingRights: !task.publishingRights} : task )); - }; + }, []); // Render the list of tasks within a container return ( -
-

Assignments

- +
+ +

Student Task List

+
+ + + +
+ + {isLoading ? ( +

Loading tasks...

+ ) : ( +
- - - - - - - - - - - - - {tasks.map((task) => ( - - - - - - - - - + + + + + + + + + - ))} - -
AssignmentCourseTopicCurrent StageReview GradeBadgesStage DeadlinePublishing Rights
{task.assignment}{task.course}{task.topic}{task.currentStage}{task.reviewGrade}{task.badges}{task.stageDeadline} - togglePublishingRights(task.id)} - /> -
AssignmentCourseTopicCurrent StageReview GradeBadgesStage DeadlinePublishing Rights
+ + + {tasks.map((task) => ( + + {task.assignment} + {task.course} + {task.topic} + {task.currentStage} + {task.reviewGrade} + {task.badges} + {task.stageDeadline} + + togglePublishingRights(task.id)} + /> + + + ))} + + + )} +
+ ); }; -export default StudentTasks; +export default StudentTasks; \ No newline at end of file diff --git a/src/pages/StudentTasks/StudentTasksBox.module.css b/src/pages/StudentTasks/StudentTasksBox.module.css new file mode 100644 index 0000000..7e3fed5 --- /dev/null +++ b/src/pages/StudentTasks/StudentTasksBox.module.css @@ -0,0 +1,103 @@ +.student-tasks .table-striped>tbody>tr:nth-of-type(odd)>td, +.student-tasks .table-striped>tbody>tr:nth-of-type(odd)>th { + background-color: #ffffff; + --bs-table-bg-type: none +} + +.student-tasks .table-striped>tbody>tr:nth-of-type(even)>td, +.student-tasks .table-striped>tbody>tr:nth-of-type(even)>th { + background-color: #f2f2f2; + --bs-table-bg-type: none; +} +.taskbox { + padding: 5px; + margin-bottom: 39px; + border: 1px dashed #999999; + float: left; + font-size: 12px; + background: none repeat scroll 0pt 0pt #fafaea; + width: 100%; +} +.tasknum { + color: #FFFFFF; + background-color: #B73204; +} + +.revnum { + color: #FFFFFF; + background-color: #999999; +} + +.notification a { + color:#0066CC +} + +/* StudentTasks.module.css */ + +.pageLayout { + display: flex; + margin: 16px; + } + + .sidebar { + width: 200px; /* Width of the sidebar */ + margin-right: 20px; /* Spacing between sidebar and main content */ + } + + .mainContent { + flex-grow: 1; + overflow: hidden; /* In case the content is too wide */ + } + + .header { + margin-bottom: 20px; /* Space below the header */ + } + + .tasksTable { + width: 100%; /* Full width of the main content area */ + /* Add more styling for your table */ + } + + .section { + margin-bottom: 20px; /* Space between sections */ + } + + .section-header { + font-size: 18px; /* Larger font size for visibility */ + font-weight: bold; /* Bold text for section headers */ + color: #333; /* Darker text for better readability */ + margin-bottom: 10px; /* Space below section header */ + } + + .section-item { + margin-left: 20px; /* Indent for items in the list */ + margin-bottom: 5px; /* Space between items */ + color: #555; /* Slightly lighter text for items */ + } + + .badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0; + background-color: #a52a2a; + color: white; + } + + .greyBadge{ + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0; + background-color: rgb(159, 156, 156); + color: white; + } \ No newline at end of file diff --git a/src/pages/StudentTasks/StudentTasksBox.tsx b/src/pages/StudentTasks/StudentTasksBox.tsx new file mode 100644 index 0000000..b60e695 --- /dev/null +++ b/src/pages/StudentTasks/StudentTasksBox.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import styles from './StudentTasksBox.module.css'; // Make sure the path to your CSS module is correct + +// Define the types for each prop + type Duty = { + name: string; + dueDate: string; + }; + + type Revision = { + // Your Revision type properties + }; + + type StudentsTeamedWith = { + [semester: string]: string[]; + }; + + interface StudentTasksBoxProps { + duties: Duty[]; + revisions: Revision[]; + studentsTeamedWith: StudentsTeamedWith; + } + + const StudentTasksBox: React.FC = ({ duties, revisions, studentsTeamedWith }) => { + + + // Function to calculate the number of days left until the due date + const calculateDaysLeft = (dueDate: string) => { + const today = new Date(); + const due = new Date(dueDate); + const timeDiff = due.getTime() - today.getTime(); + const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24)); + return daysDiff > 0 ? daysDiff : 0; + }; + + // Find the duties that have not started yet based on the due date + const tasksNotStarted = duties.filter(duty => calculateDaysLeft(duty.dueDate) > 0); + + return ( +
+
+ + Tasks not yet started +
+ + {/* Revisions section (remains empty since revisions array is empty) */} +
+ {revisions.length} + Revisions +
+ + {tasksNotStarted.map((task, index) => { + const daysLeft = calculateDaysLeft(task.dueDate); + return ( +
+ » {task.name} ({daysLeft} day{daysLeft !== 1 ? 's' : ''} left) +
+ ); + })} + + + {/* Students who have teamed with you section */} +
+ Students who have teamed with you +
+ {Object.entries(studentsTeamedWith).map(([semester, students], index) => ( +
+ {semester} + {students.length} + {students.map((student, studentIndex) => ( +
+ » {student} +
+ ))} +
+ ))} +
+ ); +}; + +export default StudentTasksBox; diff --git a/src/pages/StudentTasks/testData.json b/src/pages/StudentTasks/testData.json new file mode 100644 index 0000000..df656a3 --- /dev/null +++ b/src/pages/StudentTasks/testData.json @@ -0,0 +1,111 @@ + +{ + "assignments": [ + { + "name": "Assignment 1", + "course_name": "Course3", + "topic": "Ruby", + "current_stage": "in progress", + "review_grade": { + "comment": "3/5" + }, + "has_badge": false, + "stage_deadline": "3/18", + "publishing_rights": true + }, + { + "name": "Assignment 2", + "course_name": "Course3", + "topic": "Rails", + "current_stage": "in progress", + "review_grade": "N/A", + "has_badge": true, + "stage_deadline": "3/18", + "publishing_rights": true + }, + { + "name": "Assignment 3", + "course_name": "Course3", + "topic": "Git", + "current_stage": "in progress", + "review_grade": "N/A", + "has_badge": true, + "stage_deadline": "3/18", + "publishing_rights": false + }, + { + "name": "Assignment 1", + "course_name": "Course32", + "topic": "AI", + "current_stage": "in progress", + "review_grade": "N/A", + "has_badge": false, + "stage_deadline": "3/25", + "publishing_rights": true + }, + { + "name": "Assignment 2", + "course_name": "Course4", + "topic": "Random Forest", + "current_stage": "finished", + "review_grade": "N/A", + "has_badge": false, + "stage_deadline": "3/28", + "publishing_rights": true + } + ], + "courses": [ + { + "id": 1, + "name": "Course3", + "directory_path": "/path/to/course_files", + "info": null, + "private": false, + "created_at": "2024-04-21T23:46:38.633Z", + "updated_at": "2024-04-21T23:46:38.633Z", + "instructor_id": 2, + "institution_id": 1 + }, + { + "id": 4, + "name": "Course4", + "directory_path": "/path/to/course_files", + "info": null, + "private": false, + "created_at": "2024-04-21T23:46:38.633Z", + "updated_at": "2024-04-21T23:46:38.633Z", + "instructor_id": 2, + "institution_id": 1 + } + + ], + + "duties": [ + { + "name": "Program 1", + "dueDate": "2024-03-18" + }, + { + "name": "Program 2", + "dueDate": "2024-03-18" + }, + { + "name": "OSS project", + "dueDate": "2024-03-18" + }, + { + "name": "OSS project 2", + "dueDate": "2024-03-25" + }, + { + "name": "OSS project 3", + "dueDate": "2024-04-28" + } + ], + "revisions": [ + + ], + "studentsTeamedWith": { + "CSC/ECE 517, Fall 2023": ["teammate one", "teammate two", "teammate three", "teammate four","teammate five", "teammate six", "teammate seven"] + } +} \ No newline at end of file